从TransactionTemplate入手Spring事务的原理

TransactionTemplate源码

TransactionTemplate是事务处理的模板类简化了事务的使用,核心方法为execute,传入一个TransactionCallback来执行事务代码。

	public <T> T execute(TransactionCallback<T> action) throws TransactionException {
		Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

		if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager cpptm) {
			return cpptm.execute(this, action);
		}
		else {
			//获取/创建事务
			TransactionStatus status = this.transactionManager.getTransaction(this);
			T result;
			try {
				//执行业务逻辑
				result = action.doInTransaction(status);
			}
			catch (RuntimeException | Error ex) {
				// Transactional code threw application exception -> rollback
				//回滚事务
				rollbackOnException(status, ex);
				throw ex;
			}
			catch (Throwable ex) {
				// Transactional code threw unexpected exception -> rollback
				//回滚事务
				rollbackOnException(status, ex);
				throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
			}
			//提交事务
			this.transactionManager.commit(status);
			return result;
		}
	}

由以上代码可知,TransactionTemplate通过TransactionManager来操作事务。

事务相关的类

TransactionManager

TransactionManager事务管理器,通过该类来对事务执行操作。
它的子类是PlatformTransactionManager和ReactiveTransactionManager
TransactionTemplate中使用的是PlatformTransactionManager
image

getTransaction方法

getTransaction方法开启了事务
本质上来说一个事务的伪代码如下:

connection.setAutoCommit(false);
try {
	//执行sql
connection.commit();
} catch(Exception ex) {
connection.rollback();
}

一个事务经过以下阶段:

  1. 获取连接
  2. 准备连接,设置autoCommit为false、设置只读
  3. 绑定到线程变量中
  4. 激活同步,同步即为事务的触发器
  5. 使用connection
  6. 提交/回滚,执行钩子函数

commit/rollback方法

执行connection.commit或者rollback方法,并执行触发器
代码简化为:

	prepareForCommit(status);
	triggerBeforeCommit(status);
	triggerBeforeCompletion(status);
	doCommit();
	triggerAfterCommit(status);
	triggerAfterCompletion();

TransactionSynchronizationManager

TransactionSynchronizationManager拥有一系列的线程变量

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");

	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");

	private static final ThreadLocal<String> currentTransactionName =
			new NamedThreadLocal<>("Current transaction name");

	private static final ThreadLocal<Boolean> currentTransactionReadOnly =
			new NamedThreadLocal<>("Current transaction read-only status");

	private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
			new NamedThreadLocal<>("Current transaction isolation level");

	private static final ThreadLocal<Boolean> actualTransactionActive =
			new NamedThreadLocal<>("Actual transaction active");

resources开启事务后,保存connection、sqlSession(Mybatis)等变量,
synchronizations保存了事务的同步回调,触发器保存在这个变量中

事务事件监听器

可以使用@TransactionalEventListener来向Spring容器注册一个事务监听器,注解的phase属性指定了回调的位置:
例子:

@Component
public class DataLedgerListener {

    @Resource
    private DataLedgerService dataLedgerService;

    /**
     * 监听台账编辑事件
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleDataLedgerUpdate(DataLedgerUpdateEvent event) {
        dataLedgerService.ledgerUpdateData(event.getDataLedgerId());
    }
}

原理解析

@TransactionalEventListener本质是一个@EventListener,由EventListenerMethodProcessor处理。
EventListenerMethodProcessor扫描到带@EventListener的方法后,调用EventListenerFactory来创建一个ApplicationListener,并调用ConfigurableApplicationContext.addApplicationListener()来注册一个ApplicationListener。
@TransactionalEventListener由TransactionalEventListenerFactory创建一个TransactionalApplicationListenerMethodAdapter监听器。

TransactionalApplicationListenerMethodAdapter在监听到事件后,向TransactionSynchronizationManager的synchronizations变量中添加一个synchronization。
源码:

	public void onApplicationEvent(ApplicationEvent event) {
		if (TransactionalApplicationListenerSynchronization.register(event, this, this.callbacks)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Registered transaction synchronization for " + event);
			}
		}
		else if (this.fallbackExecution) {
			if (getTransactionPhase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {
				logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
			}
			processEvent(event);
		}
		else {
			// No transactional event execution at all
			if (logger.isDebugEnabled()) {
				logger.debug("No transaction is active - skipping " + event);
			}
		}
	}

随后在TransactionManager的commit方法中执行这些回调函数。

事务的使用

MyBatis对事务的使用

MyBatis对事务的封装为Transaction类
image
MyBatis的Executor通过Transaction.getConnection()方法获得数据库连接。
在Spring中使用mybatis时,Transaction的实现类一般使用SpringManagedTransaction,
getConnection方法的实现最终调用的是
DataSourceUtils.getConnection() 方法
image
对于MyBatis来说,什么时候执行事务的commit呢?

在Spring项目中不直接使用SqlSession,而是通过SqlSessionTemplate
SqlSessionTemplate通过代理sqlSession来执行commit操作。

代理逻辑:

  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	  //首先从事务的线程变量中获取sqlSession,无法获取才会进行创建,并存入线程变量中
      var sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        var result = method.invoke(sqlSession, args);
		//判断sqlSession是否由Spring事务管理
        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) {
        var 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) {
		//首先判断是否由Spring事务管理,是的话不执行管理,不是的话执行sqlSession.close()操作
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

posted @ 2025-11-27 16:09  Hekk丶  阅读(1)  评论(0)    收藏  举报