[spring] MyBatis 연동하기

업데이트:


MyBatis

MyBatis는 흔히 SQL 매핑 프레임워크로 분류된다. JDBC 코드의 복잡하고 지루한 작업을 단축시킬 수 있다.


장점

  • 자동으로 Connection close() 기능
  • MyBatis 내부적으로 PreparedStatement 처리
  • #{prop}와 같이 속성을 지정하면 내부적으로 자동 처리
  • 리턴 타입을 지정한느 경우 자동으로 객체 생성 및 ResultSet 처리
  • 기존의 SQL을 그대로 활용할 수 있음




라이브러리 추가

MyBatis와 mybatis-spring을 사용하기 위해 pom.xml 파일에 추가적인 라이브러리들을 설정해야 한다.

  • spring-jdbc/spring-tx : 스프링에서 DB 처리와 트랜잭션 처리
  • mybatis/mybatis-spring : MyBatis와 스프링 연동용 라이브러리

  • 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>





SQLSessionFactory

MyBatis에서 가장 핵심적인 객체는 SQLSessionSQLSessionFactory이다. SQLSessionFactory는 내부적으로 SQLSession을 통해 Connection을 생성하거나 원하는 SQL을 전달하고, 결과를 리턴받는 구조로 작성하게 된다.

root-context.xml에 아래의 코드를 추가한다.

<!-- root-context.xml -->

<bean id="sqlSessionFactory" class="org.mybatis.spring.
SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
</bean>

스프링에 SqlSessionFactory를 등록하는 작업은 SqlSessionFactoryBean을 이용한다. 패키지명을 보면 MyBatis의 패키지가 아니라 스프링과 연동 작업을 처리하는 mybatis-spring 라이브러리의 클래스임을 알 수 있다.




스프링과 연동처리

SQLSessionFactory를 이용해서 코드를 작성해도 직접 Connection을 얻어서 JDBC 코딩이 가능하지만, 좀 더 편하게 작업하기 위해서는 MyBatis의 Mapper라는 존재를 작성해야한다.

Mapper는 쉽게 말해서 SQL과 그에 대한 처리를 지정하는 역할을 한다. MyBatis-Spring을 이용하는 경우에는 Mapper를 XML과 인퍼테이스 + 어노테이션의 형태로 작성할 수 있다.


Mapper 인터페이스

‘org.zerock.mapper’ 패키지를 만들고 TimeMapper라는 인퍼테이스를 추가한다.

mapper1


package org.zerock.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’ 탭을 선택한다.

mapper2

<!-- root-context.xml의 일부 -->
<mybatis-spring:scan base-package="org.zerock.mapper" />

base-package 속성은 지정된 패키지의 모든 MyBatis 관련 어노테이션을 찾아서 처리한다. 자동으로 org.zerock.mapper 패키지를 인식하는 방식으로 작성되었다.




Mapper 테스트

MyBatis-Spring은 Mapper 인터페이스를 이용해서 실제 SQL 처리가 되는 클래스를 자동으로 생성한다. 따라서 개발자들은 인터페이스와 SQL만을 작성하는 방식으로 모든 JDBC 처리를 끝낼 수 있다.

‘src/test/java’ 밑에 ‘org.zerock.persistence.TimeMapperTests’ 클래스를 생성해 테스트 코드를 작성한다.

package org.zerock.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 org.zerock.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 timeMapper;
	
	@Test
	public void testGetTime() {
		log.info(timeMapper.getClass().getName());
		log.info(timeMapper.getTime());
	}
}

mapper3

위 코드가 정상적으로 동작한다면 스프링 내부에는 TimeMapper 타입으로 만들어진 스프링 객체(빈)가 존재한다는 뜻이 된다. 위의 코드에서 ‘timeMapper.getClass().getName()’은 실제 동작하는 클래스의 이름을 확인해준다. 실행 결과를 보면 개발 시 인터페이스만 만들어 주었는데 내부적으로 적당한 클래스가 만들어진 것을 확인할 수 있다. 즉, 스프링은 인터페이스를 이용해서 객체를 생성한다.




XML 매퍼와 같이 쓰기

XML을 작성해서 사용할 때에는 XML 파일의 위치와 XML 파일에 지정하는 Namespace 속성이 중요하다. 여기서는 ‘src/main/resources’ 폴더 내 org 폴더, zerock 폴더, mapper 폴더를 하나씩 생성하고 TimeMapper.xml을 생성한다.

mapper4


Mapper 인터페이스와 XML을 같이 이용해 보기 위해서 기존의 TimeMapper 인터페이스에 추가적인 메서드를 선언한다.

package org.zerock.mapper;

import org.apache.ibatis.annotations.Select;

public interface TimeMapper {
	@Select("SELECT sysdate FROM dual")
	public String getTime();
	
	public String getTime2();
}


메서드를 추가했으면 TimeMapper.xml에 다음과 같은 코드를 작성한다.

<?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="org.zerock.mapper.TimeMapper">
		<select id="getTime2" resultType="string">
			SELECT sysdate FROM dual
		</select>
	</mapper>

XML 매퍼를 이용할 때 신경 써야 하는 부분은 <mapper> 태그의 namespace 속성값이다. MyBatis는 Mapper 인터페이스와 XML을 인터페이스의 이름과 namespace의 속성값을 가지고 판단한다. 위와 같이 namespace에 적힌 값, ‘org.zerock.mapper.TimeMapper’를 알아서 찾아간다.

<select> 태그의 id 속성 값은 메서드의 이름과 동일하게 작성해야 한다. <select> 태그의 경우 resultType 속성은 인터페이스에 선언된 메서드의 리턴 타입과 동일하게 작성한다.

최종 테스트

// TimeMapperTests 클래스에 추가한다.

@Test
public void testGetTime2() {
    log.info("getTime2");
    log.info(timeMapper.getTime());
}

테스트 코드를 실행하면 getTime()과 동일한 결과가 출력된다.




log4jdbc-log4j2 설정

MyBatis는 내부적으로 JDBC의 PreparedStatement를 이용해서 SQL을 처리한다. 따라서 SQL에 전달되는 파라미터는 JDBC에서와 같이 ‘?’로 치환되어 처리된다. 이럴 경우 값이 제대로 처리 되었는지 확인하기가 쉽지 않고, 실행된 SQL의 내용을 정확히 확인하기 어렵다. SQL 로그를 제대로 보기 위해서는 log4jdbc-log4j2 라이브러리를 사용해야 한다.

<!-- pom.xml -->


<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4 -->
<dependency>
    <groupId>org.bgee.log4jdbc-log4j2</groupId>
    <artifactId>log4jdbc-log4j2-jdbc4</artifactId>
    <version>1.16</version>
</dependency>


라이브러리 추가 후 ‘src/main/resources’ 밑에 log4jdbc.log4j2.properties 파일을 추가한다.

// log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator


log4jdbc를 이용하는 경우에는 JDBC 드라이버와 URL 정보를 수정해야한다.

<!-- root-context.xml -->

	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	  <!--    <property name="driverClassName" 
	    value="oracle.jdbc.driver.OracleDriver"></property>
	    <property name="jdbcUrl"
	    value="jdbc:oracle:thin:@localhost:1521:XE"></property>
	   -->
	   
	   <property name="driverClassName"
	   		value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
	   <property name="jdbcUrl"
	   		value="jdbc:log4jdbc:oracle:thin:@localhost:1521:XE"></property>
	   
	   
	    <property name="username" value="book_ex"></property>
	    <property name="password" value="book_ex"></property>
	</bean>

위의 설정이 제대로 되어 있지 않으면 데이터베이스의 로그가 정상적으로 기록되지 않는다.

설정 변경 후 기존의 테스트 코드를 실행해보면 이전과는 다르게 JDBC와 관련된 로그들이 출력되는 것을 확인할 수 있다.

log4jdbc1



로그의 레벨 설정

테스트 코드를 실행하면 많은 양의 로그가 찍혀 원하는 로그를 찾는데 시간이 오래 걸릴 수 있다. 이런 상황에서는 로그의 레벨을 이용해서 조금 수정해야 한다.

테스트 코드가 실행될 때의 로그와 관련된 설정은 ‘src/test/resources’ 밑에 log4j.xml을 이용하면 된다.


log4jdbc에서 출력되는 로그를 조절하고 싶다면 추가적인 <logger>를 지정해서 처리한다.

<!-- 레벨 조정 -->
<logger name="jdbc.audit">
    <level value="warn" />
</logger>

<logger name-="jdbc.resultset">
    <level value="warn" />
</logger>

<logger name-="jdbc.connection">
    <level value="warn" />
</logger>

기본 설정의 로그는 info 레벨이기 때문에 warn과 같이 좀 더 높은 레벨의 로그만 기록하게 수정하면 로그의 양이 줄어든다.





  • 참고 : 코드로 배우는 스프링 웹 프로젝트

태그:

카테고리:

업데이트:

댓글남기기