1. 程式人生 > >Spring Transaction註解屬性

Spring Transaction註解屬性

轉:https://blog.csdn.net/kiwi_coder/article/details/20214939

 

Spring Transaction中有一個很重要的屬性:Propagation。主要用來配置當前需要執行的方法,與當前是否有transaction之間的關係。

我曉得有點兒抽象,這也是為什麼我想要寫這篇部落格的原因。看了後面的例子,大家應該就明白了。

一、Propagation取值:

REQUIRED(預設值):在有transaction狀態下執行;如當前沒有transaction,則建立新的transaction;

SUPPORTS:如當前有transaction,則在transaction狀態下執行;如果當前沒有transaction,在無transaction狀態下執行;

MANDATORY:必須在有transaction狀態下執行,如果當前沒有transaction,則丟擲異常IllegalTransactionStateException;

REQUIRES_NEW:建立新的transaction並執行;如果當前已有transaction,則將當前transaction掛起;

NOT_SUPPORTED:在無transaction狀態下執行;如果當前已有transaction,則將當前transaction掛起;

NEVER:在無transaction狀態下執行;如果當前已有transaction,則丟擲異常IllegalTransactionStateException。

二、REQUIRED與REQUIRED_NEW

上面描述的6種propagation屬性配置中,最難以理解,並且容易在transaction設計時出現問題的是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是否成立,還需要再多多考慮你的業務需求。

Transaction不是一個新東西了,那對於transaction的使用會不會有一些模式?一些經驗之談呢?答案肯定是有的,以後部落格再說。

參考資料:Spring Transaction Propagation