Mybatis+Spring 时事务时交给谁处理的呢?
如果业务方法加上 @Transactional
时,是交给 Spring 处理。默认情况下,还是mybatis自己处理事务。
源码证明
在我们调用从 mybatis 代理出来的 mapper 接口时,其实调用 mybatis 的 MapperFactoryBean
对象,该对象是JVM代理对象,其InvocationHandler
实现在SqlSessionTemplate
中。
public class SqlSessionTemplate implements SqlSession, DisposableBean {
//...
/**
* Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
* unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to the
* {@code PersistenceExceptionTranslator}.
*/
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
// ******* 1
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
//即使在非dirty会话上,也会强制提交,因为一些数据库在调用close()之前需要提交/回滚
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
}
关键在于!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)
这句代码,调用的是 SqlSessionUtils # isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory)
方法:
/**
* Returns if the {@code SqlSession} passed as an argument is being managed by Spring
* 如果作为参数传递的SqlSession正在被Spring管理,则返回 true;否则返回false
* @param session
* a MyBatis SqlSession to check -- 要检查的 Mybatis SqlSession
* @param sessionFactory
* the SqlSessionFactory which the SqlSession was built with
* -- 构建 SqlSession 时使用的 SqlSessionFactory
* @return true if session is transactional, otherwise false
* -- 如果session是事务性的,则为true,否则为false
*/
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
return (holder != null) && (holder.getSqlSession() == session);
}
该方法会检查当前 SqlSession
是否被 Spring 管理,在执行被@Transactional
修复方法时,SqlSession
就会交给 Spring 管理事务。
当前SqlSession
被 Spring 管理时,mybatis 会做出让步,不在主动提交事务。