Spring嵌套事务之同一个类中,一个方法调用另外一个有事务的方法

    科技2022-07-13  131

    前段时间,朋友问了我一个问题,说有一个service类中,有一个A()方法和B()方法, A()方法没有添加事 务,B()方法添加了一个默认的事务,A()方法中调用B()方法,如果B()方法抛出异常,那么A()方法B()方法是否会回滚? 今天就回顾一下这个问题,看看到底如何?回顾这个问题之前,需要先了解下事务的传播行为,事务的传播 行为共7种如下: PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务,最常见的选择。 PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。 PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。 PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起, 两个事务之间没有关系。 PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。 PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 Spring默认事务传播行为:PROPAGATION_REQUIRED @Service public class TUserServiceImpl implements TUserService { @Autowired private TUserMapper tUserMapper; @Override public void addTuser() { TUser user = new TUser(); user.setName("司总"); user.setAge(23); tUserMapper.addTuser(user); updateTuser(); } @Transactional @Override public void updateTuser() { TUser user = new TUser(); user.setId(1); user.setName("张三他王大爷"); user.setAge(66); tUserMapper.updateTuser(user); throw new RuntimeException(); } } 如果直接调用updateTuser()方法,因为updateTuser()方法开启了事务,抛出异常事务会进行回滚。 但是直接调用addTuser()方法,updateTuser()方法中抛出异常,addTuser()方法与updateTuser() 方法并没有回滚?why?是不是一脸黑人问号? 其实这个原因如下: spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean 动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调 用时,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法 是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所 以就不会启动transaction,我们看到的现象就是@Transactional注解无效。 @Service class A{ @Transactinal method b(){...} method a(){ //标记1 b(); } } //Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法: class proxy$A{ A objectA = new A(); method b(){ //标记2 startTransaction(); objectA.b(); } method a(){ //标记3 objectA.a(); //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法 } } 当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代 码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了(标记 1)。由此可见,(标记2)并没有被执行到,所以startTransaction()方法也没有运行。

    附带案例

    @Transactional @Override public void addTuser() { TUser user = new TUser(); user.setName("司总"); user.setAge(23); tUserMapper.addTuser(user); updateTuser(); } @Override public void updateTuser() { TUser user = new TUser(); user.setId(1); user.setName("张三他王大爷"); user.setAge(66); tUserMapper.updateTuser(user); throw new RuntimeException(); } addTuser()方法存在事务,updateTuser()方法不存在事务,这种方式调用addTuser()方法抛出异常会进行 回滚操作。原因想必大家都很清楚了,如果不清楚的我再解释解释: addTuser()方法存在事务注解,调用addTuser()方法其实是通过代理类来调用的,会在代理类方法中开启 startTransaction() 然后再调用被代理类的addTuser()方法,addTuser()方法又调用了updateTuser()方 法所以它们是在同一个事务中的,抛出异常肯定会进行回滚。看下面这个代码图(借鉴了上面案例,重新修改了下) @Service class A{ @Transactinal method addTuser(){ updateTuser(); } method updateTuser(){ } } Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法: class proxy$A{ A objectA = new A(); method addTuser(){ startTransaction(); objectA.addTuser(); } method updateTuser(){ objectA.updateTuser(); } }

     

    Processed: 0.009, SQL: 8