Mybatis : CRUD의 기능을 xml로 구조화하여 Mapper 설정 파일을 한 JDBC구현 프레임 워크
MyBatis Reference
https://mybatis.org/mybatis-3/
hikari : 가벼운 용량과 빠른 속도를 가지는 JDBC의 커넥션 풀 프레임워크
Mybatis 환경설정
https://www.notion.so/jh94/Spring-Mybatis-b0f338171c504fd0a2bf0f6427de985b
mapper.xml : DAO역할을 대신함, xml태그와 Query로 DB에 접근함
mybatus-config.xml : mybatis에 사용될 DB 관련 설정값 입력, mapper.xml 등록
root-context.xml : Service, Repository(DAO), DB등 비즈니스 로직과 관련된 설정, (hikari 를 사용했음)
servlet-context.xml : web.xml에서 작성한 DispatcherServlet의 설정을 기록
pom.xml : 프로젝트 전체에 적용설정, 라이브러리 파일 입력
SqlSessionFactoryBuilder : SqlSessionFactory 의 빌더
SqlSessionFactory : JDBC DataSource의 필수 프로퍼티가 필요함, DB 연결과 SQL의 실행에 대한 모든 것을 가진 가장 중요한 객체
SqlSession : 데이터베이스에 접근할 때 가장 중요한 역할을 하는 컴포넌트
@Configuration // 설정으로 인식
public class DBConfig {
@Bean // Spring IoC 컨테이너가 Bean으로 관리함
public static SqlSessionFactory getSqlSessionFactory() {
//step01 : mybatis-config.xml 읽어서 Factory 생성
SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
SqlSessionFactory sqlSessionFactory = null;
String resource = "/mybatis-config.xml";
InputStream is = null;
try {
is = Resources.getResourceAsStream(resource);
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSessionFactory;
}
@Bean
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
if(sqlSessionFactory != null) {
sqlSession = sqlSessionFactory.openSession();
}
return sqlSession;
}
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Dept {
private int deptno;
private String dname;
private String loc;
}
VO(Value Object) : 읽기 전용 객체(특정 객체를 읽기만을을 위해 사용), Entity, Setter 사용 금지
단순 읽는 목적의클래스를 만들 필요가 없으므로
이걸 Map 인터페이스로 받아옴 (HashMap 사용)
Dept 객체로 전달하면 없는 데이터는 null로 표시함
HashMap으로 전달하면 없는 데이터는 표시하지 않음
@Controller // 컴포넌트로 인식함
public class DeptController {
@Autowired // 필요한 의존 객체의 “타입"에 해당하는 빈을 찾아 주입함
DeptService deptService;
@RequestMapping(value = "/mybatis", method = RequestMethod.GET)
public void getAllDepts(Locale locale, Model model) {
// 1. getAllDepts()
List<Dept> deptList = deptService.getAllDepts();
System.out.println(deptList);
// 2. getDeptByDeptno(int deptno)
Dept dept = deptService.getDeptByDeptno(10);
System.out.println(dept);
// 3. getDnameByDeptno(int deptno)
String dname = deptService.getDnameByDeptno(10);
System.out.println(dname);
// 4. getDnameByDeptnoAndLoc(int deptno, String loc)
String dname2 = deptService.getDnameByDeptnoAndLoc(10, "NEW YORK");
System.out.println(dname2);
// 5. getDnameByDeptnoAndLocMap(HashMap<String, Object> hashmap)
HashMap<String, Object> hashmap = new HashMap<String, Object>();
hashmap.put("deptno", 10);
hashmap.put("loc", "NEW YORK");
String dname3 = deptService.getDnameByDeptnoAndLocMap(hashmap);
System.out.println(dname3);
// 6. getAllDeptsHashMap()
System.out.println(deptService.getAllDeptsHashMap());
// 7. insertDept(new Dept(90, "DEV", "TECH"))
deptService.insertDept(new Dept(90, "DEV", "TECH"));
// 8. updateLocByDeptno(90, "IT")
HashMap<String, Object> hashmap2 = new HashMap<String, Object>();
hashmap2.put("deptno", 90);
hashmap2.put("loc", "IT");
deptService.updateLocByDeptno(hashmap2);
// 9. deleteDeptByDeptno(90)
deptService.deleteDeptByDeptno(90);
}
}
@Service
public class DeptService {
@Autowired
DeptMapper deptMapper;
// getAllDepts()
public List<Dept> getAllDepts() {
return deptMapper.getAllDepts();
}
// getDeptByDeptno
public Dept getDeptByDeptno(int deptno) {
return deptMapper.getDeptByDeptno(deptno);
}
// getDnameByDeptno
public String getDnameByDeptno(int deptno) {
return deptMapper.getDnameByDeptno(deptno);
}
// getDnameByDeptnoAndLoc
public String getDnameByDeptnoAndLoc(int deptno, String loc) {
return deptMapper.getDnameByDeptnoAndLoc(deptno, loc);
}
// getAllDeptMap
public String getDnameByDeptnoAndLocMap(HashMap<String, Object> hashmap) {
return deptMapper.getDnameByDeptnoAndLocMap(hashmap);
}
// getAllDeptsHashMap
public List<HashMap<String, Object>> getAllDeptsHashMap() {
return deptMapper.getAllDeptsHashMap();
}
// insertDept
public void insertDept(Dept dept) {
deptMapper.insertDept(dept);
}
// updateLocByDeptno
public void updateLocByDeptno(HashMap<String, Object> hashmap2) {
deptMapper.updateLocByDeptno(hashmap2);
}
// deleteDeptByDeptno
public void deleteDeptByDeptno(int deptno) {
deptMapper.deleteDeptByDeptno(deptno);
}
}
@Mapper // Mapper는 component가 아니므로 root-context에서 따로 설정해야함
public interface DeptMapper {
// getAllDepts
List<Dept> getAllDepts();
// getDeptByDeptno
Dept getDeptByDeptno(int deptno);
// getDnameByDeptno
String getDnameByDeptno(int deptno);
// getDnameByDeptnoAndLoc
String getDnameByDeptnoAndLoc(@Param("deptno")int deptno,@Param("loc") String loc);
// getDnameByDeptnoAndLocMap
String getDnameByDeptnoAndLocMap(HashMap<String, Object> hashmap);
// getAllDeptsHashMap
List<HashMap<String, Object>> getAllDeptsHashMap();
// insertDept
void insertDept(Dept dept);
// updateLocByDeptno
void updateLocByDeptno(HashMap<String, Object> hashmap2);
// deleteDeptByDeptno
void deleteDeptByDeptno(int deptno);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "//mybatis.org/DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.spring.mapper.DeptMapper">
<!-- https://mybatis.org/mybatis-3/ko/sqlmap-xml.html -->
<!-- getAllDepts -->
<select id="getAllDepts" resultType="com.spring.dto.Dept">
SELECT deptno, dname, loc FROM dept
</select>
<!-- getDeptByDeptno -->
<select id="getDeptByDeptno" parameterType="_int" resultType="com.spring.dto.Dept">
SELECT deptno, dname, loc FROM dept WHERE deptno = #{deptno}
</select>
<!-- getDnameByDeptno -->
<select id="getDnameByDeptno" parameterType="_int" resultType="string">
SELECT dname FROM dept WHERE deptno = #{deptno}
</select>
<!-- getDnameByDeptnoAndLoc -->
<select id="getDnameByDeptnoAndLoc" resultType="string">
SELECT dname FROM dept WHERE deptno = #{deptno} AND loc = #{loc}
</select>
<!-- getDnameByDeptnoAndLocMap -->
<select id="getDnameByDeptnoAndLocMap" parameterType="hashmap" resultType="string">
SELECT dname FROM dept WHERE deptno = #{deptno} And loc = #{loc}
</select>
<!-- getAllDeptsHashMap ver1 -->
<!-- <select id="getAllDeptsHashMap" resultType="java.util.HashMap">
SELECT deptno, dname, loc FROM dept
</select> -->
<!-- getAllDeptsHashMap ver2 -->
<select id="getAllDeptsHashMap" resultMap="printDeptnoAndLoc">
SELECT deptno, loc FROM dept
</select>
<resultMap type="java.util.HashMap" id="printDeptnoAndLoc">
<result column="deptno" property="deptno"/>
<result column="loc" property="loc"/>
</resultMap>
<!-- insertDept -->
<insert id="insertDept" parameterType="com.spring.dto.Dept">
INSERT INTO dept (deptno, dname, loc)
VALUES (#{deptno}, #{dname}, #{loc})
</insert>
<!-- updateLocByDeptno -->
<update id="updateLocByDeptno" parameterType="hashmap">
UPDATE DEPT SET loc = #{loc} WHERE deptno = #{deptno}
</update>
<!-- deleteDeptByDeptno -->
<delete id="deleteDeptByDeptno">
DELETE FROM dept WHERE deptno = ${deptno}
</delete>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- component, mapper 스캔 설정 -->
<context:component-scan base-package="com.spring.*" />
<mybatis-spring:scan base-package="com.spring.mapper"/>
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="userID" />
<property name="password" value="userPW" />
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:/com/spring/mapper/*.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
JDK버전, lombok 버전에 따라 연동이 안되는 에러 등
설정 해야하는 부분이 너무 많아서 설정 관련 에러들이 정말 많았음
코드의 구성또한 직렬화되어서 어느 한 부분이라도 NPE, 오타, not found를 자주 볼 수 있었음
초기 설정하는 방법이 정말 오래걸림, 또한 JDK와 라이브러리, Eclipse 버전 체크가 정말 중요함
Interface를 왜 사용해야하는지 인터페이스에서 추상적으로 정의해놓고 사용하면 왜 좋은지를 잘 모르겠음
모든 것이 직렬화 되어서 손을 안대는 부분이 없던 것 같다.
MyBatis를 사용하면 그래도 MVC패턴, JDBC, CP, DI, IoC 를 전부 경험할 수 있었다.
SqlSessionFactory EntityManagerFactory
SessionFactory EntityManager
이렇게 JPA와 닮은 점도 많았다.
HashMap을 사용해서 까지 전달해야하는게 정말 이렇게 까지 해야하나 했지만 사용하다보니 MyBatis에서는 HashMap은 정말 편했다.
클래스를 만들어서 읽기만을 위한 객체라니 완전 천장에 메달린 굴비같았다.