事務屬性的7種傳播行為


事務屬性的7種傳播行為

編程式事務和聲明式事務

  • 編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,Spring推薦使用TransactionTemplate。(參考四)
    /**
     * 數據源加入事務管理
     * @param masterDataSource
     * @return
     */
    @Bean(name = "transactionManager")
    @Primary
    public DataSourceTransactionManager masterDataSourceTransactionManager(@Qualifier("masterDataSource") DataSource masterDataSource){
        final DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(masterDataSource);
        return dataSourceTransactionManager;
    }

 /**
     * 事務管理加入事務模板
     * @param transactionManager
     * @return
     */
    @Bean(name = "transactionTemplate")
    @Primary
    public TransactionTemplate masteerTransactionTemplate(@Qualifier("transactionManager") DataSourceTransactionManager transactionManager){
        final TransactionTemplate transactionTemplate = new TransactionTemplate();
        transactionTemplate.setTransactionManager(transactionManager);
        transactionTemplate.setIsolationLevelName("ISOLATION_DEFAULT");
        // 事物的傳播屬性
        transactionTemplate.setPropagationBehaviorName("PROPAGATION_REQUIRED");
        return transactionTemplate;
    }
  • 聲明式事務管理建立在AOP之上的。其本質是對方法前后進行攔截,然后在目標方法開始之前創建或者加入一個事務,在執行完目標方法之后根據執行情況提交或者回滾事務。
// 聲明事物的傳播屬性 
@Transactional(rollbackFor = Exception.class)
    @Override
    public void addUsers(List<User> users){
        for (User user : users) {
            int i = userMapper.insert(user);
            if (i == 1) {
                System.out.println("添加成功");
            } else {
                System.out.println("添加失敗");
            }
        }
    }

通俗地去理解兩者的區別,即聲明式事務只需要聲明就可以達到事務的效果;編程式事務需要編程才可以達到事務效果。

聲明式事務最大的優點就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需在配置文件中做相關的事務規則聲明(或通過基於@Transactional注解的方式),便可以將事務規則應用到業務邏輯中。

顯然聲明式事務管理要優於編程式事務管理,這正是Spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上注解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,后者的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立為方法等等。

聲明式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置文件,另一種就是基於@Transactional注解。顯然基於注解的方式更簡單易用,更清爽。(參考三)

事務傳播行為

什么叫事務傳播行為?即然是傳播,那么至少有兩個東西,才可以發生傳播。單體不存在傳播這個行為。

事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。
例如:methodA事務方法調用methodB事務方法時,methodB是繼續在調用者methodA的事務中運行呢,還是為自己開啟一個新事務運行,這就是由methodB的事務傳播行為決定的。

  • 注意:需要兩個方法上都加上注解,Spring事務中,默認是只有運行時異常(RuntimeException及其子類)才會回滾,而且使用的默認傳播性是REQUIRED。

    @Transactional(rollbackFor=RuntimeException.class) // 運行時異常才回滾。可以把 rollbackFor 設置為 Exception.class 異常就回滾。
    

Spring定義了七種傳播行為:

  • PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。

  • PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。

  • PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。

  • PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。

  • PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

  • PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。

  • PROPAGATION_NESTED--如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。

前六個策略類似於EJB CMT:常量名相同,因此,對EJB開發人員來說,應該立刻就感到熟悉。第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行為(如Spring的DataSourceTransactionManager),或者通過JTA支持嵌套事務。

PROPAGATION_REQUIRED

如果存在一個事務,則支持當前事務。如果沒有事務則開啟一個新的事務。
可以把事務想像成一個膠囊,在這個場景下方法B用的是方法A產生的膠囊(事務)。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}

單獨調用methodB方法時,因為當前上下文不存在事務,所以會開啟一個新的事務。
調用methodA方法時,因為當前上下文不存在事務,所以會開啟一個新的事務。當執行到methodB時,methodB發現當前上下文有事務,因此就加入到當前事務中來。

PROPAGATION_SUPPORTS

如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

// 事務屬性為SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}

單純的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。

PROPAGATION_MANDATORY

如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

// 事務屬性為MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // do something
}

當單獨調用methodB時,因為當前沒有一個活動的事務,則會拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);當調用methodA時,methodB則加入到methodA的事務中,事務地執行。

PROPAGATION_REQUIRES_NEW

使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。
它會開啟一個新的事務。如果一個事務已經存在,則先將這個存在的事務掛起。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
doSomeThingA();
methodB();
doSomeThingB();
// do something else
}

// 事務屬性為REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // do something
}

當調用

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之外的其它代碼導致的結果卻被回滾了。

PROPAGATION_NOT_SUPPORTED

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

方法A包含方法B調用,執行到方法B時,會掛起事務A,然后以非事務的方式執行方法B。

PROPAGATION_NEVER

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

PROPAGATION_NESTED

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

嵌套執行

@Transactional(propagation = Propagation.REQUIRED)
methodA(){
  doSomeThingA();
  methodB();
  doSomeThingB();
}

@Transactional(propagation = Propagation.NEWSTED)
methodB(){
  ……
}

如果單獨調用methodB方法,則按REQUIRED屬性執行。如果調用methodA方法,相當於下面的效果:

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.

服務掛起

前提:方法A支持事務,方法B不支持事務,方法A調用方法B。
解讀:在方法A開始運行時,系統為它建立Transaction,方法A中對於數據庫的處理操作,會在該Transaction的控制之下。這時,方法A調用方法B,方法A打開的 Transaction將掛起,方法B中任何數據庫操作,都不在該Transaction的管理之下。當方法B返回,方法A繼續運行,之前的Transaction回復,后面的數據庫操作繼續在該Transaction的控制之下 提交或回滾。

參考資料:


免責聲明!

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



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