@Transactional 注解哪些情况下会失效?

2020-06-14  

今天又研究了一道面试题,有关 @Transactional 注解失效的问题,这道题的综合性较强。

 

先说下我整理的答案:

 

1、@Transactional 作用在非 public 修饰的方法上

 

2、@Transactional 作用于接口,使用 CGLib 动态代理

 

3、@Transactional 注解属性 propagation 设置以下三种可能导致无法回滚

  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

 

4、同一类中加 @Transactional 方法被无 @Transactional 的方法调用,事务失效

 

5、@Transactional 方法内异常被捕获

 

6、默认 RuntimeException 和 Error 及子类抛出,会回滚;rollbackFor 指定的异常及子类发生才会回滚

 

7、数据库不支持事务,如 MySLQL 使用 MyISAM 存储引擎

 

8、Spring 的配置文件中未配置事务注解生效

<tx:annotation-driven transaction-manager="transactionManager"/>

 

9、Spring Boot 引入 jbdc 或 jpa 包,默认开启事务注解。若未引入这两个包,需要使用 @EnableTransactionManagement 进行配置

...

 

简单分析一下:

@Transactional 注解的作用就是保证方法内的多个数据库操作具有事务特性,即要么都成功,要么都失败。

失效的本质原因就在注解启用、事务管理器注入、手动 commit 与 rollback 的处理(Connection 的 autoCommit 设置为 false,然后在代码里手动 commit 或 rollback)这三个环节中。

 

比如我的博客网站 xml 配置文件中配置的事务管理器就是

org.springframework.jdbc.datasource.DataSourceTransactionManager

它继承自

org.springframework.transaction.support.AbstractPlatformTransactionManager

事务的具体控制就是 DataSourceTransactionManager 实现。

 

Spring 启动时解析 xml 配置,把 TransactionInterceptor 类型的 bean 注入到了 BeanFactoryTransactionAttributeSourceAdvisor 中,调用 TransactionInterceptor 的 invoke 方法完成整个事务的逻辑。

 
@Override
  public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
​
    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
      @Override
      public Object proceedWithInvocation() throws Throwable {
        return invocation.proceed();
      }
    });
  }

 

invokeWithinTransaction 方法就是对事务的处理。

 

@Transactional 仅作用于 public 方法的逻辑在 getTransactionAttribute 方法中

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
    }

 

@Transactional 的注解参数解析逻辑在 invokeWithinTransaction 方法里

//If the transaction attribute is null, the method is non-transactional.
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// First try is the method in the target class.
    TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
    if (txAtt != null) {
      return txAtt;
    }
​
    // Second try is the transaction attribute on the target class.
    txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAtt != null) {
      return txAtt;
    }
​
    if (specificMethod != method) {
      // Fallback is to look at the original method.
      txAtt = findTransactionAttribute(method);
      if (txAtt != null) {
        return txAtt;
      }
      // Last fallback is the class of the original method.
      return findTransactionAttribute(method.getDeclaringClass());
    }

 

事务信息通过 createTransactionIfNecessary 方法创建

// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

 

代码执行发生异常的处理逻辑是在 completeTransactionAfterThrowing 方法中

completeTransactionAfterThrowing(txInfo, ex);

 

根据注解参数 rollback

if (txInfo.transactionAttribute.rollbackOn(ex)) {
        try {
          txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
        }
        catch (TransactionSystemException ex2) {
          logger.error("Application exception overridden by rollback exception", ex);
          ex2.initApplicationException(ex);
          throw ex2;
        }
        catch (RuntimeException ex2) {
          logger.error("Application exception overridden by rollback exception", ex);
          throw ex2;
        }
        catch (Error err) {
          logger.error("Application exception overridden by rollback error", ex);
          throw err;
        }
      }
 

根据注解参数 commit

else {
        // We don't roll back on this exception.
        // Will still roll back if TransactionStatus.isRollbackOnly() is true.
        try {
          txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
        catch (TransactionSystemException ex2) {
          logger.error("Application exception overridden by commit exception", ex);
          ex2.initApplicationException(ex);
          throw ex2;
        }
        catch (RuntimeException ex2) {
          logger.error("Application exception overridden by commit exception", ex);
          throw ex2;
        }
        catch (Error err) {
          logger.error("Application exception overridden by commit error", ex);
          throw err;
        }
      }
    }

 

代码执行正常的事务提交在 commitTransactionAfterReturning 方法

commitTransactionAfterReturning(txInfo);

 

以上就是 Spring 对事务的处理流程。

 

ConstXiong 备案号:苏ICP备16009629号-3