事務的7種傳播行為


轉載:https://blog.csdn.net/soonfly/article/details/70305683

事務傳播行為

事務傳播行為指的是當一個事務方法被另一個事務方法調用時,這個方法是怎么運行的。
舉例說明:有兩個事務方法,一個是方法A,一個是方法B,方法A中調用了方法B,那方法B是開啟一個事務運行還是在方法A中的事務中運行,是由方法B的事務傳播行為控制的。
在Spring中定義了7中事務傳播行為:

傳播行為 含義
PROPAGATION_REQUIRED 表示當前方法必須運行在事務中,如果當前事務存在,方法將會在該事務中運行,否則,會啟動一個新的事務
PROPAGATION_SUPPORTS 表示該方法不需要事務上下文,但是如果存在當前的事務的話,那么該方法會在這個事務匯總運行
PROPAGATION_MANDATORY 表示該方法必須在事務中運行,如果當前事務不存在,則會拋出一個異常
PROPAGATION_REQUIRED_NEW 表示當前方法必須運行在它自己的事務中。一個新的事務將被啟動,如果存在當前事務,在該方法執行期間,當期事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
PROPAGATION_NOT_SUPPORTED 表示當前方法不應該運行在事務中,如果存在當前事務,在該方法運行期間,當前事務將被掛起,如果使用JTATransactionManager的話,則需要訪問TransactionManager
PROPAGATION_NEVER 表示當前方法不應該運行在事務上下文中,如果當前正有一個事務在運行,則拋出異常
PROPAGATION_NESTED 表示如果當前已經存在一個事務,那么該方法將會在嵌套事務中運行。嵌套的事務可以獨立於當前事務進行單獨的提交或回滾。如果當前事務不存在,那么其行為和 PROPAGATION_REQUIRED一樣。注意各廠商對這種傳播行為的支持是有所差異的,可以參考資源管理器的文檔來確認它們是夠支持嵌套事務

1、PROPAGATION_REQUIRED

存在事務就支持該事務,不存在則開啟一個事務
image
演示代碼:

@Component
public class Transaction {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
        methodB();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范閑',now())");
        // 此處拋出異常,方法A和方法B的操作都將回滾
        int i = 100 / 0;
    }
}

單獨調用方法B,因為上下文不存在事務,所以會開啟一個事務
當調用方法A時,因為上下文不存在事務,所以會開啟一個事務,當執行到方法B時,方法B發現存在一個事務,方法B就不會開啟新的事務,而是在方法A中的事務中執行。

2、PROPAGATION_SUPPORTS

如果存在事務則支持當前事務,如果不存在事務則無事務運行
演示代碼:

@Component
public class Transaction {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
        methodB();
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范閑',now())");
        // 存在事務回滾操作,不存在事務則不回滾
        int i = 100 / 0;
    }
}

單獨調用方法B,無事務運行
調用方法A,方法B加入方法A的事務,事務的運行

3、PROPAGATION_MANDATORY

如果存在一個事務,則事務的運行,沒有事務則拋出異常

@Component
public class Transaction {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
        methodB();
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范閑',now())");
    }
}

直接調用方法B,因為沒有事務所有會拋出異常
No existing transaction found for transaction marked with propagation 'mandatory'
調用方法A,方法B會加入到方法A的事務中去

4、PROPAGATION_REQUIRED_NEW

image
使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。
會開啟一個事務,如果存在一個事務,則會把存在的事務掛起

@Component
public class Transaction {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        doSomeTingA();
        methodB();
        doSomeTingB();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范閑',now())");
    }

    public void doSomeTingA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");

    }

    public void doSomeTingB() {
        jdbcTemplate.update("insert into actor value (21,'范思轍',now())");
    }
}

當調用

main(){
    methodA();
}

相當於調用

main(){
    TransactionManager tm = null;
    try{
        //獲得一個JTA事務管理器
        tm = getTransactionManager();
        tm.begin();//開啟一個新的事務
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//掛起當前事務
        try{
            tm.begin();//重新開啟第二個事務
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//提交第二個事務
        } Catch(RunTimeException ex) {
            ts2.rollback();//回滾第二個事務
        } finally {
            //釋放資源
        }
        //methodB執行完后,恢復第一個事務
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//提交第一個事務
    } catch(RunTimeException ex) {
        ts1.rollback();//回滾第一個事務
    } finally {
        //釋放資源
    }
}

在這里,把ts1稱為外層事務,ts2稱為內層事務。從上面的代碼可以看出,ts2與ts1是兩個獨立的事務,互不相干。Ts2是否成功並不依賴於 ts1。如果methodA方法在調用methodB方法后的doSomeThingB方法失敗了,而methodB方法所做的結果依然被提交。而除了 methodB之外的其它代碼導致的結果卻被回滾了

5、PROPAGATION_NOT_SUPPORTED

PROPAGATION_NOT_SUPPORTED總是非事務的運行,並且掛起任何存在的事務,使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。
image

6、PROPAGATION_NEVER

總是非事務的運行,如果存在一個活動的事務,則拋出異常

7、PROPAGATION_NESTED

image
如果一個活動的事務存在,則運行在一個嵌套事務中,如果沒有活動事務,則按照PROPAGATION_REQUIRED屬性執行
這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager作為事務管理器。
需要JDBC 驅動的java.sql.Savepoint類。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true(屬性值默認為false)。

@Component
public class Transaction {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        doSomeTingA();
        methodB();
        doSomeTingB();
    }

    @Transactional(propagation = Propagation.NESTED)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范閑',now())");
    }

    public void doSomeTingA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");

    }

    public void doSomeTingB() {
        jdbcTemplate.update("insert into actor value (21,'范思轍',now())");
    }
}

單獨執行方法B,按照PROPAGATION_REQUIRED屬性執行
如果調用方法A,相當於如下效果:

main(){
    Connection con = null;
    Savepoint savepoint = null;
    try{
        con = getConnection();
        con.setAutoCommit(false);
        doSomeThingA();
        savepoint = con2.setSavepoint();
        try{
            methodB();
        } catch(RuntimeException ex) {
            con.rollback(savepoint);
        } finally {
            //釋放資源
        }
        doSomeThingB();
        con.commit();
    } catch(RuntimeException ex) {
        con.rollback();
    } finally {
        //釋放資源
    }
}

當methodB方法調用之前,調用setSavepoint方法,保存當前的狀態到savepoint。如果methodB方法調用失敗,則恢復到之前保存的狀態。但是需要注意的是,這時的事務並沒有進行提交,如果后續的代碼(doSomeThingB()方法)調用失敗,則回滾包括methodB方法的所有操作。嵌套事務一個非常重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗並不會引起外層事務的回滾。

PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:

它們非常類似,都像一個嵌套事務,如果不存在一個活動的事務,都會開啟一個新的事務。
使用 PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務一樣,一旦內層事務進行了提交后,外層事務不能對其進行回滾。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它需要JTA事務管理器的支持。

使用PROPAGATION_NESTED時,外層事務的回滾可以引起內層事務的回滾。而內層事務的異常並不會導致外層事務的回滾,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時,需要JDBC 3.0以上驅動及1.4以上的JDK版本支持。其它的JTATrasactionManager實現可能有不同的支持方式。

PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴於環境的 “內部” 事務. 這個事務將被完全 commited 或 rolled back 而不依賴於外部事務, 它擁有自己的隔離范圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。

另一方面, PROPAGATION_NESTED 開始一個 “嵌套的” 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交。

由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 完全是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 嵌套事務也會被 commit, 這個規則同樣適用於 roll back.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM