Spring 事务回滚

Spring事务

基本知识

配置@Transactional注解的方法会被Spring AOP拦截,执行TransactionInterceptor的invoke方法,它是一个around advice。在执行目标方法之前创建事务,事务信息被记录在TransactionInfo对象中,TransactionInfo创建完毕后,被存储在线程变量中,如下面代码,

  1. private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
  2. new NamedThreadLocal<TransactionInfo>("Current aspect-driven transaction");
  3. private void bindToThread() {
  4. // Expose current TransactionStatus, preserving any existing TransactionStatus
  5. // for restoration after this transaction is complete.
  6. this.oldTransactionInfo = transactionInfoHolder.get();
  7. transactionInfoHolder.set(this);
  8. }

取出后赋值给oldTransactionInfo,然后将当前TransactionInfo设置进去。

初始化TransactionInfo后,执行目标方法,如果抛出异常,则执行事务的异常处理,否则提交事务,在提交事务之前或者回滚事务之后,transactionInfoHolder将线程变量的值设置成oldTransactionInfo。具体看下面的代码:

  1. private void restoreThreadLocalStatus() {
  2. // Use stack to restore old transaction TransactionInfo.
  3. // Will be null if none was set.
  4. transactionInfoHolder.set(this.oldTransactionInfo);
  5. }

整个事务处理流程在下面的方法中,

  1. protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
  2. throws Throwable {
  3. //初始化事务属性
  4. final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
  5. //初始化事务管理器
  6. final PlatformTransactionManager tm = determineTransactionManager(txAttr);
  7. final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
  8. if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
  9. //开启事务
  10. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
  11. Object retVal = null;
  12. try {
  13. retVal = invocation.proceedWithInvocation();
  14. }catch (Throwable ex) {
  15. //处理异常事务
  16. completeTransactionAfterThrowing(txInfo, ex);
  17. throw ex;
  18. } finally {
  19. cleanupTransactionInfo(txInfo);
  20. }
  21. //提交事务
  22. commitTransactionAfterReturning(txInfo);
  23. return retVal;
  24. }

下面看一下事务的创建、回滚和提交三个动作

开启事务

在开启事务之前,会先初始化事务属性,信息定义在TransactionDefinition中,包括隔离级别、传播行为、超时和只读。然后创建事务管理器,Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现,事务管理器缓存在transactionManagerCache对象中。

事务 说明
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或MyBatis进行持久化数据时使用
org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
org.springframework.orm.Hibernate3.HibernateTransactionManager 使用Hibernate3.0版本进行持久化数据时使用

接下来就是创建事务对象TransactionInfo,DataSourceTransactionObject,然后从连接池中获取一个Connection,设置隔离级别,是否自动提交等,最后将Connection设置到事务对象中并开启事务。如果已经开启事务的话,就会复用DataSourceTransactionObject对象。

事务回滚

业务代码中如果抛出异常,会调用completeTransactionAfterThrowing,Spring会检查抛出的异常是否满足回滚的条件,在事务初始化时在TransactionDefinition中初始化抛出哪些异常要回滚,如果没有定义,默认会对RuntimeException和Error的异常回滚。如果满足回滚的条件,调用PlatformTransactionManager的rollback方法,如果该事务是一个新的事务,那么调用Connection的rollback接口。如果是参与的是其它事务,那么将事务对象设置成setRollbackOnly,具体如下所示:

  1. protected void doSetRollbackOnly(DefaultTransactionStatus status) {
  2. DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
  3. txObject.setRollbackOnly();
  4. }

回滚完成后,将oldTransactionInfo设置到线程变量中。

事务提交

事务提交直接调用PlatformTransactionManager的commit方法,在commit方法中,会检查是否需要回滚。如果都不满足的话,那么就调用Connection的commit方法,参与到其它事务,不会回滚。

问题总结

参考文献

【1】https://github.com/willdonggg/tech-blog/blob/master/Transaction rolled back because it has been marked as rollback-only.md
【2】https://blog.csdn.net/xieyuooo/article/details/8269218