잊지 않겠습니다.

15. Spring과 iBatis

Java 2013. 9. 11. 14:18

* 사내 강의용으로 사용한 자료를 Blog에 공유합니다. Spring을 이용한 Web 개발에 대한 전반적인 내용에 대해서 다루고 있습니다.



Spring에서 myBatis를 사용하기 위해서는 Hibernate는 Spring이 Hibernate에 대한 SessionFactory, DataSource를 제공하고 있는 것에 비해, 오히려 mybatis 측에서 Spring에 대한 library를 제공하고 있습니다. 

pom.xml에 다음과 같은 내용을 추가해주시길 바랍니다. 

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.1.1</version>
</dependency>

pom.xml에 적용후, command창에서 mvn dependency:tree 를 한번 실행시켜보시면 다음과 같은 결과를 볼 수 있습니다. 




기본적으로 mybatis-spring은 spring 3.1RELEASE를 가지고 개발된 jar입니다. 따라서, mybatis-spring을 참조하는 즉시, spring 3.1Release가 바로 추가가 됩니다. 그런데, 저희 프로젝트는 기본적으로 Spring 3.2RELEASE를 기반으로 구성되어 있습니다. 이런 경우 어떻게 해야지 될까요?
다행히도, Spring은 하위 호환성을 100% 맞추고 있습니다. 따라서, mybatis-spring에서 사용하고 있는 Spring 3.1 module을 모두 제거하고, Spring 3.2 RELEASE를 모두 사용하도록 maven의 dependency를 조절해줘야지 됩니다. maven에서 dependency 된 jar안에 포함된 다른 module을 제거하는 것은 다음과 같은 포멧으로 pom.xml을 수정해줘야지 됩니다.

이러한 경우에 pom.xml을 수정하는 방법은 2가지가 있습니다. 

1. 각 dependency에서 각 library들을 exclude 시켜주는 방법
2. dependencyManagement에서 각 library들의 version을 관리해주는 방법

개인적으로는 2번째의 방법이 좀 더 나은 것 같습니다. dependencyManagement를 이용해서 mybatis-spring의 dependency 충돌을 해결한 pom.xml의 일부입니다. 

   <repositories>
        <repository>
            <id>mybatis-snapshot</id>
            <name>MyBatis Snapshot Repository</name>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        </repository>
    </repositories>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>3.2.1.RELEASE</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>3.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>3.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>3.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>3.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>3.2.1.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.22</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.2.2</version>
        </dependency>
    </dependencies>

이제 dependency를 다시한번 확인해보도록 하겠습니다. 

mybatis-spring에서 의존하고 있던 jar들은 모두 제거가 된 것을 알 수 있습니다. 그리고 Spring에 필요한 jar들이 모두 추가 됨을 볼 수 있습니다.

마지막으로 applicationContext.xml을 추가하도록 하겠습니다. myBatis는 기본적으로 spring에서 제공하는 DataSource를 그대로 이용할 수 있습니다. DataSource를 그대로 이용한다는 것은 TransactionManager를 이용한 @Transactional 역시 사용이 가능하다는 뜻입니다. 지금까지 해왔던 내용대로 설정을 할 수 있습니다. 다만 차이가 있다면 Spring을 사용하지 않고 myBatis를 이용할 때는 SqlSession을 SqlSessionFactory에서 직접 얻어오는 방식을 채택했습니다. 그렇지만, Spring을 이용하는 경우, mybatis-spring에서 제공하는 SqlSessionFactoryBean과 SqlSessionFactoryTemplage을 이용해서 작업을 한다는 차이 이외에는 테스트 코드 자체도 큰 차이가 없습니다.

다음은 applicationContext.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"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
  <context:property-placeholder location="classpath:spring.property" />
  <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
  </bean>
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:mybatis.xml" />
  </bean>
  <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory" />
  </bean>
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

dataSource와 transactionManager는 JdbcTemplate을 사용할때와 완전히 동일합니다. 그리고 Hibernate에서 사용하던 LocalSessionFactoryBean 대신에 SqlSessionFactoryBean과 SqlSessionTemplate를 사용하는 차이만을 가지고 있습니다. 

이제 구성된 설정을 이용해서 각 Dao interface들을 얻어보도록 하겠습니다. 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@Transactional
public class BookDaoTest {
    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;
    private BookDao bookDao;
    
    @Before
    public void setUp() {
        assertThat(sqlSessionTemplate, is(not(nullValue())));
        bookDao = sqlSessionTemplate.getMapper(BookDao.class);
        bookDao.deleteAll();
        assertThat(bookDao.countAll(), is(0));
    }
    
    @Test
    public void add() {
        List<Book> books = getBooks();
        for(Book book : books) {
            bookDao.add(book);
        }
    }
    
    @Test
    public void getAll() {
        List<Book> books = getBooks();
        for(Book book : books) {
            bookDao.add(book);
        }
        List<Book> allBooks = bookDao.getAll();
        for(Book book : allBooks) {
            System.out.println(book);
        }
    }

기존에는 각각의 session에서 commit를 실행시켜줬지만, 이제는 @Transactional을 이용한 세련된 Transaction을 하고 있는 것을 볼 수 있습니다. 사용법 역시 큰 차이가 있지 않으며 Spring과 interface를 조합한 사용 계층에서의 큰 변경 없이 코드의 기술을 완전히 변경을 시킬 수 있는 것을 알 수 있습니다. 
코딩하는 연습을 하는 시간을 갖겠습니다. 나머지 코드들을 모두 작성해주세요. 


매우 간편한 설정 및 사용의 편의성. mybatis가 현업에서 가장 많이 사용되고 있는 이유가 보이시나요? 이렇게 쉬울수는 없다. 라는 느낌이 맞을 정도입니다. 


Summary

지금까지 구성된 bookstore를 myBatis-spring을 이용해서 구성해주세요. 



Posted by Y2K
,