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 requiredAfter 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 the sqlSessionTemplate 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.
- data-access-spring-context.xml: Holding spring configuration defining my data access
- mybatis-config.xml: Standard MyBatis configuration file
- user-dao.xml: MyBatis mapper file. one mapper file for one DAO defined in dao-spring-context.xml
- dao-spring-context.xml: Holding all DAOs used by application
- UserDAO.java
Version of various plugin and framework I used for this little POC...
- Spring 3.0.5
- MyBatis 3.0.2
- MyBatis-Spring 1.0.0-RC2
Let's start visiting each of these files to understand the required configuration...
- data-access-spring-context.xml:
123456789101112131415161718192021222324252627<?
XML:NAMESPACE
PREFIX = [default] http://www.springframework.org/schema/beans
NS
=
"http://www.springframework.org/schema/beans"
/><
beans
xmlns
=
"http://www.springframework.org/schema/beans"
xsi:schemalocation
=
"http://www.springframework.org/schema/beans 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"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx
=
"http://www.springframework.org/schema/tx"
xmlns:aop
=
"http://www.springframework.org/schema/aop"
>
<
bean
id
=
propertyConfigurer
class
=
org
.springframework.beans.factory.config.PropertyPlaceholderConfigurer>
<
property
name
=
"location"
>
<
value
>classpath:jdbc.properties</
value
>
</
property
>
</
bean
>
<
bean
id
=
dataSource
class
=
org
.springframework.jndi.JndiObjectFactoryBean>
<
property
name
=
"jndiName"
>
<
value
>${DataSource.JndiName.DS}</
value
>
</
property
>
<
property
name
=
"resourceRef"
>
<
value
>false</
value
>
</
property
>
</
bean
>
<
bean
id
=
sqlSessionFactory
class
=
org
.mybatis.spring.SqlSessionFactoryBean>
<
property
name
=
"configLocation"
value
=
"classpath:com/jak/sandbox/config/dataaccess/sqlmap/mybatis-config.xml"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
>
</
property
></
property
></
bean
>
<
bean
id
=
sqlSessionTemplate
class
=
org
.mybatis.spring.SqlSessionTemplate>
<
property
name
=
"sqlSessionFactory"
ref
=
"sqlSessionFactory"
>
</
property
></
bean
>
</
beans
>
- mybatis-config.xml:
12345678<
CONFIGURATION
>
<
TYPEALIASES
>
<
TYPEALIAS
type
=
"com.jak.sandbox.shared.dto.model.User"
alias
=
"User"
>
</
TYPEALIAS
></
TYPEALIASES
>
<
MAPPERS
>
<
MAPPER
resource
=
"com/jak/sandbox/biz/dataaccess/sqlmap/module/user-dao.xml"
>
</
MAPPER
></
MAPPERS
>
</
CONFIGURATION
>
- user-dao.xml
12345678<
MAPPER
namespace
=
"com.jak.sandbox.biz.dao.UserDAO"
>
<
RESULTMAP
id
=
UserMap
type
=
"User"
>
<
RESULT
property
=
"userId"
column
=
"ID"
>
<
RESULT
property
=
"userName"
column
=
"NAME"
>
</
RESULT
></
RESULT
></
RESULTMAP
>
<
SELECT
id
=
selectUsers
resultmap
=
"UserMap"
> SELECT ID, NAME FROM USERS</
SELECT
>
</
MAPPER
>
- dao-spring-context.xml:
1234567<?
XML:NAMESPACE
PREFIX = [default] http://www.springframework.org/schema/beans
NS
=
"http://www.springframework.org/schema/beans"
/><
beans
xmlns
=
"http://www.springframework.org/schema/beans"
xsi:schemalocation
=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx
=
"http://www.springframework.org/schema/tx"
>
<
bean
id
=
userDAO
class
=
org
.mybatis.spring.MapperFactoryBean>
<
property
name
=
"sqlSessionTemplate"
ref
=
"sqlSessionTemplate"
>
<
property
name
=
"mapperInterface"
value
=
"com.jak.sandbox.biz.dao.UserDAO"
>
</
property
></
property
></
bean
>
</
beans
>
- UserDAO.java:
12345678910package
com.jak.sandbox.biz.dao;
import
java.util.List;
import
com.jak.sandbox.shared.dto.model.User;
public
interface
UserDAO
{
List<User> 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 selectUsers method.
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.