Quick note about MyBatis with Spring. It’s a summary from official site mybatis-spring. Code demo: A simple SpingMVC + Mybatis Helloworld
Requirement
Additional jar org.mybatis.mybatis-spring is needed
Data Source
Here, any kind of DataSource is allowed. For example, org.springframework.jdbc.jar
:
<!-- In Resources folder, create jdbc.properties with following content -->
<!-- jdbc.driver=com.mysql.jdbc.Driver -->
<!-- jdbc.url=jdbc:mysql://localhost:3306/db_name -->
<!-- jdbc.username=root -->
<!-- jdbc.password=password -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"/>
</bean>
<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>
I got
CannotGetJdbcConnectionException
when coded my SpingMVC + Mybatis Helloworld example. The solution is to add?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
. So set explicitly unicode and timezone.
Setup SqlSessionFactory
In previous note MyBatis Basic, we get SqlSessionFactory
by SqlSessionFactoryBuilder
:
String resource = "path/to/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
But in MyBatis-Spring xml, bean SqlSessionFactory
is created by SqlSessionFactoryBean
:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" /> <!-- Obliged -->
<!-- Indicate location of mapper files where exists mysql statements -->
<property name="mapperLocations" value="classpath*:/mybatis/*Mapper.xml"/>
<!-- Indicate mybatis config files where exists typeAliases, settings, etc -->
<property name="configLocation" value="classpath:/mybatis/mybatis-config.xml"/>
</bean>
<!-- In java code, it does the following step to create SqlSessionFactory
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
SqlSessionFactory sessionFactory = factoryBean.getObject();
-->
Define mappers
Two ways to define:
- Annotation based
- XML based
When using xml based:
- Tell
SqlSessionFactoryBean
where to find it bymapperLocations
. - Mapper namespace must be full package path to mapper interface.
- id must be the same as function name in mapper interface, also the resultType, paramType, etc.
The following example mixed both of xml and annotation.
<?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">
<!-- namespace must indicate mapper interface full package path -->
<mapper namespace="com.dong.web.mapper.UserMapper">
<select id="getRowCount" resultType="int">
select count(*) from tb_user
</select>
</mapper>
public interface UserMapper {
int getRowCount();
@Select("select * from tb_user")
List<User> getAllUsers();
}
SqlSession
In previous note MyBatis Basic, we get SqlSession
from SqlSessionFactory
and do transaction manually like following.
SqlSession session = sqlSessionFactory.openSession();
try {
...
session.commit();
} catch(Exception e) {
e.printStackTrace();
session.rollback();
} finally {
session.close();
}
But in mybatis-spring, beans will be injected with a thread safe SqlSession
that automatically commits, rollbacks and closes the session based on Spring’s transaction configuration.
We have two ways to get sesion in DAOs:
- SqlSessionTemplate
- SqlSessionDaoSupport
But I prefer to use
MapperScannerConfigurer
orMapperFactoryBean
directly to avoid coding DAOs manually.MapperScannerConfigurer
could even automatically scan mapper interfaces. It’s the beat choice!
Transaction
So first, in mybatis-spring, just need to enable Spring transaction processing:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
Then add @Transactional
annotation on service layer.
SqlSessionTemplate
SqlSessionTemplate
implements SqlSession
. It is thread safe and can be shared by multiple DAOs or mappers. It is used to:
- Ensure that
SqlSession
used is the one associated with the current Spring transaction when call SQL method. - Manage session life-cycle, including closing, committing or rolling back the session as necessary.
- Translate MyBatis exceptions into Spring DataAccessExceptions.
So the idea here is to create a bean of SqlSessionTemplate
and inject it in DAO layer:
<!-- Create a SqlSession bean -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!-- Inject it in DAO layer -->
<bean id="userDAO" class="com.dong.web.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
</bean>
DAO class with injected sqlSession:
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getAllUsers() {
return sqlSession.select("com.dong.web.mapper.UserMapper.getAllUsers");
}
}
Batch Processing
To enable batch feature:
<bean id="sqlSession" class="com.dong.web.dao.UserServiceImpl">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
Now all SQL statements will be batched:
public void insertUsers(User[] users) {
for (User user : users) {
sqlSession.insert("org.dong.web.mapper.UserMapper.insertUser", user);
}
}
SqlSessionDaoSupport
It is an abstract support class that provides SqlSession
. We could call getSqlSession()
by extending SqlSessionDaoSupport
to get SqlSession
:
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
public User getUser(String userId) {
return (User) getSqlSession().selectOne("com.dong.web.mapper.UserMapper.getUser", userId);
}
}
MapperFactoryBean
It is used to avoid coding manually DAOs by SqlSessionDaoSupport
or SqlSessionTemplate
, so no DAOs here in java code! It handles creating an SqlSession as well as closing it. If there is a Spring transaction in progress, the session will also be committed or rolled back when the transaction completes.
<!-- Create a MapperFactoryBean for UserMapper interface -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.dong.web.mapper.UserMapper" />
</bean>
<!-- Inject mapper in service layer -->
<bean id="userService" class="com.dong.web.service.UserServiceImpl">
<property name="userMapper" ref="userMapper" />
</bean>
public class UserServiceImpl implements UserService {
private UserMapper userMapper;
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public List<User> getAllUsers() {
return this.userMapper.getAllUsers();
}
}
MapperScannerConfigurer
With MapperFactoryBean
, we need to declare a bean for each mapper interface. So better to use MapperScannerConfigurer
to automatically scan to find mapper interfaces and register each of them as a MapperFactoryBean
.
<!-- Scan all the interfaces under mapper/ -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dong.web.mapper" />
</bean>
IDE will mention
Could not autowired
, it’s because eachMapperFactoryBean
is created byMapperScannerConfigurer
, IDE could not find yet an existing implementation, D’ont worry. (To check, at least, when I tried my helloworld, it works fine even with this error check)