Thursday, July 12, 2012


Using MyBatis 3 with Spring 3 Using Mapper XML Files


http://jaykhimani.blogspot.com/2010/11/using-mybatis-3-with-spring-3-using.html

Recently I started to work on design an application as a part of a new project assigned to me at my work place. Since this is totally a new application to be developed from scratch, I got liberty/flexibility to choose the tools/frameworks which will form the foundation of the application. With lots of frameworks to choose from and many choices available for different tiers, I started evaluating each framework against the requirement of the application vs where it fits in which tier. When I came to data access tier (resource tier as some say), I realized that MyBatis (new name of iBatis after it moved to Google code from Apache) was best fit as ORM framework. I also decided to use Spring 3.x as DI framework and we all know how well MyBatis/iBatis and Spring (beans, data access, transaction etc) gel with each other. Using Spring's DI, declarative transaction management and MyBatis' query externalization, we as a developer can concentrate on implementing application/business logic and keep rest of the stuff like transaction management, JDBC resource management etc to be "headache" of container/frameworks.

So as I decided to go with MyBatis, I came to know that Spring 3.0 development finished first then MyBatis 3 and hence there is no official support in Spring 3.0 for MyBatis 3.0. Its expected to be there in Spring 3.1 release (Source: Spring-MyBatis integration RC2 reference doc). But thanks to MyBatis community, there is already a project for myabtis-spring integrations. At start I struggled a bit on how to wire Spring beans, dao with MyBatis mapper configuration files. As I was googling around, I stumbled upon a very good post by Giovanni Cuccu on this. It was very enlightening and provided me a good start. I created a maven/spring/mybatis project in my eclipse and started writing down some quick and crude code to test out. As soon as I tried to run my sample code I encountered...

java.lang.IllegalArgumentException: Property 'sqlSessionTemplate' is required
After debugging the mybatis-spring integration source code, I found that since I was using Spring DAO using mybatis-spring integration, org.mybatis.spring.support.SqlSessionDaoSupport class' checkDaoConfig explicitly checks for thesqlSessionTemplate property to be defined. After trying for some time and to no success, I changed my DAO implementation to be a mapper interface using MapperFactoryBean. This eliminated of having a class implementing SqlSessionDaoSupport just to invoke Spring's SqlMapClientTemplate's helper methods. Instead now its just an interface holding method signatures mirroring the java representation of mapped sql statements in MyBatis mapper configuration files. With this changed implementation, everything worked like a charm.

Here is the spring wiring I did to make mybatis-spring work with mapper files. Usually I keep my spring configuration logically separated by keep configuration related to data access in one file and application DAOs go to another. So with this I end up creating four configuration files and one java interface for this project.
  1. data-access-spring-context.xml: Holding spring configuration defining my data access
  2. mybatis-config.xml: Standard MyBatis configuration file
  3. user-dao.xml: MyBatis mapper file. One mapper file for one DAO defined in dao-spring-context.xml
  4. dao-spring-context.xml: Holding all DAOs used by application
  5. UserDAO.java

Version of various plugin and framework I used for this little POC...
  1. Spring 3.0.5
  2. MyBatis 3.0.2
  3. MyBatis-Spring 1.0.0-RC2

Let's start visiting each of these files to understand the required configuration...
  1. data-access-spring-context.xml:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                            http://www.springframework.org/schema/tx
                            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                            http://www.springframework.org/schema/aop
                            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" id="propertyConfigurer">
            <property name="location">
                <value>classpath:jdbc.propertiesvalue>
            property>
        bean>
        <bean class="org.springframework.jndi.JndiObjectFactoryBean" id="dataSource">
            <property name="jndiName">
                <value>${DataSource.JndiName.DS}value>
            property>
            <property name="resourceRef">
                <value>falsevalue>
            property>
        bean>
       <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
            <property name="configLocation" value="classpath:com/jak/sandbox/config/dataaccess/sqlmap/mybatis-config.xml">
            <property name="dataSource" ref="dataSource">
        property>property>bean>
         
        <bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSessionTemplate">
            <property name="sqlSessionFactory" ref="sqlSessionFactory">
        property>bean>
         
    beans>
    For simplicity, I've not included the transaction manager, advice point cut etc in this. So here, main point of interest is how the sql session factory and sql session template is configured which actually wires MyBatis configuration to Spring data access
  2. mybatis-config.xml:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    <configuration>
        <typealiases>
            <typealias alias="User" type="com.jak.sandbox.shared.dto.model.User">
        typealias>typealiases>
        <mappers>
            <mapper resource="com/jak/sandbox/biz/dataaccess/sqlmap/module/user-dao.xml">
        mapper>mappers>
    configuration>
    This is a standard MyBatis configuration file, holding all the mappers of the applications.
  3. user-dao.xml
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <mapper namespace="com.jak.sandbox.biz.dao.UserDAO">
        <resultmap id="UserMap" type="User">
            <result column="ID" property="userId">
            <result column="NAME" property="userName">
        result>result>resultmap>
        <select id="selectUsers" resultmap="UserMap">
            SELECT ID, NAME FROM
            USERS
        select>
    mapper>
    Here I've defined a SQL queries required my my User DAO. User DAO is wired to spring data access and mybatis in my DAO spring context i.e. dao-spring-context.xml
  4. dao-spring-context.xml:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
        <bean class="org.mybatis.spring.MapperFactoryBean" id="userDAO">
            <property name="sqlSessionTemplate" ref="sqlSessionTemplate">
            <property name="mapperInterface" value="com.jak.sandbox.biz.dao.UserDAO">
        property>property>bean>
    beans>
    Notice that the fully qualified interface name defined as mapperInterface serves as the namespace for the mapper in user-dao.xml
  5. UserDAO.java:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.jak.sandbox.biz.dao;
    import java.util.List;
    import com.jak.sandbox.shared.dto.model.User;
    public interface UserDAO
    {
        List selectUsers();
    }

Well this is pretty much you need to do in order to use MyBatis with Spring. Now just write a test program which loads both spring context as defined above, inject userDAO bean to any of your Java class (typically a business service) and invoke selectUsersmethod.

MyBatis & Spring made data access so simple and using interface as your data access and having implementation as MyBatis mappers, makes data access more cleaner. This is because ideally your data access should only be taking request from business tier to perform some operations like select/insert/update/delete and return the result of such operation back. There should not be any logic. Having your data access as interfaces, takes the any chances of developer mistakenly putting any logic.