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塊裏,代碼繼續執行,所以事務也不會提交。

但走這種邪門歪路仍是有風險的,老老實實新開一個事務比較好。

相關文章
相關標籤/搜索