文章目录
MyBatis
1. 前言
在 MyBatis 中,当我们编写好访问数据库的映射器接口后,MapperScannerConfigurer
就能自动成批地帮助我们根据这些接口生成 DAO 对象,然后我们再使用 Spring 把这些 DAO 对象注入到业务逻辑层的对象( Service 类的对象)。因此,在这种情况下的 DAO 层,我们几乎不用编写代码,而且也没有地方编写,因为只有接口。这固然方便,不过如果我们需要在 DAO 层写一些代码的话,这种方式就无能为力了。此时, MyBatis-Spring 提供给我们的 SqlSessionDaoSupport
类就派上了用场。
2. 认识 SqlSessionDaoSupport
org.mybatis.spring.support.SqlSessionDaoSupport
是spring-mybatis包下的类,继承了DaoSupport
抽象类,作为DAO 的基类来使用。它需要一个 SqlSessionTemplate
或一个 SqlSessionFactory
,若两者都设置了,则 SqlSessionFactory
会被忽略(实际上它接收了 SqlSessionFactory 后也会利用factory创建一个 SqlSessionTemplate )。
这样,我们在子类中就能通过调用 SqlSessionDaoSupport类的 getSqlSession()
方法来获取这个 SqlSessionTemplate
对象。而 SqlSessionTemplate 类实现了 SqlSession 接口,因此,有了 SqlSessionTemplate 对象,访问数据库就不在话下了。所以,我们需要 Spring 给 SqlSessionDaoSupport 类的子类的对象(多个 DAO 对象)注入一个 SqlSessionFactory 或一个 SqlSessionTemplate 。好消息是, SqlSessionTemplate 是线程安全的,因此,给多个 DAO 对象注入同一个 SqlSessionTemplate 是没有问题的,本例也将注入SqlSessionFactory 。
但坏消息是,自 mybatis-spring-1.2.0
以来, SqlSessionDaoSupport 的 setSqlSessionTemplate
和 setSqlSessionFactory
两个方法上的@Autowired
注解被删除,这就意味着继承于 SqlSessionDaoSupport 的 DAO 类,它们的对象不能被自动注入 SqlSessionFactory 或 SqlSessionTemplate 对象。如果在 Spring 的配置文件中一个一个地配置的话,显然太麻烦。比较好的解决办法是在我们的 DAO 类中覆盖这两个方法之一,并加上 @Autowired
注解。那么如果在每个 DAO 类中都这么做的话,显然很低效。更优雅的做法是,写一个继承于 SqlSessionDaoSupport 的BaseDao
,在 BaseDao 中完成这个工作,然后其他的 DAO 类再都从 BaseDao 继承。
3. BaseDaoImpl
3.1 基于注解实现
继承spring 的SqlSessionDaoSupport
,重写setSqlSessionTemplate()
方法
public class BaseDaoImpl extends SqlSessionDaoSupport {
@Autowired
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
super.setSqlSessionTemplate(sqlSessionTemplate);
}
}
配置好SqlSessionFactory
<!-- mybatis配置 -->
<bean id="sqlSessionFactory" class="com.sas.core.spring.SasSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="${mybatis.mapperLocations}" />
<property name="configTypeAliasLocations" value="${mybatis.configLocation}" />
</bean>
3.2基于xml配置
利用spring的set注入实现
<bean id="baseSqlDao" class="com.sas.core.dao.impl.sql.BaseDaoSqlImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="sqlSessionTemplate" ref="sqlSessionTemplate" />
</bean>
必须事先配置好 SqlSessionFactory
或 SqlSessionTemplate
<!-- mybatis配置 -->
<bean id="sqlSessionFactory" class="com.sas.core.spring.SasSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="${mybatis.mapperLocations}" />
<property name="configTypeAliasLocations" value="${mybatis.configLocation}" />
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"
scope="prototype">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
3.3 加强 BaseDaoImpl
基于重构原则,我们可以在BaseDaoImpl
内通过泛型创建通用方法,加强BaseDaoImpl
BaseMeta
对应你实体类的基类
/***************
* 所有dao实现的基类
* @param <TObject> 实体类
* @param <TId> 主键类型
*/
public class BaseDaoImpl<TObject extends BaseMeta, TId extends Object> extends SqlSessionDaoSupport
implements BaseDao<TObject, TId> {
/***********
* get the class of meta
* @Title: getMetaClass
*
* @return
* @throws
*/
@SuppressWarnings("rawtypes")
protected Class getMetaClass() {
throw new ServerUnknownException("please add method getMetaClass in subclass of BaseDaoSqlImpl!");
}
/******************
* 获取方法名
* @return
*/
private final String getMetaClassName(){
return this.getMetaClass().getSimpleName();
}
/***************
* cachedao 已经实现了该方法
*/
public final String getTableName() {
throw new UnsupportedOperationException("BaseDaoSqlImpl.getTableName is not supportted for" +
" BaseCacheDaoImpl has already implementted this method!");
}
/*************
* 添加记录,成功返回对象, 否则返回null
* @param obj
* @return
*/
public final TObject add(TObject obj){
if(obj == null){
return null;
}
if(getSqlSession().insert(this.getMetaClassName() + ".add", obj) > 0){
return obj;
}
return null;
}
/***************
* 根据id获取对象
* @param id
* @return
*/
public final TObject getById(TId id){
return getSqlSession().selectOne(this.getMetaClassName() + ".getById", id);
}
/**************
* 根据id列表获取批量对象
* @param ids
* @return
*/
public final List<TObject> listByIds(TId[] ids){
if (ArrayUtils.isEmpty(ids)) {
return Collections.emptyList();
}
List<TObject> tObjectList = this.getSqlSession().selectList(this.getMetaClassName() + ".listByIds", ids);
return this.assureNotNullList(tObjectList);
}
/*************
* 根据id列表批量删除对象,返回删除的行数
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public final int deleteByIds(TId... ids)
{
if(ArrayUtils.isEmpty(ids)){
return 0;
}
return getSqlSession().delete(this.getMetaClassName() + ".deleteByIds",ids);
}
/***************
* 确保返回的list不是null,以免nullpointexception
* @param list
* @return
*/
protected final <T> List<T> assureNotNullList(final List<T> list){
return list == null ? new ArrayList<T>(0) : list;
}
}
3.4 创建BaseDaoImpl的实现类
根据具体业务创建实现类,例如 UserDaoSqlImpl
,实现 UserDao
接口
具体SqlSession的用法可以看官方手册
public class UserDaoSqlImplextends BaseDaompl<User, Long> implements SasUserDao {
@SuppressWarnings("rawtypes")
@Override
protected Class getMetaClass(){
return SasUser.class;
}
@Override
public boolean updateLastLoginTime(long userId,long newLoginTime) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("userId", userId);
params.put("lastLoginTime", newLoginTime);
//User 是UserMapper.xml的`namespace`属性,可以自定义
//updateLastLoginTime 是UserMapper.xml的语句id
return this.getSqlSession().update("User.updateLastLoginTime", params) > 0;
}
}
3.5 剩余代码补齐
3.5.2 BaseDao
public interface BaseDao<TObject extends BaseMeta, TId extends Object> {
/***************
* 获取表名
* @return
*/
public String getTableName();
/*************
* 添加记录,成功返回对象, 否则返回null
* @param obj
* @return
*/
public TObject add(TObject obj);
/***************
* 根据id获取对象
* @param id
* @return
*/
public TObject getById(TId id);
/**************
* 根据id列表获取批量对象
* @param ids
* @return
*/
public List<TObject> listByIds(TId[] ids);
/*************
* 根据id列表批量删除对象,返回删除的行数
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public int deleteByIds(TId... ids);
}
3.5.2 UserDao
public interface UserDao extends BaseDao<SasUser, Long>{
boolean updateLastLoginTime(final long userId, final long newLoginTime);
}
3.5 UserMapper.xml
<?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">
<mapper namespace="User">
<update id="updateLastLoginTime" parameterType="map">
UPDATE
user
SET
lastLoginTime = #{lastLoginTime}
WHERE
id = #{id}
</update>
</mapper>
4. 总结
至此,我们可以通过继承BaseDaoImpl
来手动编写Dao,完美完结
撒花,结束~
附 : Junit 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:application-context.xml"})
//@ContextConfiguration({"classpath:spring-context.xml","classpath:spring-context-druid.xml","classpath:spring-context-mybatis.xml"})
public class UserDaoTest {
@Autowired
private UserDao userDao;
@Test
public void TestAddUser(){
Long loginTime= System.currentTimeMillis();
Long id = 1L;
if(!userDao.updateLastLoginTime(id ,loginTime);){
System.out.println("Failed to updateLastLoginTime!");
return;
}
System.out.println("Success to updateLastLoginTime! id : " + id );
}
}