[Spring Framework ⑤] Spring에서 iBatis 연동하기

개발 노트 2008. 8. 4. 10:26 posted by 무병장수권력자


Persistance Support of Spring Framework

작성자 : 김문규
최초 작성일 : 2008. 8. 1

Spring에서 DB에 연결하는 방법에 대해서 알아보려 합니다. 다음과 같이 세가지 방법이 있습니다.
 - JDBC를 이용하는 방법
 - JdbcDaoSupport 클래스를 이용해서 JDBC를 조금 편하게 템플릿화하는 방법
 - SqlMapClientDaoSupport 클래스를 이용해서 iBatis Framework를 연동하는 방법

각 방법이 어떻게 다른지를 설명드리고 튜토리얼 수준의 예제를 보여드리겠습니다.

1. JDBC
일반적으로 JDBC를 이용할 경우 아래의 순서를 따라서 구현됩니다. 
 - DriverManager에 해당 DBMS Driver를 등록
 - 해당 Driver로 부터 Connection 객체 획득
 - Connection 객체로부터 Statement 객체 획득
 - Statement의 method를 이용하여 SQL실행
 - ResultSet 으로 받아서 처리(executeUpdate 의 경우엔 제외)
 - 객체 close() (ResultSet, Statement, Connection)

여기서 실제로 개발자의 비지니스 로직이 들어가는 부분은 4번과 5번의 일부입니다. 나머지는 음... 기계적인 코드입니다. 실제 코드를 예로 들면 아래와 같습니다.

Connection.java

.........
Connection con = null;
Statement st = null;
ResultSet rs = null;
 
try {
    con=DriverManager("jdbc:mysql://localhost/dbname","root","1234");  // dbname에는 사용하는 database 이름, root 계정, 패스워드는 1234

    Statement st=con.createStatement();
    ResultSet rs=st1.executeQuery("select * form names");
 
    if( rs.next() ) {
        do {
            // result set을 잘 정리합니다. 물론 일일이
            ...
            // 비지니스 로직을 수행합니다.
            ...
        } while( rs.next() )
    }
} catch (Exception ex) {
    // 적절한 예외 처리
} finally {
    if( rs != null) rs.close();
    if( st != null ) st.close();
    if( con != null ) conn.close();
}
.........

의외로 너무 많은 부분인 반복되는 느낌입니다. 이런 부분은 Spring을 사용하면 많이 줄어들게 됩니다. 다음절에서 확인해 보지요.

2. DaoSupport in Spring Framework
Spring에는 Template이라는 것을 이용해서 이 과정을 알아서 처리해 줍니다.
 - JdbcTemplate
 - NamedParameterJdbcTemplate
 - SimpleJdbcTemplate
 
이를 편하게 사용하기 위해서 주로 아래의 Dao 클래스를 이용합니다.
 - JdbcDaoSupport
 - NamedParameterJdbcDaoSupport
 - SimpleJdbcDaoSupport

여기서 가장 다양한 기능을 가진 SimpleJdbcDaoSupport 클래스의 사용법을 코드에서 확인해 보도록 하겠습니다. (전체 코드는 이전 4번 튜토리얼 분석 포스트에서 사용했던 예제를 보시면 됩니다.)

applicationContext.xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
 
<bean id="productDao" class="springapp.repository.JdbcProductDao">
    <property name="dataSource" ref="dataSource"/>
</bean>

ProductDao.java

public class JdbcProductDao extends SimpleJdbcDaoSupport implements ProductDao {
    public int selectCount() {
        return getSimpleJdbcTemplate().queryForInt(
                "select count(*) from products");
    }
}

음..확실히 간편해 진것 같습니다.

하지만, 쿼리문이 코드 내부에 하드코딩되어 있다는 점이 약간 아쉬운 부분입니다. 쿼리문 변경 시에 코드도 같이 변경되고 재컴파일도 되어야 합니다. (뭐 솔직히 컴파일 하면 되긴 해요... ^^) 그리고 따로 설명하지 않겠지만 ParameterizeRowMapper 인터페이스를 이용해서 domain 객체와 결과를 연결하는 방법이 있는데 이 역시 하드코딩 되기 때문에 유지 보수의 유연성이 떨어지는 면이 없지 않습니다.

그래서, 쿼리 및 쿼리의 결과와 객체간의 연결을 외부에서 따로 관리하는 기능을 요구하게 되고 이런 기능을 가진 iBatis가 최근 많이 사용되고 있습니다. 다음 절에서 iBatis의 연동에 대해 알아보겠습니다.
 
3. Spring Framework와 iBatis 연동
1) iBatis
국내 SI 현실에 가장 잘 어울린다고 평가받고 있는 persistance framework 입니다. 그 이유는 SQL 쿼리문을 그대로 사용하기 때문에 기존 JDBC 개발자들에게 거부감이 덜하고  경험적(?)인 시스템의 이해가 빠르기 때문일 겁니다. 여타의 ORM framwork는 배우기도 힘들고 또는 제 3자 입장에서 이해하기도 힘들어서 우리나라 SI 업계처럼 개발팀이 이합집산하는 문화에는 어울리지 않는다는 군요.

각설하고 간단한 예를 보도록 하지요. iBatis에 대해서는 제가 따로 설명하지 않을 것입니다. 아래의 자료들을 보시면 간단하게 이해하실 수 있을 겁니다. 그리고, googling 하시면 JPetStore라는 예제가 있습니다. 이를 한번 실행시켜 보시는 것도 도움이 될 것입니다.

다음은 iBatis 관련해서 많은 자료를 만드신 이동국님의 Tutorial 변역본입니다. 8장밖에 안되지만 iBatis의 동작에 대해서 이해하는데 많은 도움이 되실 것입니다.
별첨으로 하나 더 첨부합니다. 이 파일은 sqlMap의 사용법에 대해 설명된 문서이고 역시 이동국님이 번역하신 자료입니다.

자~! 이제 iBatis의 사용법에 대해서 간단히 감을 잡으셨나요?
앞 절에서 언급한 것처럼 제 생각에는 2가지의 큰 장점을 가진다고 생각됩니다. (다른 장점이 있으시면 사알짝 좀 알려주세요~~)
 - SQL 쿼리문이 외부 설정 파일에서 관리된다.
 - domain 객체와 SQL result set의 연결이 외부 설정 파일에서 관리된다.

그런데 JDBC와 마찬가지로 iBatis에도 단순하게 기계적으로 반복되는 코드가 있습니다. 바로 SqlMapClient를 선언하는 부분이지요! (아래 코드 참조)
public MyAppSqlConfig {
    private static final SqlMapClient sqlMap;
    static {
        try {
            String resource = “com/ibatis/example/sqlMap-config.xml”;
            Reader reader = Resources.getResourceAsReader (resource);
            sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
        } catch (Exception e) {
            .......
    }
}
 
JDBC를 지원하기 위해 JdbcDaoSupport 클래스가 있지요? 그럼 SqlMapClient를 지원해 주는 것은? 네! SqlMapClientDaoSupport 클래스 입니다. 그럼 드디어 sqlMapClientDaoSupport 클래스에 대해 알아보겠습니다.

2) sqlMapClientDaoSupport
앞서 알아본 JdbcDaoSupport와 이름도 비슷하지만 사용법도 매우 유사합니다. 설정 파일과 관련된 자바 코드를 보시지요.
applicationContext.xml - sqlMapConfig.xml - Product.xml 이 연쇄적인 관계를 가지고 있습니다. 여기서 applicationContext.xml은 spring 설정 파일이고 나머지는 iBatis 설정 파일입니다.
비지니스 로직 수행 중 iBatisMessageDao의 함수들이 요청될 것이고 이떄 Product.xml에 있는 쿼리문들을 호출해서 수행하게 됩니다.
주석문을 달고 색깔로 구분해 놓았습니다. 연관 관계에 유의하시면서 주의 깊게 확인하시길 바랍니다. (오랜만에 칼라풀한 포스트 입니다. ^^)

applicationContext.xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
</bean>

// sqlMapClient를 선언해야 합니다.
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
     <property name="dataSource" ref="dataSource" />
     <property name="configLocation" value="WEB-INF/sqlmap/sqlMapConfig.xml" />
</bean>

// DAO 입니다.
<bean id="productDao" class="springapp.repository.iBatisProductDao">
     <property name="sqlMapClient" ref="sqlMapClient" />
</bean>

<bean id="productManager" class="springapp.service.SimpleProductManager">
        <property name="productDao" ref="productDao" />
</bean>

sqlMapConfig.xml

// classpath가 root 디렉토리 임에 유의 하셔야 합니다.
<sqlMapConfig>
    <sqlMap resource="../sqlmap/Product.xml" />
</sqlMapConfig>

Product.xml

<sqlMap namespace="ProductNamespace">
    // Product 도메인 객체 입니다. 여기저기서 사용되게 됩니다.
    <typeAlias alias="Product" type="springapp.domain.Product" />

    <!-- getProductList -->
    // getProductList와 관련된 결과 값의 형태를 정의합니다.
    <resultMap id="getProductResultMap" class="Product">
        <result property="id" column="id" />
        <result property="description" column="description" />
        <result property="price" column="price" />
    </resultMap>
 
    <select id="getProductList" resultMap="getProductResultMap" resultClass="Product">
        select id, description, price from products
    </select>

    <!-- saveProduct -->
    // saveProduct와 관련된 입력 값의 형태를 정의합니다.
    <parameterMap id="saveProductParamMap" class="Product">
        <parameter property="id"  />
        <parameter property="description"  />
        <parameter property="price"  />
    </parameterMap>
 
    // parameter를 지정하는 방식에 유의 하시길 바랍니다.
    <update id="saveProduct" parameterClass="Product" >
        update products set description = (#description#), price = (#price#) where id = (#id#)
    </update>
</sqlMap>

iBatisMessageDao.java
 
// 코드 내부에는 전혀 쿼리 문이 없습니다!! 여기에 별 5개 꼭 확인하세요!
public class iBatisProductDao extends SqlMapClientDaoSupport implements ProductDao {
    @SuppressWarnings("unchecked")
    public List<Product> getProductList() {
        List<Product> products = getSqlMapClientTemplate().queryForList("getProductList");
        return products;
    }

    public void saveProduct(Product prod) {
        getSqlMapClientTemplate().update("saveProduct", prod);
    }
}

2절에서 설명한 JdbcDaoSupport 클래스의 사용법과 비교했을 때 iBatis는 이를 한단계 더 추상화 되어 있음을 알 수 있습니다. 모든 쿼리문과 해당 쿼리 결과와 객체간의 관계를 XML 외부 설정으로 따로 관리하고 있다는 것이죠.
그렇기 때문에, 앞에서 말씀드린데로 이 방법은 java 코드내에 하드코딩하는 것보다 개발의 유연성을 높이고 추후 관리시에 유리한 장점을 가집니다. (추상화란 처음에는 귀찮고 나중에는 편리한 것이지요!)

다음은 이전 4번째 spring 프레임워크 포스트에서 사용했던 튜토리얼을 iBatis로 연동시킨 예제입니다. 위에 설명드린 내용이 어떻게 적용되어 있는지 한번 더 확인하실 수 있을 것으로 생각됩니다.


4. 맺음말
휴..정말 긴 튜토리얼 리뷰였습니다. 매일 생각없이 JDBC로 개발하던 저에게는 참으로 재미난 것이 아닐 수 없었습니다.
물론 때에 따라서는 JDBC 자체가 빛나는 순간이 있겠지만, 대부분의 경우에는 iBatis가 우리를 편하게 해주지 않을까 생각됩니다. iBatis 관련 공부 중 이런 글이 있었습니다.
"iBatis는 80%의 JDBC 기능을 20%의 자바 코드로 구현한 간단한 프레임워크이다."
맞습니다. 이는 만병통치약이 아닙니다. 다만 대부분에 경우에 잘 먹히는 고마운 녀석인 것이지요. 뭐 종합감기약 정도 ^^;
그렇기 때문에 여러분도 한번 직접 경험해 보시고 개인적인 판단을 세우시길 바랍니다. 여러분에게 맞지 않을 수 있으니까요. 이상입니다. 감사합니다.

5. 참고자료
1) 웹 개발자를 위한 스프링 2.5 프로그래밍 (가메출판사, 최범균 지음)