→해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.
MyBatis?
: 간략히 말하면, SQL Mapping 프레임워크로 분류되는데, 개발자들은 JDBC 코드의 복잡한 작업을 피하는 용도로 주로 사용한다. MyBatis는 기존의 SQL을 그대로 활용할 수 있는 장점이 있고, 대체로 쉬운 편이어서 JDBC의 대안으로 많이 사용된다.
JDBC 프로그렘 | MyBatis |
-. 직접 Connection을 맺고 마지막에 Close -. PreparedStatement 직접 생성 처리 -. PreparedStatement의 Setter 등에 대한 모든 작업을 개발자가 처리 -. SELECT의 경우 직접 ResultSet 처리 |
-. 자동으로 Connection close() 가능 -. MyBatis 내부적으로 PreparedStatement 처리 -. #{prop}와 같이 속성을 지정하면 내부적으로 자동 처리 -. 리턴 타입을 지정하는 경우 자동으로 객체 생성 및 ResultSet 처리 |
mybatis-spring이라는 라이브러리를 통해 쉽게 연동작업 처리가 가능
MyBatis 관련 라이브러리 추가하기
pom.xml 추가
<!-- MyBatis 관련 라이브러리 추가 시작-->
<!-- mybatis/mybatis-spring : Mybatis와 spring 연동용 라이브러리 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- spring-jdbc/spring-tx :스프링에서 데이터베이스 처리와 트랜잭션 처리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- MyBatis 관련 라이브러리 추가 끝 -->
SQLSesssionFactory
: MyBatis에서 핵심적인 객체는 SQLSession과 SQLSessionFactory이다.
SQLSessionFactory란, 내부적으로 SQLSession이라는 것을 만들어 내는 존재인데, 개발에서는 SQLSession을 통해서 Connection을 생성하거나 원하는 SQL을 전달하고 결과는 리턴받는 구조로 작성하게 된다.
root-context.xml 추가
: 스프링에서 SqlSessionFactory를 등록하는 작업은 SqlSessionFactoryBean을 이용한다. 패키지명을 보면 MyBatis의 패키지가 아니라 스프링과 연동 작업을 처리하는 mybatis-spring 라이브러리의 클래스임을 확인 할 수 있다.
<!-- HikariCP configuration -->
<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>
</bean>
Java 설정을 이용하는 경우 : RootConfig.java 추가
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
return (SqlSessionFactory) sqlSessionFactory.getObject();
}
DataSourceTests.java : Test 코드로 확인해보기
: SqlSessionFactoryBean을 이용해서 SqlSession을 사용 해보는 테스트
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
//Java 설정을 사용하는 경우
//@ContextConfiguration(classes = {RootConfig.class})
@Log4j
public class DataSourceTests {
@Setter(onMethod_ = {@Autowired})
private DataSource dataSource;
@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());
}
}
.....
생략
}
결과확인
스프링과의 연동처리
: SQL을 어떻게 처리할 것인지를 별도의 설정을 분리하고, 자동으로 처리되는 방식을 이용해본다. 이를 위해서는 MyBatis의 Mapper라는 존재를 작성해줘야 한다.
Mapper란?
: SQL과 그에 대한 처리를 지정하는 역할을 한다. MyBatis-Spring을 이용하는 경우에는 Mapper를 XML과 인터페이스+어노테이션의 형태로 작성 가능하다.
Mapper 인터페이스 작성하기
: org.zerock.mapper라는 패키지 생성 후, TimeMapper라는 인터페이스를 작성한다.
TimeMapper.java
package org.zerock.mapper;
import org.apache.ibatis.annotations.Select;
public interface TimeMapper {
@Select("SELECT sysdate FROM dual")
public String getTime();
}
Mapper 설정
: MyBatis가 동작할 때 Mapper를 인식할 수 있도록 root-context.xml에 추가적으로 설정을 해준다.
root-context.xml 일부
: <mybatis-spring:scan> 태그의 base-package 속성은 지정된 패키지의 모든 MyBatis 관련 어노테이션을 찾아서 처리한다.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<mybatis-spring:scan base-package="org.zerock.mapper"/>
<context:component-scan base-package="org.zerock.sample"></context:component-scan>
위 내용을 Java 설정을 이용하는 경우
RootConfig.java 일부
package org.zerock.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@ComponentScan(basePackages = {"org.zerock.sample"})
@MapperScan(basePackages = {"org.zerock.mapper"})
public class RootConfig {
@Bean
public DataSource dataSource() {
...
생략
Test 코드로 확인해보기
TimaMapperTests.java
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")
//Java를 이용한 설정
//@ContextConfiguration(classes= {org.zerock.config.RootConfig.class})
@Log4j
public class TimeMapperTests {
@Setter(onMethod_= {@Autowired})
private TimeMapper timeMapper;
@Test
public void testGetTime() {
log.info(timeMapper.getClass().getName());
log.info(timeMapper.getTime());
}
}
결과 확인
: 인터페이스만 만들어 주었는데 내부적으로 적당한 클래스가 만들어진 것을 확인할 수 있다. 스프링이 인터페이스를 이용해서 객체를 생성한다는 사실에 초점을 맞추자.
XML 매퍼와 같이 쓰기
: SQL이 길어지거나 복잡한 결루에는 어노테이션보다 XML을 이용하는 방식이 더 편리하다.
TimaMapper.xml 생성하기
: 매퍼에 대한 자세한 사항은 이 곳에서 확인 가능하다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- 자세한 정보 참고 URL : http://www.mybatis.org/mybatis-3/ko/sqlmap-xml.html -->
<!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 매퍼를 이용할 때 신경써야 하는 부분은 태그의 Namespace 속성값이다. MyBatis는 Mapper 인터페이스와 XML을 인터페이스 이름과 namespace 속성값을 가지고 판단한다.
TimaMapper.java
: Mapper 인터페이스와 XML을 같이 이용해보기 위해 기존 TimeMapper 인터페이스에 getTime2 메소드 추가
package org.zerock.mapper;
import org.apache.ibatis.annotations.Select;
public interface TimeMapper {
@Select("SELECT sysdate FROM dual")
public String getTime();
public String getTime2();
}
Test 코드로 확인해보기
TimaMapperTests.java
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")
//Java를 이용한 설정
//@ContextConfiguration(classes= {org.zerock.config.RootConfig.class})
@Log4j
public class TimeMapperTests {
@Setter(onMethod_= {@Autowired})
private TimeMapper timeMapper;
@Test
public void testGetTime() {
log.info(timeMapper.getClass().getName());
log.info(timeMapper.getTime());
}
@Test
public void testGetTime2() {
log.info("getTime2");
log.info(timeMapper.getTime2());
}
}
결과 확인
: getTime()과 동일한 결과 출력
log4jdbc-log4j2 설정하기
: 실제 적용되는 SQL 로그를 제대로 보기 위해서는 log4jdbc-log4j2 라이브러리를 사용해야 한다. 이를 위해선 아래 3가지 단계를 적용해야한다.
1) pom.xml 추가
<!-- SQL의 로그를 제대로 보기 위한 라이브러리 사용 -->
<!-- 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>
2) 로그 설정 파일 추가
log4jdbc.log4j2.properties
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
3) JDBC의 연결 정보 수정
root-context.xml
<!-- HikariCP의 bean 설정 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- <property name="driverClassName" value="oracle.jdbc.OracleDriver"></property>
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1522:xe"></property> -->
<!-- log4jdbc를 이용하기 위해서는 JDBC드라이버와 URL 정보 수정이 필요함. -->
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1522:xe"></property>
<property name="username" value="book_ex"></property>
<property name="password" value="book_ex"></property>
</bean>
위 내용을 Java 설정을 이용하는 경우
RootConfig.java 일부
@Bean
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
//hikariConfig.setDriverClassName("oracle.jdbc.driver.OracleDriver");
//hikariConfig.setJdbcUrl("jdbc:oracle:thin:@localhost:1522:xe");
/* log4jdbc를 이용하기 위해서는 JDBC드라이버와 URL 정보 수정이 필요함. */
hikariConfig.setDriverClassName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy");
hikariConfig.setJdbcUrl("jdbc:log4jdbc:oracle:thin:@localhost:1522:xe");
hikariConfig.setUsername("book_ex");
hikariConfig.setPassword("book_ex");
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
return dataSource;
}
TimaMapperTests.java 테스트 코드 재실행 결과
로그의 레벨 설정
: 테스트 코드 실행시 상당히 많은 양의 로그(기본 info레벨) 가 나오기 때문에 로그 레벨을 높여 로그의 양을 줄이도록 해보자.
log4j.xml 일부
: 로그 레벨에 대한 자세한 설명은 이 곳을 참고하면 된다.
<logger name="jdbc.audit">
<level value="warn" />
</logger>
<logger name="jdbc.resultset">
<level value="warn" />
</logger>
<logger name="jdbc.connection">
<level value="warn" />
</logger>
<!-- Root Logger -->
<root>
<priority value="info" />
<appender-ref ref="console" />
</root>
결과 확인
다음 시간에는 스프링 MVC 설정으로 찾아오겠다~