Spring-奇怪的回滚

上周我遇到了一个奇怪的问题,异常被try住的状况下,事务仍然回滚了。

public class ServiceA{
    public void methodA(){
        ...; // 执行插入
        try{
            serviceB.methodB(); // 这一行抛了异常
        } catch (Exception e){}
    }
}

public class ServiceB{
   public void methodB(){
      ...;// 执行插入
   } 
}复制代码

ServiceA与ServiceB都由Spring来管理事务,ServiceB在ServiceA的方法中被调用,并且ServiceB周围有个try块。java

通常来说,顺着去理解,既然异常被try住了,那么就不会引发事务回滚。但抛异常的时候仍是义无反顾的回滚。bash

拔屌无情。spa


ServiceB周围若是没有那个try块,回滚就很好理解了。因此为何呢?代理

只能回头去查配置。两个类、事务、嵌套…… ……唔,多半和这个有关code

<tx:method name="*" propagation="REQUIRED" />复制代码

propagation,此参名为事务传播方式,除“REQUIRED”外,还存在“REQUIRES_NEW”等,共7种传播方式。其它的不提,由于本次问题只与这两种方式有关。cdn

“REQUIRED”:若是当前没有事务,就新建一个事务,若是已经存在一个事务中,加入到这个事务中。
xml

“REQUIRES_NEW”:新建事务,若是当前存在事务,把当前事务挂起。
blog


那末,就是由于配置了“REQUIRED”的关系,致使ServiceA与ServiceB共享一个事务,一块儿提交,一块儿回滚,因此当ServiceB抛异常时,即使被try住,仍是带着ServiceA一块儿回滚了。当我把配置改为“REQUIRES_NEW”,ServiceB与ServiceA就各玩各的。ServiceB的异常被try住后,ServiceA的操做仍是能够继续执行,提交事务。
事务

ok,问题解决了。string


可,为何呢?

Spring是怎么管理事务的?

 AOP


那这个东西说的简单点,就是Spring会为每个它管理的类,都生成一个代理类,而且对外只提供代理类,操做也都靠代理类来完成。

这是原来的

public class ServiceB{
   public void methodB(){
      ...;// 执行插入
   } 
}复制代码

代理完后基本上变成这样

public class ServiceBProxy{
    private ServiceB serviceB;
    
    public void methodBProxy(){
        try{
            beginTransaction();

            serviceB.methodB();

            commit();
        }catch(Exception e){
            rollback();
        }
    }
}复制代码

那么整个流程的伪码,最后是否是就长这样(固然我这个伪码仍是很伪的,并且有一部分猜想在里面)


因此当事务传播方式为“REQUIRED”时,serviceA内写try块也没用,serviceB产生的异常,已经先一步被serviceB代理类的try块捕获,致使事务回滚。

其实到这一步,再动动脑子就发现了,改一下try块的粒度,就能够作到在“REQUIRED”条件下,即使抛异常也提交事务。


在serviceB方法内部加try块的话,异常直接捕获,不会逃离到serviceB代理类的try块里,代码继续执行,所以事务也不会提交。

但走这种邪门歪路仍是有风险的,老老实实新开一个事务比较好。

相关文章
相关标签/搜索