SQL Mapping 프레임워크로 흔히 분류된다.
개발자들은 JDBC 코드의 복잡하고 지루한 작업을 피하기 위해 사용한다.
전통적인 JDBC프로그램과 MyBatis의 구조를 비교해본다.
스프링은 다른 프레임워크들과의 연동을 쉽게 하는 추가적인 라이브러리 들이 많다. MyBatis 또한 mybatis-spring 이라는 라이브러리를 통해 쉽게 연동이 가능하다.
Spring(mybatis-spring) --- MyBatis --- DB
pom.xml에 추가해야할 라이브러리들은 다음과 같다.
spring-jdbc/spring-tx : 스프링에서 데이터베이스 처리와 트랜잭션 처리(이름만 보면 전혀 상관없어보이긴한다.)
mybatis/mybatis-spring : MyBatis와 스프링 연동
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
MyBatis에서 가장 핵심적인 객체는 SQLSession이라는 녀석과 SQLSessionFactory 인터페이스 이다.
SQLSessionFactory는 이름만 보아도 SQLSession을 만들어 내는 공장이라는 것을 알 수 있다.
SQLSession은 개발자가 이를 통해 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 리턴 받는 구조로 작성하게 된다.
root-context.xml에 다음을 추가한다.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
따로 작성할 필요 없이 커넥션 풀 연동할때 사용한 테스트 코드에 그냥 추가해보자
@Setter(onMethod_ = @Autowired)
private SqlSessionFactory sqlSessionFactory;
@Test
public void testMyBatis() {
try(SqlSession session = sqlSessionFactory.openSession();
Connection conn = session.getConnection();) {
log.info(session);
log.info(conn);
} catch(Exception e) {
fail(e.getMessage());
}
}
Mapper는 SQL과 그에 대한 처리를 지정해주는 역할을 한다.
MyBatis-Spring을 이용한다면 Mapper를 XML과 인터페이스 + 어노테이션 형태로 작성 가능
Mapper을 작성하는 것은 XML을 이용할 수도 있지만 인터페이스로도 가능하다.
src/main/java에 TimeMapper라는 인터페이스를 추가했다.
package com.minsung.mapper; import org.apache.ibatis.annotations.Select; public interface TimeMapper { @Select("SELECT sysdate FROM dual") public String getTime(); }
MyBatis가 동작할 때 이 Mapper를 인식할 수 있도록 root-context.xml에 설정을 해야한다. 가장 간단한 방법은 <mybatis:scan> 태그를 이용하는 것.
root-context.xml > Namespaces 항목 > mybatis-spring 탭 선택
아래 코드 작성한다.
<mybatis-spring:scan base-package="com.minsung.mapper"/>
base-package속성은 지정된 패키지의 모든 MyBatis 관련 어노테이션을 찾아 처리한다.
mybatis-spring은 mapper인터페이스를 이용해 실제 SQL 처리가 되는 클래스를 자동으로 생성해준다.
package com.minsung.persistence;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.minsung.mapper.TimeMapper;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class TimeMapperTests {
@Setter(onMethod_ = @Autowired)
private TimeMapper tmapper;
@Test
public void testGetTime() {
log.info(tmapper.getClass().getName());
log.info(tmapper.getTime());
}
}
INFO : com.minsung.persistence.TimeMapperTests - com.sun.proxy.$Proxy22
INFO : com.minsung.persistence.TimeMapperTests - 2020-06-19 17:15:59
위와 같은 로그 메세지를 포함하여 여러 줄들이 나올 것이다.
위 코드에서는 timeMapper.getClass().getName() 을 통해 실제 동작하는 클래스의 이름을 확인해준다.
나는 분명 인터페이스만 만들고 그 인터페이스 타입의 레퍼런스변수만 선언해주었는데 내부적으로 TimeMapper타입의 스프링 객체(빈)이 생성됬다는 것이다.
위 예제에선 TimeMapper
인터페이스에 어노테이션으로 select문을 작성했었다. 아주 간편하긴 하지만 만약 SQL문이 복잡하거나 길어진다면 XML을 이용해 작성하는 것이 더 좋을 수 있다.
XML파일의 위치
1. Mapper인터페이스가 있는 곳에 같이 작성
2. src/main/resources 에 폴더들을 만들어서 작성.
책은 2번째 방식을 택해서 보여준다.
src/main/resources에 하나씩 com/minsung/mapper 폴더들을 만들어준다. 반드시 한번에 하나씩 폴더를 만들것.(한번에 다 만들면 인식이 안될수도)
이름에 대한 규칙도 딱히 없지만 가독성을 위해 매칭되는 인터페이스와 같은 이름을 사용하는 것이 좋다.
일단 TimeMapper인터페이스에 메서드를 하나 추가한다.
package com.minsung.mapper;
import org.apache.ibatis.annotations.Select;
public interface TimeMapper {
@Select("SELECT sysdate FROM dual")
public String getTime();
//Mapper인터페이스와 XML을 같이 사용하는 메서드. 기능은 위와 동일
public String getTime2();
}
XML파일에는 MyBatis의 XML매퍼에서 이용하는 태그에 대한 설정이 필요
https://mybatis.org/mybatis-3/ko/sqlmap-xml.html#Auto-mapping 참고
<?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.minsung.mapper.TimeMapper">
<select id="getTime2" resultType="string">
SELECT sysdate FROM dual
</select>
</mapper>
<mapper>태그의 namespaces 속성값은 해당 Mapper인터페이스이다.
MyBatis는 Mapper인터페이스와 XML을 인터페이스의 이름과 namespaces 속성값을 가지고 판단한다.
실제로 com.minsung.mapper.TimeMapper 인터페이스가 존재하고,
namespaces 속성값이 일치한다면 병합하여 처리한다.
다시말해서 위 예제의 경우에는
select 태그의 id속성값은 매칭되는 인터페이스의 메서드 이름과 동일해야한다. resultType속성은 인터페이스에 선언된 메서드의 리턴타입과 동일해야 한다.
//TimeMapperTests에 작성
@Test
public void testGetTime2() {
log.info("getTime2");
log.info(tmapper.getTime2());
}
결과 로그
INFO : com.minsung.persistence.TimeMapperTests - getTime2
INFO : com.minsung.persistence.TimeMapperTests - 2020-06-19 17:54:01