<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    老妖的博客
    現實的中沒有幾個人能夠真為對方去死,甚至山盟海誓很快就會在金錢面前變的微不足道,這才是生活。沒有永遠的愛,除了你的父母對你,當然也就沒有永遠的恨,更沒有永遠的痛,時間是最好的治療大師,它會很快撫平你心靈上累累的傷痕。很多年以后你想起來時,那些在你生命中洶涌來往的人群至多是個模糊的影子或者毫無意義的名字
    posts - 105,  comments - 171,  trackbacks - 0
    轉自:http://www.javaeye.com/topic/35907

    解惑 spring 嵌套事務

    /**
    * @author 王政
    * @date 2006-11-24
    * @note 轉載請注明出處
    */

    在所有使用 spring 的應用中, 聲明式事務管理可能是使用率最高的功能了, 但是, 從我觀察到的情況看,
    絕大多數人并不能深刻理解事務聲明中不同事務傳播屬性配置的的含義, 讓我們來看一下 TransactionDefinition 接口中的定義

    代碼
    1. /**   
    2.      * Support a current transaction, create a new one if none exists.   
    3.      * Analogous to EJB transaction attribute of the same name.   
    4.      * <p>This is typically the default setting of a transaction definition.   
    5.      */   
    6.     int PROPAGATION_REQUIRED = 0;   
    7.   
    8.     /**   
    9.      * Support a current transaction, execute non-transactionally if none exists.   
    10.      * Analogous to EJB transaction attribute of the same name.   
    11.      * <p>Note: For transaction managers with transaction synchronization,   
    12.      * PROPAGATION_SUPPORTS is slightly different from no transaction at all,   
    13.      * as it defines a transaction scopp that synchronization will apply for.   
    14.      * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)   
    15.      * will be shared for the entire specified scope. Note that this depends on   
    16.      * the actual synchronization configuration of the transaction manager.   
    17.      * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization   
    18.      */   
    19.     int PROPAGATION_SUPPORTS = 1;   
    20.   
    21.     /**   
    22.      * Support a current transaction, throw an exception if none exists.   
    23.      * Analogous to EJB transaction attribute of the same name.   
    24.      */   
    25.     int PROPAGATION_MANDATORY = 2;   
    26.   
    27.     /**   
    28.      * Create a new transaction, suspend the current transaction if one exists.   
    29.      * Analogous to EJB transaction attribute of the same name.   
    30.      * <p>Note: Actual transaction suspension will not work on out-of-the-box   
    31.      * on all transaction managers. This in particular applies to JtaTransactionManager,   
    32.      * which requires the <code>javax.transaction.TransactionManager</code> to be   
    33.      * made available it to it (which is server-specific in standard J2EE).   
    34.      * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager   
    35.      */   
    36.     int PROPAGATION_REQUIRES_NEW = 3;   
    37.   
    38.     /**   
    39.      * Execute non-transactionally, suspend the current transaction if one exists.   
    40.      * Analogous to EJB transaction attribute of the same name.   
    41.      * <p>Note: Actual transaction suspension will not work on out-of-the-box   
    42.      * on all transaction managers. This in particular applies to JtaTransactionManager,   
    43.      * which requires the <code>javax.transaction.TransactionManager</code> to be   
    44.      * made available it to it (which is server-specific in standard J2EE).   
    45.      * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager   
    46.      */   
    47.     int PROPAGATION_NOT_SUPPORTED = 4;   
    48.   
    49.     /**   
    50.      * Execute non-transactionally, throw an exception if a transaction exists.   
    51.      * Analogous to EJB transaction attribute of the same name.   
    52.      */   
    53.     int PROPAGATION_NEVER = 5;   
    54.   
    55.     /**   
    56.      * Execute within a nested transaction if a current transaction exists,   
    57.      * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.   
    58.      * <p>Note: Actual creation of a nested transaction will only work on specific   
    59.      * transaction managers. Out of the box, this only applies to the JDBC   
    60.      * DataSourceTransactionManager when working on a JDBC 3.0 driver.   
    61.      * Some JTA providers might support nested transactions as well.   
    62.      * @see org.springframework.jdbc.datasource.DataSourceTransactionManager   
    63.      */   
    64.     int PROPAGATION_NESTED = 6;   

     

    我們可以看到, 在 spring 中一共定義了六種事務傳播屬性, 如果你覺得看起來不夠直觀, 那么我來轉貼一個滿大街都有的翻譯

    引用

    PROPAGATION_REQUIRED -- 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
    PROPAGATION_SUPPORTS -- 支持當前事務,如果當前沒有事務,就以非事務方式執行。
    PROPAGATION_MANDATORY -- 支持當前事務,如果當前沒有事務,就拋出異常。
    PROPAGATION_REQUIRES_NEW -- 新建事務,如果當前存在事務,把當前事務掛起。
    PROPAGATION_NOT_SUPPORTED -- 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
    PROPAGATION_NEVER -- 以非事務方式執行,如果當前存在事務,則拋出異常。
    PROPAGATION_NESTED -- 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。
    前六個策略類似于EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。
    它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行為(如Spring的DataSourceTransactionManager)

     

    在我所見過的誤解中, 最常見的是下面這種:

    引用

    假如有兩個業務接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一個方法實現如下

     

    /**
    * 事務屬性配置為 PROPAGATION_REQUIRED
    */
    void methodA() {
    // 調用 ServiceB 的方法
    ServiceB.methodB();
    }

    那么如果 ServiceB 的 methodB 如果配置了事務, 就必須配置為 PROPAGATION_NESTED

     

    這種想法可能害了不少人, 認為 Service 之間應該避免互相調用, 其實根本不用擔心這點,PROPAGATION_REQUIRED 已經說得很明白,
    如果當前線程中已經存在事務, 方法調用會加入此事務, 果當前沒有事務,就新建一個事務, 所以 ServiceB#methodB() 的事務只要遵循最普通的規則配置為 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我們稱之為內部事務, 為下文打下基礎) 拋了異常, 那么 ServiceA#methodA(我們稱之為外部事務) 如果沒有特殊配置此異常時事務提交 (即 +MyCheckedException的用法), 那么整個事務是一定要 rollback 的, 什么 Service 只能調 Dao 之類的言論純屬無稽之談, spring 只負責配置了事務屬性方法的攔截, 它怎么知道你這個方法是在 Service 還是 Dao 里 ?

    說了這么半天, 那到底什么是真正的事務嵌套呢, 解釋之前我們來看一下 Juergen Hoeller 的原話

    Juergen Hoeller 寫道

    PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed.

     

    Such independent inner transactions are for example used for id generation through manual sequences, where the access to the sequence table should happen in its own transactions, to keep the lock there as short as possible. The goal there is to avoid tying the sequence locks to the (potentially much longer running) outer transaction, with the sequence lock not getting released before completion of the outer transaction.

    PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.

    Nested transactions essentially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there - all within one isolated transaction, and not losing any previous work done within the outer transaction.

    For example, consider parsing a very large input file consisting of account transfer blocks: The entire file should essentially be parsed within one transaction, with one single commit at the end. But if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. You could either start over the entire transaction every time a block fails, remembering which blocks to skip - or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. The latter is of course much more efficient, in particular when a block at the end of the file fails.

     

    Juergen Hoeller 寫道

    Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction.

     

    So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. The immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.

    If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. At that point, the rollback rules of the outer transaction decide whether to trigger a rollback. That would be a rollback of the entire outer transaction then.

    So essentially, it depends on your exception handling. If you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. If you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction.

     

    也就是說, 最容易弄混淆的其實是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么這兩種方式又有何區別呢? 我簡單的翻譯一下 Juergen Hoeller 的話 :

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

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

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


    那么外部事務如何利用嵌套事務的 savepoint 特性呢, 我們用代碼來說話

    代碼
    1. ServiceA {   
    2.        
    3.     /**  
    4.      * 事務屬性配置為 PROPAGATION_REQUIRED  
    5.      */  
    6.     void methodA() {   
    7.         ServiceB.methodB();   
    8.     }   
    9.   
    10. }   
    11.   
    12. ServiceB {   
    13.        
    14.     /**  
    15.      * 事務屬性配置為 PROPAGATION_REQUIRES_NEW  
    16.      */    
    17.     void methodB() {   
    18.     }   
    19.        
    20. }      

     

    這種情況下, 因為 ServiceB#methodB 的事務屬性為 PROPAGATION_REQUIRES_NEW, 所以兩者不會發生任何關系, ServiceA#methodA 和 ServiceB#methodB 不會因為對方的執行情況而影響事務的結果, 因為它們根本就是兩個事務, 在 ServiceB#methodB 執行時 ServiceA#methodA 的事務已經掛起了 (關于事務掛起的內容已經超出了本文的討論范圍, 有時間我會再寫一些掛起的文章) .

    那么 PROPAGATION_NESTED 又是怎么回事呢? 繼續看代碼

    代碼
    1. ServiceA {   
    2.        
    3.     /**  
    4.      * 事務屬性配置為 PROPAGATION_REQUIRED  
    5.      */  
    6.     void methodA() {   
    7.         ServiceB.methodB();   
    8.     }   
    9.   
    10. }   
    11.   
    12. ServiceB {   
    13.        
    14.     /**  
    15.      * 事務屬性配置為 PROPAGATION_NESTED  
    16.      */    
    17.     void methodB() {   
    18.     }   
    19.        
    20. }      

     

    現在的情況就變得比較復雜了, ServiceB#methodB 的事務屬性被配置為 PROPAGATION_NESTED, 此時兩者之間又將如何協作呢? 從 Juergen Hoeller 的原話中我們可以找到答案, ServiceB#methodB 如果 rollback, 那么內部事務(即 ServiceB#methodB) 將回滾到它執行前的 SavePoint(注意, 這是本文中第一次提到它, 潛套事務中最核心的概念), 而外部事務(即 ServiceA#methodA) 可以有以下兩種處理方式:

    1. 改寫 ServiceA 如下

    代碼
    1. ServiceA {   
    2.        
    3.     /**  
    4.      * 事務屬性配置為 PROPAGATION_REQUIRED  
    5.      */  
    6.     void methodA() {   
    7.         try {   
    8.             ServiceB.methodB();   
    9.         } catch (SomeException) {   
    10.             // 執行其他業務, 如 ServiceC.methodC();   
    11.         }   
    12.     }   
    13.   
    14. }   
    15.   

     

    這種方式也是潛套事務最有價值的地方, 它起到了分支執行的效果, 如果 ServiceB.methodB 失敗, 那么執行 ServiceC.methodC(), 而 ServiceB.methodB 已經回滾到它執行之前的 SavePoint, 所以不會產生臟數據(相當于此方法從未執行過), 這種特性可以用在某些特殊的業務中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都沒有辦法做到這一點. (題外話 : 看到這種代碼, 似乎似曾相識, 想起了 prototype.js 中的 Try 函數 )

    2. 代碼不做任何修改, 那么如果內部事務(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滾到它執行之前的 SavePoint(在任何情況下都會如此),
    外部事務(即 ServiceA#methodA) 將根據具體的配置決定自己是 commit 還是 rollback (+MyCheckedException).


    上面大致講述了潛套事務的使用場景, 下面我們來看如何在 spring 中使用 PROPAGATION_NESTED, 首先來看 AbstractPlatformTransactionManager

    代碼
    1. /**  
    2.  * Create a TransactionStatus for an existing transaction.  
    3.  */  
    4. private TransactionStatus handleExistingTransaction(   
    5.         TransactionDefinition definition, Object transaction, boolean debugEnabled)   
    6.         throws TransactionException {   
    7.   
    8.    ... 省略   
    9.   
    10.     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {   
    11.         if (!isNestedTransactionAllowed()) {   
    12.             throw new NestedTransactionNotSupportedException(   
    13.                     "Transaction manager does not allow nested transactions by default - " +   
    14.                     "specify 'nestedTransactionAllowed' property with value 'true'");   
    15.         }   
    16.         if (debugEnabled) {   
    17.             logger.debug("Creating nested transaction with name [" + definition.getName() + "]");   
    18.         }   
    19.         if (useSavepointForNestedTransaction()) {   
    20.             // Create savepoint within existing Spring-managed transaction,   
    21.             // through the SavepointManager API implemented by TransactionStatus.   
    22.             // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.   
    23.             DefaultTransactionStatus status =   
    24.                     newTransactionStatus(definition, transaction, falsefalse, debugEnabled, null);   
    25.             status.createAndHoldSavepoint();   
    26.             return status;   
    27.         }   
    28.         else {   
    29.             // Nested transaction through nested begin and commit/rollback calls.   
    30.             // Usually only for JTA: Spring synchronization might get activated here   
    31.             // in case of a pre-existing JTA transaction.   
    32.             doBegin(transaction, definition);   
    33.             boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);   
    34.             return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);   
    35.         }   
    36.     }   
    37. }   


    一目了然

     

    1. 我們要設置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認為 false!!!

    再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法

    代碼
    1. /**  
    2.  * Create a savepoint and hold it for the transaction.  
    3.  * @throws org.springframework.transaction.NestedTransactionNotSupportedException  
    4.  * if the underlying transaction does not support savepoints  
    5.  */  
    6. public void createAndHoldSavepoint() throws TransactionException {   
    7.     setSavepoint(getSavepointManager().createSavepoint());   
    8. }   

     

    可以看到 Savepoint 是 SavepointManager.createSavepoint 實現的, 再看 SavepointManager 的層次結構, 發現
    其 Template 實現是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager
    中的 TransactonObject 都是它的子類 :



    JdbcTransactionObjectSupport 告訴我們必須要滿足兩個條件才能 createSavepoint :

    2. java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+
    3. Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0


    確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了. (全文完)

    posted on 2007-12-24 17:22 老妖 閱讀(1917) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    隨筆分類(48)

    隨筆檔案(104)

    好友鏈接

    我的豆瓣

    積分與排名

    • 積分 - 220770
    • 排名 - 257

    最新評論

    閱讀排行榜

    主站蜘蛛池模板: 性做久久久久久免费观看| 国产免费拔擦拔擦8X高清在线人 | 在线观看免费亚洲| 亚洲国产电影在线观看| 最近中文字幕免费2019| 亚洲黄网站wwwwww| 久久久久久精品成人免费图片| 亚洲色偷偷av男人的天堂| 真实国产乱子伦精品免费| 亚洲精品无码久久毛片波多野吉衣| 久久一区二区三区免费播放| 亚洲精品乱码久久久久66| 久久久国产精品福利免费| 亚洲第一中文字幕| 中国xxxxx高清免费看视频| 亚洲av无码久久忘忧草| 性色av免费观看| 黄色免费网站在线看| 中文字幕亚洲无线码| 精品免费tv久久久久久久| 亚洲永久中文字幕在线| 女性自慰aⅴ片高清免费| 黄色网址免费在线| 久久噜噜噜久久亚洲va久| 精品无码人妻一区二区免费蜜桃| 亚洲成人网在线观看| 亚洲人成无码www久久久| 伊人久久国产免费观看视频| 中文字幕亚洲无线码| 67194国产精品免费观看| 亚洲码和欧洲码一码二码三码 | 亚洲精品无码久久久久| 99精品视频在线观看免费专区| 亚洲AV综合色区无码二区偷拍| 热99re久久精品精品免费| A毛片毛片看免费| 亚洲免费一级视频| 亚洲视频人成在线播放| **毛片免费观看久久精品| 国产精品亚洲综合天堂夜夜| 亚洲av永久无码精品秋霞电影影院|