spring3的propagation的取值REQUIRED與REQUIRED_NEW的區別

spring3的propagation的取值REQUIRED與REQUIRED_NEW的區別


spring 事務的傳播行爲中,有兩個容易混淆的行爲:REQUIRED和REQURED_NEW。當程序在某些情況下拋出異常時,如果對於這兩者不夠了解,就可能很難發現而且解決問題。

下面我們給出三個場景進行分析:

場景一:

ServiceA.java:

public class ServiceA {
    @Transactional
    public void callB() {
        serviceB.doSomething();
    }
}

ServiceB.java

public class ServiceB {
    @Transactional
    public void doSomething() {
        throw new RuntimeException("B throw exception");
    }
}


這種情況下,我們只需要在調用ServiceA.callB時捕獲ServiceB中拋出的運行時異常,則transaction就會正常的rollback。


場景二

在保持場景一中ServiceB不變,在ServiceA中調用ServiceB的doSomething時去捕獲這個異常,如下:

public class ServiceA {
    @Transactional
    public void callB() {
        try {
            serviceB.doSomething();
        } catch (RuntimeException e) {
            System.err.println(e.getMessage());
        }
    }
}


這個時候,我們再調用ServiceA的callB。程序會拋出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only這樣一個異常信息。原因是什麼呢?

因爲在ServiceA和ServiceB中的@Transactional propagation都採用的默認值:REQUREID。根據我們前面講過的REQUIRED特性,當ServiceA調用ServiceB的時候,他們是處於同一個transaction中。如下圖所示:


當ServiceB中拋出了一個異常以後,ServiceB會把當前的transaction標記爲需要rollback。但是ServiceA中捕獲了這個異常,並進行了處理,認爲當前transaction應該正常commit。此時就出現了前後不一致,也就是因爲這樣,拋出了前面的UnexpectedRollbackException。


場景三

在保持場景二中ServiceA不變,修改ServiceB中方法的propagation配置爲REQUIRES_NEW,如下:

public class ServiceB {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doSomething() {
        throw new RuntimeException("B throw exception");
    }
}


此時,程序可以正常的退出了,也沒有拋出UnexpectedRollbackException。原因是因爲當ServiceA調用ServiceB時,serviceB的doSomething是在一個新的transaction中執行的。如下圖所示:


所以,當doSomething拋出異常以後,僅僅是把新創建的transaction rollback了,而不會影響到ServiceA的transaction。ServiceA就可以正常的進行commit。

當然這裏把ServiceA和ServiceB放在兩個獨立的transaction是否成立,還需要再多多考慮你的業務需求.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章