Spring MVC- MyBatis 연동하기
이제 토비의 스프링에 대한 개념을 어느정도 익혀가고 있으니 Database와 연동하기 위한 빈을 연동하는 작업을 금일 수행할 것이다. Database와 연동하기위해 사용할 기술은 MyBatis인데 SQL코드와 소스코드를 나눠주고 객체와 SQL 간의 매핑을 하는데 쓰이는 퍼시스턴스 프레임워크(데이터의 저장, 조회, 변경, 삭제를 다루는 클래스 및 설정 파일들의 집합)으로 현재 우리나라의 많은 Spring 프로젝트에서 사용되고 있는 기술이다.
마이바티스의 홈페이지는 다음과 같다.
마이바티스의 정의
마이바티스(MyBatis)는 자바 퍼시스턴스 프레임워크의 하나로 XML 서술자나 애너테이션(annotation)을 사용하여 저장 프로시저나 SQL 문으로 객체들을 연결시킨다.
마이바티스는 아파치 라이선스 2.0으로 배포되는 자유 소프트웨어이다.
마이바티스는 IBATIS 3.0의 포크이며 IBATIS의 원 개발자들이 포함된 팀에 의해 유지보수되고 있다.
__ 위키피디아
마이바티스의 정의는 살펴보았고 일단 마이바티스에서 제공해주는 클래스를 빈으로 등록하기 위해 web.xml에 bean들의 메타정보를 저장할 xml파일들의 위치를 resource밑의 config에 두도록 설정하였다. database관련된 bean설정은 따로 관리하기 위해 xml파일을 두개를 만들었다.
[web.xml]
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/*-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
context-param에 설정한 location 설정이 잘 사용되는지 확인하기 위해 테스트로 root-context.xml에 다음과 같이 빈을 만들고 테스트 코드를 돌려보았다.
[ src/main/resources/config/root-context.xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myBean" class="com.copocalypse.www.util.MyBean"/>
</beans>
[MyBean.java]
package com.copocalypse.www.util;
public class MyBean {
public String myTestMethod() {
return "test";
}
}
[MyBeanTest.java]
package com.copocalypse.www.util;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:config/*-context.xml")
public class MyBeanTest {
@Autowired
ApplicationContext ac;
@Test
public void myTestMethodTest() {
MyBean myBean = (MyBean) ac.getBean("myBean");
assertThat("test", is(myBean.myTestMethod()));
}
}
[테스트 결과]
테스트결과 web.xml에 설정한 context-param의 location설정이 제대로 xml파일들을 불러올 수 있다는 것을 확인할 수 있었다. 이제 마이바티스와 관련된 bean을 등록하고 POJO 객체와의 매핑작업을 진행해 보도록 하겠다.
MyBatis를 이용하여 객체와 SQL간의 연결을위해 해야할 작업은 MyBatis관련 빈 등록, MyBatis 설정정보 등록, Mapping xml 생성 매핑될 클래스 생성(DAO 클래스) 등의 작업을 거쳐야한다. 그렇기에 다음과같은 작업순서로 Mybatis연동작업을 수행하고자 한다.
작업의 순서
-
테이블 생성
-
MyBatis 의존관계 등록
-
MyBatis에서 제공하는 기능을 사용하기 위한 빈 등록 및 설정정보, Mapping.xml파일 생성
-
DB 설정 Properties 생성
-
연결 테스팅
MyBatis에서 제공하는 기능을 사용하기 위한 빈 등록 및 설정정보, Mapping xml파일 생성
1 테이블 생성 및 데이터 등록
user라는 테이블에 id, name, age등의 정보를 담을 수 있도록 테이블을 생성한 후, insert한다.
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user(name,age) values('주현태',30);
2 MyBatis 의존관계 등록
나같은 경우는 현재 Maven 기반 프로젝트인데 mybatis, junit test, lombok(코드경량화) 등의 라이브러리를 사용할 수 있도록 의존관계를 설정하였다.
[pom.xml]
<properties>
<java-version>1.6</java-version>
<org.springframework-version>4.3.10.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
`````````````````````````` 중략 `````````````````````````````
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>compile</scope>
</dependency>
<!-- Database Connection -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</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>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>
<!-- lombok -->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
3 MyBatis에서 제공하는 기능을 사용하기 위한 빈 등록 및 설정정보, Mapping xml파일 생성
[src/main/resources/config/datasource-context.xml]
위의 그림과같이 세개의 빈이 생성되며 의존관계가 주입된다.
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:property-placeholder
location="classpath:properties/db.properties" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driver}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
</bean>
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:/config/mybatis-config.xml" />
<property name="mapperLocations" value="classpath:/sqlmappers/**/*_SQL.xml" />
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
</beans>
[src/main/resources/config/mybatis-config.xml]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" value="false" />
<setting name="multipleResultSetsEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="SIMPLE" />
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<!-- Value Object 설정 -->
<typeAliases>
<typeAlias alias="hashmap" type="java.util.HashMap" />
<typeAlias alias="Int" type="java.lang.Integer" />
<typeAlias alias="User" type="com.copocalypse.www.vo.User" />
</typeAliases>
</configuration>
[src/main/resources/sqlmappers/user_SQL.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="user">
<select id="selectUser" parameterType="Int" resultType="User">
SELECT id,name,age from user
where id=#{id}
</select>
</mapper>
[UserDao.java]
package com.copocalypse.www.dao;
import com.copocalypse.www.vo.User;
public interface UserDao{
public User getUser(int uerId);
}
[UserDaoImpl.java]
package com.copocalypse.www.dao;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.copocalypse.www.vo.User;
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Autowired
SqlSession sqlSession;
@Override
public User getUser(int uerId) {
User user = sqlSession.selectOne("user.selectUser", uerId);
return user;
}
}
4 DB 설정 Properties 생성
[src/main/resources/properties/db.properties]
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://[ip]/[db명]?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
db.username=[id]
db.password=[password]
5 테스트코드 작성
[UserDaoTest.java]
package com.copocalypse.www.dao;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.copocalypse.www.vo.User;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:config/*-context.xml","file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
public class UserDaoTest {
@Autowired
ApplicationContext ac;
@Autowired
UserDao dao;
@Test
public void getUserTest() {
User user=dao.getUser(1);
assertThat(user.getName(), is("주현태"));
assertThat(user.getAge(), is(30));
}
}
이번에 MyBatis - Spring MVC를 연동하면서 가장 고민하였던 부분중 하나가 테스트 케이스의 작성이었다. 서블릿에서 생성하는 빈을 어떻게 가져올 수 있는 것일까 고민을 많이 하였는데 다음과 같이 @WebAppConfiguration 어노테이션을 사용하면 웹환경의 ApplicationContext를 갖고올 수 있다.
[테스트 결과]
단지 MyBatis와 스프링의 연결은 이전에도 많이 해보았지만, 이렇게 원리를 알고나서 작업을 하니 더욱 수월하였고 테스트케이스를 어떻게 작성할까 고민하는 과정에서 새로운 기술들도 습득을 하여서 만족스럽다.