系列文章
spring 事务源码(三)如何保证被@Transactional标记方法中的所有sql都在一个事务内
核心问题
spring 如何保证被事务注解标记方法中的所有sql都在一个事务内
测试代码
还是之前的代码,但是这次得改下,想办法让sql抛出异常
<select id="getNum" resultType="java.lang.Integer">
select count(*)
from ss
where 1 = 1;
</select>
@Transactional
public void test(){
courseItemService.ss();
}
public void ss(){
baseMapper.getNum();
}
运行项目可以看到如下异常堆栈
最开始调用myabtis的类开始网上查就能查到
源码
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取sqlsession
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// 如果当前线程上下文没有事务,则自动提交
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// 关闭链接
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
.....
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
org.mybatis.spring.SqlSessionUtils#getSqlSession(.....)
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
// 获取当前线程中的sqlsession持有对象
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 获取sqlsession
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 如果sqlsession不存在则创建
session = sessionFactory.openSession(executorType);
// 注册sqlsession 到当前线程上下文
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
org.mybatis.spring.SqlSessionUtils#registerSessionHolder
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
// 如果当前线程上下文有正在运行的事务
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
// 将SqlSessionHolder 绑定到当前线程
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager
.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
.........
}
} else {
.....
}
}
总结
原理其实很简单,就是在当前线程上下文中放置一个sqlsession对象,方法中任何一个sql执行先去判断是否存在,如果存在则用存在的,否则去创建一个新的