事务@Transactional为什么会无效?

目录
文章目录隐藏
  1. 1. 非公共物质的方法
  2. 2.try/catch 导致事务无效
  3. 3.调用类内用的@Transactional 方法
  4. 总结

导致 @Transactional 无效的常见情况有以下 5 个:

  1. 非公共物质的方法;
  2. timeout 初始化时间设置过小;
  3. 代码中使用 try/catch 处理异常;
  4. 调用类内部的@Transactional 方法;
  5. 不支持数据库事务。

很多人只知道答案但不知道原因,这就像只谈恋爱不结婚一样,是不能同意接受的,所以本篇我们就来讨论一下,导致事务无效的背后原因到底是什么?

在 5 种场景中,第 2 种情况(数据库讨论时间设置过小)和第 5 种不支持事务),我们这里不再赘述了,这里重点介绍我们来其他 3 种情况。

1. 非公共物质的方法

非公共事务的方法上,即使加了事务@Transactional仍然不会生效动态,原因是因为@Transactional使用是 Spring AOP 实现的,而 Spring AOP 是通过代理的,而@Transactional在生成代理时会判断,如果方法为物质的方法生成代理对象,则这样也不会公开自动执行事务了,它的部分源如下:

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
   // Don't allow no-public methods as required.
   // 非 public 方法,设置为 null
   if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
   }
   // 后面代码省略....
 }

2.try/catch 导致事务无效

@Transactional 执行流程是:@Transactional 会在方法执行前,会自动开启事务;在方法成功执行完成,会自动提交事务;如果方法在执行过程中,出现异常,那么它会自动回滚事务。

然而在方法中自己不会尝试追赶之后,事务就不会滚了,怎么会呢?造成这个问题的原因和自动回复的原因有@Transactional的原因,它的原因是:

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
      throws Throwable {
   // If the transaction attribute is null, the method is non-transactional.
   final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass);

   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
       // Standard transaction demarcation with getTransaction and commit/rollback calls.
       // 自动开启事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
         // This is an around advice: Invoke the next interceptor in the chain.
         // This will normally result in a target object being invoked.
         // 反射调用业务方法
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
          // target invocation exception
          // 异常时,在 catch 逻辑中,自动回滚事务
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
       // 自动提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }

   else {
     // .....
   }
}

我们可以通过 Transactional 从当执行的方法中出现异常,而@Transactional却出现了异常,然后再执行事务,然后当开发者自行执行事务处理之后,@Transactional 就在上面发现/发现异常了,那样就不会触发事务的自动回滚了,这就是为什么当@Transactional 发生 try/catch 之后就不会自动回滚(事务)的原因。

3.调用类内用的@Transactional 方法

当调用代码类内部的 @Transactional 物质的方法时,事务也不会生效,如下所示:

@RequestMapping("/save")
public int saveMappping(UserInfo userInfo) {
    return save(userInfo);
}
@Transactional
public int save(UserInfo userInfo) {
    // 非空效验
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    int result = userService.save(userInfo);
    int num = 10 / 0; // 此处设置一个异常
    return result;
}

上面的代码在添加用户之后发生了异常,程序也没有执行回滚,这是因为@Transactional是基于 Spring AOP 实现的,而 Spring AOP 又是基于动态代理实现的,而当调用类内部的方法时,不是通过对象完成的,而是通过对象实现的,这样事务就成为了代理对象,理所当然的就生效了。

总结

非公共事务的方法在@Transactional实现时因为没有做证明,如果是非公共的不会生成代理对象,所以就无效了;而调用类内部的@Transactional对象的方法时,也是成功调用代理对象,是调用调用方法,所以事务处理也可以这样,因为遇到开发者自定义的尝试进行事务处理;@Transaction 没有通过,但是不会通过/异常滚到的自动处理如果自定义用户了try/catch@Transactional就这样异常,所以不会自动回滚事务了。

「点点赞赏,手留余香」

1

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » 事务@Transactional为什么会无效?

发表回复