详解spring事务失效和回滚失败的场景总结

本文详细介绍了Spring事务失效的几种常见场景,包括访问权限问题、final修饰方法、内部调用、未被Spring管理、多线程、myisam表不支持事务、未开启事务以及事务不回滚的原因,如传播特性错误、异常处理和自定义回滚异常。

详解spring事务失效和回滚失败的场景

参考:https://blog.youkuaiyun.com/A_Java_Dog/article/details/120429840

前言

在某些业务场景下,如果一个请求中,需要同时写入多张表的数据或者执行多条sql。为了保证操作的原子性(要么同时成功,要么同时失败),避免数据不一致的情况,我们一般都会用到spring事务。

在以下文章中,我将会逐一介绍各种事务失效的情况
在这里插入图片描述

一、 事务不生效

访问权限问题

众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。

但如果我们在开发过程中,把有某些事务方法,定义了错误的访问权限,就会导致事务功能出问题,例如:

@Service
public class UserService {
   
   
    
    @Transactional
    private void add(UserModel userModel) {
   
   
         saveData(userModel);
         updateData(userModel);
    }
}

我们可以看到add方法的访问权限被定义成了 private,这样会导致事务失效,spring要求被代理`方法必须得是public的。

说白了,在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   
   
    // Don't allow no-public methods as required.可以看到, 这里不支持public类型的方法
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
   
   
      return null;
    }

    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    // First try is the method in the target class.
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
   
   
      return txAttr;
    }

    // Second try is the transaction attribute on the target class.
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
   
   
      return txAttr;
    }

    if (specificMethod != method) {
   
   
      // Fallback is to look at the original method.
      txAttr = findTransactionAttribute(method);
      if (txAttr != null) {
   
   
        return txAttr;
      }
      // Last fallback is the class of the original method.
      txAttr = findTransactionAttribute(method.getDeclaringClass());
      if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
   
   
        return txAttr;
      }
    }
    return null;
  }

也就是说,如果我们自定义的事务方法(即目标方法),它的访问权限不是public,而是private、default或protected的话,spring则不会提供事务功能。

方法用final修饰

有时候,某个方法不想被子类重新,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final,例如:

@Service
public class UserService {
   
   

    @Transactional
    public final void add(UserModel userModel){
   
   
        saveData(userModel);
        updateData(userModel);
    }
}

我们可以看到add方法被定义成了final的,这样会导致事务失效。

为什么?

如果你看过spring事务的源码,可能会知道spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。

但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。

注意:如果某个方法是static的,同样无法通过动态代理,变成事务方法。

方法的内部调用

假设:被外部调用的公共方法 A 有两个进行了数据操作的子方法 B 和子方法 C 的事务注解说明:

  • 被外部调

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值