单独使用mybatis是有很多限制的(
比如无法实现跨越多个session的事务
),而且很多业务系统本来就是使用spring来管理的事务,因此mybatis最好与spring集成起来使用。
1 Spring集成配置
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"></property>
<property name="configLocation" value="classpath:context/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath*:/com/tx/demo/**/*SqlMap.xml" />
<property name="typeHandlersPackage" value="com.tx.core.mybatis.handler"></property>
<property name="failFast" value="true"></property>
<property name="plugins">
<array>
<bean class="com.tx.core.mybatis.interceptor.PagedDiclectStatementHandlerInterceptor">
<property name="dialect">
<bean class="org.hibernate.dialect.PostgreSQLDialect"></bean>
</property>
</bean>
</array>
</property>
</bean>
<bean id="myBatisExceptionTranslator" class="org.mybatis.spring.MyBatisExceptionTranslator">
<property name="dataSource">
<ref bean="datasource"></ref>
</property>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
<constructor-arg name="executorType" ref="SIMPLE"></constructor-arg>
<constructor-arg name="exceptionTranslator" ref="myBatisExceptionTranslator"></constructor-arg>
</bean>
<bean id="myBatisDaoSupport" class="com.tx.core.mybatis.support.MyBatisDaoSupport">
<property name="sqlSessionTemplate">
<ref bean="sqlSessionTemplate"/>
</property>
</bean>
2 Spring事务配置
<context:component-scan base-package="com.xxx.service" />
<jee:jndi-lookup id="jndiDataSource" jndi-name="java:comp/env/jdbc/datasource" />
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="jndiDataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
或:
<context:component-scan base-package="com.xxx.service" />
<jee:jndi-lookup id="jndiDataSource" jndi-name="java:comp/env/jdbc/datasource" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="edit*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="del*">PROPAGATION_REQUIRED</prop>
<prop key="*">readOnly</prop>
</props>
</property>
</bean>
<aop:config>
<aop:pointcut id="pc" expression="execution(public * com.jeasy..service.*.*(..))" />
<aop:advisor pointcut-ref="pc" advice-ref="baseTransactionProxy" />
</aop:config>
3 单个集成
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="jndiDataSource" />
<property name="configLocation" value="classpath:/mybatis/mybatis-config.xml" />
<property name="typeAliasesPackage" value="com.xxx.dto" />
</bean>
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.xxx.dao.UserDao" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
我们不但要明白如何使用,更要明白为什么要这么使用。
SqlSessionFactoryBean是一个工厂bean,它的作用就是解析配置(数据源、别名等)
。
MapperFactoryBean是一个工厂bean
,在spring容器里,工厂bean是有特殊用途的,当spring将工厂bean注入到其他bean里时,
它不是注入工厂bean本身而是调用bean的getObject方法
。我们接下来就看看这个getObjec方法干了些什么:
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
看到这里大家应该就很明白了,这个方法和我们之前单独使用Mybatis的方式是一样的,都是先获取一个Sqlsession对象,然后再从Sqlsession里获取Mapper对象(
再次强调Mapper是一个代理对象,它代理的是mapperInterface接口,而这个接口是用户提供的dao接口
)。自然,最终注入到业务层就是这个Mapper对象。
实际的项目一般来说不止一个Dao,如果你有多个Dao那就按照上面的配置依次配置即可。
4 如何使用批量更新
前一节讲了如何注入一个mapper对象到业务层, mapper的行为依赖于配置,
mybatis默认使用单个更新(即ExecutorType默认为SIMPLE而不是BATCH)
,当然我们可以通过修改mybatis配置文件来修改默认行为,但如果我们只想让某个或某几个mapper使用批量更新就不得行了。
这个时候我们就需要使用模板技术
:
<bean id="sqlSessionTemplateSimple" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="SIMPLE"/>
</bean>
<bean id="sqlSessionTemplateBatch" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="BATCH"/>
</bean>
这里笔者定义了两个模板对象,一个使用单个更新,一个使用批量更新。
有了模板之后我们就可以改变mapper的行为方式了
:
<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.xxx.dao.UserDao" />
<property name="sqlSessionTemplate" ref="sqlSessionTemplateBatch" />
</bean>
跟上一节的mapper配置不同的是,
这里不需要配置sqlSessionFactory属性,只需要配置sqlSessionTemplate(sqlSessionFactory属性在模板里已经配置好了)
。
由于在3.1.1升级后,可直接通过BatchExcutor实现具体的批量执行。
在该excutor中会重用上一次相同的PreparedStatement
。
5 通过自动扫描简化mapper的配置
前面的章节可以看到,我们的dao需要一个一个的配置在配置文件中,如果有很多个dao的话配置文件就会非常大,这样管理起来就会比较痛苦。幸好mybatis团队也意识到了这点,他们利用spring提供的自动扫描功能封装了一个自动扫描dao的工具类,这样我们就可以使用这个功能简化配置:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx.dao" />
<property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateSimple" />
<property name="markerInterface" value="com.xxx.dao.SimpleDao" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx.dao" />
<property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateBatch" />
<property name="markerInterface" value="com.xxx.dao.BatchDao" />
</bean>
MapperScannerConfigurer本身涉及的spring的技术我就不多讲了,感兴趣且对spring原理比较了解的可以去看下它的源码。我们重点看一下它的三个属性:
basePackage:扫描器开始扫描的基础包名,支持嵌套扫描;
sqlSessionTemplateBeanName:前文提到的模板bean的名称;
markerInterface:基于接口的过滤器
,实现了该接口的dao才会被扫描器扫描,与basePackage是与的作用。
除了使用接口过滤外,还可使用注解过滤
:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx.dao" />
<property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateBatch" />
<property name="annotationClass" value="com.xxx.dao.BatchAnnotation" />
</bean>
annotationClass:
配置了该注解的dao才会被扫描器扫描
,与basePackage是与的作用。需要注意的是,与上个接口过滤条件只能配一个。
markerInterface:
markerInterface是用于指定一个接口的
,当指定了markerInterface之后,MapperScannerConfigurer将只注册继承自markerInterface的接口。
6 与Spring集成源码分析
6.1 SqlSessionFactory
我们知道在Mybatis的所有操作都是基于一个SqlSession的,而SqlSession是由SqlSessionFactory来产生的,SqlSessionFactory又是由SqlSessionFactoryBuilder来生成的。
但是Mybatis-Spring是基于SqlSessionFactoryBean的
。在使用Mybatis-Spring的时候,我们也需要SqlSession,而且这个SqlSession是内嵌在程序中的,一般不需要我们直接访问。
SqlSession也是由SqlSessionFactory来产生的,但是Mybatis-Spring给我们封装了一个SqlSessionFactoryBean
,在这个bean里面还是
通过SqlSessionFactoryBuilder来建立对应的SqlSessionFactory,进而获取到对应的SqlSession
。通过SqlSessionFactoryBean我们可以通过对其指定一些属性来提供Mybatis的一些配置信息。所以接下来我们需要在Spring的applicationContext配置文件中定义一个SqlSessionFactoryBean。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="classpath:com/tiantian/ckeditor/mybatis/mappers/*Mapper.xml" />
<property name="typeAliasesPackage" value="com.tiantian.ckeditor.model" />
</bean>
接下来就是在Spring的applicationContext文件中定义我们想要的Mapper对象对应的MapperFactoryBean了。
通过MapperFactoryBean可以获取到我们想要的Mapper对象
。MapperFactoryBean实现了Spring的FactoryBean接口,
所以MapperFactoryBean是通过FactoryBean接口中定义的getObject方法来获取对应的Mapper对象的
。在定义一个MapperFactoryBean的时候有两个属性需要我们注入,
一个是Mybatis-Spring用来生成实现了SqlSession接口的SqlSessionTemplate对象的sqlSessionFactory
;`另一个就是我们所要返回的对应的Mapper接口了。
定义好相应Mapper接口对应的MapperFactoryBean之后,我们就可以把我们对应的Mapper接口注入到由Spring管理的bean对象中了,比如Service bean对象。这样当我们需要使用到相应的Mapper接口时,
MapperFactoryBean会从它的getObject方法中获取对应的Mapper接口
,而
getObject内部还是通过我们注入的属性调用SqlSession接口的getMapper(Mapper接口)方法来返回对应的Mapper接口的
。这样就通过把SqlSessionFactory和相应的Mapper接口交给Spring管理实现了Mybatis跟Spring的整合。
如果想使用MapperScannerConfigurer,想要了解该类的作用,就得先了解MapperFactoryBean。
6.2 MapperFactoryBean
MapperFactoryBean的出现
为了代替手工使用SqlSessionDaoSupport或SqlSessionTemplate编写数据访问对象(DAO)的代码,使用动态代理实现
。
比如下面这个官方文档中的配置:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
org.mybatis.spring.sample.mapper.UserMapper是一个接口,我们创建一个MapperFactoryBean实例,然后注入这个接口和sqlSessionFactory(mybatis中提供的SqlSessionFactory接口,MapperFactoryBean会使用SqlSessionFactory创建SqlSession)这两个属性。
之后想使用这个UserMapper接口的话,直接通过spring注入这个bean,然后就可以直接使用了,
spring内部会创建一个这个接口的动态代理
。
当发现要使用多个MapperFactoryBean的时候,一个一个定义肯定非常麻烦,于是mybatis-spring提供了MapperScannerConfigurer这个类,
它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean
。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.spring.sample.mapper" />
<property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate" />
</bean>
这段配置会扫描org.mybatis.spring.sample.mapper下的所有接口,然后创建各自接口的动态代理类。
如果我们需要使用MapperScannerConfigurer来帮我们自动扫描和注册Mapper接口的话我们需要在Spring的applicationContext配置文件中定义一个MapperScannerConfigurer对应的bean。对于MapperScannerConfigurer而言有一个属性是我们必须指定的,那就是basePackage。
basePackage是用来指定Mapper接口文件所在的基包的,在这个基包或其所有子包下面的Mapper接口都将被搜索到
。多个基包之间可以使用逗号或者分号进行分隔。最简单的MapperScannerConfigurer定义就是只指定一个basePackage属性,如:
package org.format.dynamicproxy.mybatis.dao;
public interface UserDao {
public User getById(int id);
public int add(User user);
public int update(User user);
public int delete(User user);
public List<User> getAll();
}
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:sqlMapConfig.xml"/>
<property name="mapperLocations" value="classpath:sqlMapper/*Mapper.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.format.dynamicproxy.mybatis.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
有时候我们指定的基包下面的并不全是我们定义的Mapper接口,为此MapperScannerConfigurer还为我们提供了另外两个可以缩小搜索和注册范围的属性。
一个是annotationClass,另一个是markerInterface
。
- annotationClass:当指定了annotationClass的时候,MapperScannerConfigurer将只注册使用了annotationClass注解标记的接口。
- markerInterface:markerInterface是用于指定一个接口的,当指定了markerInterface之后,MapperScannerConfigurer将只注册继承自markerInterface的接口。
如果上述两个属性都指定了的话,那么MapperScannerConfigurer将取它们的并集,而不是交集
。即使用了annotationClass进行标记或者继承自markerInterface的接口都将被注册为一个MapperFactoryBean。
- sqlSessionFactory:
这个属性已经废弃
。当我们使用了多个数据源的时候我们就需要通过sqlSessionFactory来指定在注册MapperFactoryBean的时候需要使用的SqlSessionFactory,因为在没有指定sqlSessionFactory的时候,会以Autowired的方式自动注入一个。换言之当我们只使用一个数据源的时候,即只定义了一个SqlSessionFactory的时候我们就可以不给MapperScannerConfigurer指定SqlSessionFactory
。
- sqlSessionFactoryBeanName:它的功能跟sqlSessionFactory是一样的,只是它指定的是定义好的SqlSessionFactory对应的bean名称。
- sqlSessionTemplate:
这个属性已经废弃
。它的功能也是相当于sqlSessionFactory的,因为就像前面说的那样,MapperFactoryBean最终还是使用的SqlSession的getMapper方法取的对应的Mapper对象。当定义有多个SqlSessionTemplate的时候才需要指定它。对于一个MapperFactoryBean来说SqlSessionFactory和SqlSessionTemplate只需要其中一个就可以了,当两者都指定了的时候,SqlSessionFactory会被忽略
。
- sqlSessionTemplateBeanName:指定需要使用的sqlSessionTemplate对应的bean名称。
注意:
由于使用sqlSessionFactory和sqlSessionTemplate属性时会使一些内容在PropertyPlaceholderConfigurer之前加载
,导致在配置文件中使用到的外部属性信息无法被及时替换而出错,
因此官方现在新的Mybatis-Spring中已经把sqlSessionFactory和sqlSessionTemplate属性废弃了
,推荐大家使用sqlSessionFactoryBeanName属性和sqlSessionTemplateBeanName属性。
我们先通过测试用例debug查看userDao的实现类到底是什么:
我们可以看到,userDao是1个MapperProxy类的实例。看下MapperProxy的源码,没错,实现了InvocationHandler,说明使用了jdk自带的动态代理:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口
,BeanDefinitionRegistryPostProcessor接口是一个可以修改spring工厂中已定义的bean的接口,
该接口有个postProcessBeanDefinitionRegistry方法
。
然后我们看下ClassPathMapperScanner中的关键是如何扫描对应package下的接口的。
其实MapperScannerConfigurer的作用也
就是将对应的接口的类型改造为MapperFactoryBean
,而这个MapperFactoryBean的属性mapperInterface是原类型。MapperFactoryBean本文开头已分析过。
所以最终我们还是要分析MapperFactoryBean的实现原理!
MapperFactoryBean继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类继承DaoSupport抽象类,DaoSupport抽象类实现了InitializingBean接口,因此实例个MapperFactoryBean的时候,都会调用InitializingBean接口的afterPropertiesSet方法。
DaoSupport的afterPropertiesSet方法:
MapperFactoryBean重写了checkDaoConfig方法:
然后通过spring工厂拿对应的bean的时候:
这里的SqlSession是SqlSessionTemplate,SqlSessionTemplate的getMapper方法:
Configuration的getMapper方法,会使用MapperRegistry的getMapper方法:
MapperRegistry的getMapper方法:
MapperProxyFactory构造MapperProxy:
没错! MapperProxyFactory就是使用了jdk组带的Proxy完成动态代理。
MapperProxy本来一开始已经提到。MapperProxy内部使用了MapperMethod类完成方法的调用:
下面,我们以UserDao的getById方法来debug看看MapperMethod的execute方法是如何走的:
@Test
public void testGet() {
int id = 1;
System.out.println(userDao.getById(id));
}
<select id="getById" parameterType="int" resultType="org.format.dynamicproxy.mybatis.bean.User">
SELECT * FROM users WHERE id = #{id}
</select>
6.4 SqlSessionTemplate
Mybatis-Spring为我们提供了一个实现了SqlSession接口的SqlSessionTemplate类,
它是线程安全的,可以被多个Dao同时使用
。同时它还跟Spring的事务进行了关联,确保当前被使用的SqlSession是一个已经和Spring的事务进行绑定了的。而且它还可以自己管理Session的提交和关闭。当使用了Spring的事务管理机制后,SqlSession还可以跟着Spring的事务一起提交和回滚。
使用SqlSessionTemplate时我们可以在Spring的applicationContext配置文件中如下定义:
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
通过源码我们何以看到 SqlSessionTemplate 实现了SqlSession接口,也就是说我们可以使用SqlSessionTemplate 来代理以往的DefailtSqlSession完成对数据库的操作,
但是DefailtSqlSession这个类不是线程安全的
,所以这个类不可以被设置成单例模式的。
如果是常规开发模式 我们每次在使用DefailtSqlSession的时候都从SqlSessionFactory当中获取一个就可以了。
但是与Spring集成以后,Spring提供了一个全局唯一的SqlSessionTemplate示例 来完成DefailtSqlSession的功能
,问题就是:无论是多个dao使用一个SqlSessionTemplate,还是一个dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession,当多个web线程调用同一个dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,那么它是如何确保线程安全的呢?让我们一起来分析一下。
- 首先,通过如下代码创建代理类,表示创建SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
- 核心代码就在 SqlSessionInterceptor的invoke方法当中:
private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取SqlSession(这个SqlSession才是真正使用的,它不是线程安全的)
//这个方法可以根据Spring的事务上下文来获取事务范围内的sqlSession
//一会我们在分析这个方法
final SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
//调用真实SqlSession的方法
Object result = method.invoke(sqlSession, args);
//然后判断一下当前的sqlSession是否被Spring托管 如果未被Spring托管则自动commit
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
//返回执行结果
return result;
} catch (Throwable t) {
//如果出现异常则根据情况转换后抛出
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
//关闭sqlSession
//它会根据当前的sqlSession是否在Spring的事务上下文当中来执行具体的关闭动作
//如果sqlSession被Spring管理 则调用holder.released(); 使计数器-1
//否则才真正的关闭sqlSession
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
- 在上面的invoke方法当中使用了俩个工具方法 分别是:
SqlSessionUtils.getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)
SqlSessionUtils.closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)
那么这个俩个方法又是如何与Spring的事务进行关联的呢?
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
holder.requested();
return holder.getSqlSession();
}
SqlSession session = sessionFactory.openSession(executorType);
if (isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
bindResource(sessionFactory, holder);
registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (getResource(environment.getDataSource()) == null) {
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} else {
}
return session;
}
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
if ((holder != null) && (holder.getSqlSession() == session)) {
holder.released();
} else {
session.close();
}
}
这样我们就可以通过Spring的依赖注入在Dao中直接使用SqlSessionTemplate来编程了,这个时候我们的Dao可能是这个样子:
package com.tiantian.mybatis.dao;
import java.util.List;
import javax.annotation.Resource;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import com.tiantian.mybatis.model.Blog;
@Repository
public class BlogDaoImpl implements BlogDao {
private SqlSessionTemplate sqlSessionTemplate;
public void deleteBlog(int id) {
sqlSessionTemplate.delete("com.tiantian.mybatis.mapper.BlogMapper.deleteBlog", id);
}
public Blog find(int id) {
return sqlSessionTemplate.selectOne("com.tiantian.mybatis.mapper.BlogMapper.selectBlog", id);
}
public List<Blog> find() {
return this.sqlSessionTemplate.selectList("com.tiantian.mybatis.mapper.BlogMapper.selectAll");
}
public void insertBlog(Blog blog) {
this.sqlSessionTemplate.insert("com.tiantian.mybatis.mapper.BlogMapper.insertBlog", blog);
}
public void updateBlog(Blog blog) {
this.sqlSessionTemplate.update("com.tiantian.mybatis.mapper.BlogMapper.updateBlog", blog);
}
public SqlSessionTemplate getSqlSessionTemplate() {
return sqlSessionTemplate;
}
@Resource
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
}