Spring的@Transactional失效场景和事务注解方法之间调用(二)

Spring的@Transactional失效场景和事务注解方法之间调用(二)

三 @Transactional注解的方法之间调用(同类和不同类之间)

针对methodA方法调用methodB,且methodB中抛出异常的情况:

同类调用 不同类调用
编号 methodA是否@Transactional methodB是否@Transactional 事务是否有一致性 编号 methodA是否@Transactional methodB是否@Transactional 事务是否有一致性
1 1 只有B回滚
2 2
3 3
4 有且requires_new 4 有且requires_new
5 有且catch 无/有 5 有且catch 操作失败且抛异常
6 6 有且catch 有且requires_new 只有B回滚

3.1 同类中调用

3.1.1 各种情况说明

1 如果A非事务方法,调用同类中的B是事务方法,那么此时B的@Transactional不生效,此时即便B抛异常,那么也不会回滚。此时不具有事务一致性(情况1);

2 对于@Transactional的A,无论B方法中,是否有注解、或者注解为requires-new,都会处于同一个事务中,具有事务一致性(情况2、3、4);

3 如果@T的A中调用B(无论是否@Tran),此时catch,那么此时由于事务不会抛异常,所以事务不会回滚。(想要保证事务有效,此时需要再catch中再次throw异常)

3.1.2 总结

根据同类中调用,@Transactional会失效的原理,以及解决方案,那么,针对同类中的方法调用,就总结出如下的情况:

@Transactional
methodA(){
this.methodB();
this.methodC();
//Service service= AopContext.currentProxy(); 
//service.methodB();
//service.methodC();
}

//@Transactional /@Transactional(Propagation.REQUIRES_NEW)情况---------两种注解都无效
methodB(){
  int i=1/0;//抛异常
}

//@Transactional /@Transactional(Propagation.REQUIRES_NEW)情况
methodC(){
}

1 如果只在methodA上注解,无论B和C是否加注解,那么B和C都处于当前事务中(在b抛出异常时,作为整体的事务,都会回滚);

2 在自我调用过程中,无论是否使用AopContext.currentProxy()注解调用,都处于同一个事务中(此时,使用AC.current.P调用,B和C上的注解也都是无效的);

3 如果想要B和C单独处于独立的事务中,那么需要将a方法注解取消,BC方法上以下两种情况,都会使得BC方法单独处于独立的事务中,各自保持各自事务的一致性;(注意:这里需要使用代理对象AopContext.currentProxy(),调用后续内部的方法,才能使事务生效)

methodA(){
Service service= AopContext.currentProxy();  //注意:此时仍旧需要使用代理对象(因为,如果A非事务,调用内部事务方法B,注解失效)
service.methodB();
service.methodC();
}

@Transactional /@Transactional(Propagation.REQUIRES_NEW)情况
methodB(){
  int i=1/0;//抛异常
}

@Transactional /@Transactional(Propagation.REQUIRES_NEW)情况
methodC(){
}

4 如果想要对单独的事务方法B和C,根据执行情况控制逻辑执行,可进行如下的开发:

methodA(){
Service service= AopContext.currentProxy();
  try{
    service.methodB();
  }catch(Exception){
    //该处逻辑可以自己定义,实现B和C两个单独的事务之间的逻辑
    return;
  }

  try{
    service.methodC();
  }catch(Exception){
    return;
  }
  
}

@Transactional /@Transactional(Propagation.REQUIRES_NEW)情况
methodB(){
  try{
      int i=1/0;//抛异常
  }catch(Exception){
    //注意:在事务方法中,try以内的部分不受事务控制,如果需要再抛出异常时,spring仍旧能够回滚,需要再catch中重新抛出异常
    //另外,在当前throw代码之前的部分,仍旧处于回滚事务的部分,在throw抛异常时,仍旧会回滚。所以catch中添加的操作是无效的
    throw new RuntimeException("xxx");
  }

}

@Transactional /@Transactional(Propagation.REQUIRES_NEW)情况
methodC(){
}

此时,因为methodA中方法不是事务,而对于B和C都是单独的事务,因此a方法中catch到异常时,不需要再抛异常了。如果c的执行需要根据b的执行情况,那么就用上面的逻辑:

如果B抛异常,那么直接在methodB事务方法中catch并重新throw异常,所以会回滚;然后,A方法会catch异常。此时可自定义逻辑,如果直接return,那么不会执methodC中操作;如果B成功,那么C正常执行。

3.2 不同类中调用

methodB中抛异常,被methodA调用时,

同类调用 不同类调用
编号 methodA是否@Transactional methodB是否@Transactional 事务是否有一致性 编号 methodA是否@Transactional methodB是否@Transactional 事务是否有一致性
1 1 只有B回滚
2 2
3 3
4 有且requires_new 4 有且requires_new
5 有且catch 无/有 5 有且catch 操作失败且抛异常
6 6 有且catch 有且requires_new 只有B回滚

3.2.1 各种情况说明

1 a无@T,b有@T,此时只有b是个单独的事务方法,会单独的回滚,a执行。

2 如果a是@Tran,调用b方法,无论b是否有@Trans,具有事务一致性。只不过:

在@Tran A--->@Tran B时,可以认为事务rollback了两次,此时b抛异常回滚,同时A也抛异常回滚(情况3);

此时回滚一次(情况2);

3 如果@Tran A--->@Tran B,且a中catch异常,那么操作不会成功(可以理解回滚),此外,还抛异常(情况5)

org.springframework.transaction.UnexpectedRollbackException: Transaction rollback because it has benn marked as rollback-only

由于两者同处于一个事务,b抛异常,调用事务的rollback,并且标记为rollback-only;而在a方法中catch异常,这时候没有抛出异常,spring认为方法a需要commit,调用提交方法,而b中已经标记事务只能回滚,此时就会直接抛异常了,方法的执行失败。(此时事务的执行不会成功,操作失败)

针对此种情况,需要再a方法中,在catch中重新throw异常。

4 a中catch,b中设置事务的传播级别为REQUIRES_NEW。此时新添加的事务,会作为新的事务执行。

@Service()
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 数据库操作A(增,删,该)
        try {
            bClass.bFunction();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

@Service()
public class BClass {

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void bFunction() {
        //todo: 数据库操作A(增,删,该)
        throw new RuntimeException("函数执行有异常!");
    }
}

此时,b方法回滚,a操作执行。(情况6)

posted @ 2023-06-12 18:26  LeasonXue  阅读(1182)  评论(0)    收藏  举报