eric-1001c

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            3 隨筆 :: 45 文章 :: 12 評(píng)論 :: 0 Trackbacks

          Spring事務(wù)傳播機(jī)制解惑

          概述

          當(dāng)我們調(diào)用一個(gè)基于Spring的Service接口方法(如UserService#addUser())時(shí),它將運(yùn)行于Spring管理的事務(wù)環(huán)境中,Service接口方法可能會(huì)在內(nèi)部調(diào)用其它的Service接口方法以共同完成一個(gè)完整的業(yè)務(wù)操作,因此就會(huì)產(chǎn)生服務(wù)接口方法嵌套調(diào)用的情況,Spring通過(guò)事務(wù)傳播行為控制當(dāng)前的事務(wù)如何傳播到被嵌套調(diào)用的目標(biāo)服務(wù)接口方法中。

          事務(wù)傳播是Spring進(jìn)行事務(wù)管理的重要概念,其重要性怎么強(qiáng)調(diào)都不為過(guò)。但是事務(wù)傳播行為也是被誤解最多的地方,在本文里,我們將詳細(xì)分析不同事務(wù)傳播行為的表現(xiàn)形式,掌握它們之間的區(qū)別。

          事務(wù)傳播行為種類

          Spring在TransactionDefinition接口中規(guī)定了7種類型的事務(wù)傳播行為,它們規(guī)定了事務(wù)方法和事務(wù)方法發(fā)生嵌套調(diào)用時(shí)事務(wù)如何進(jìn)行傳播:

          1事務(wù)傳播行為類型

          事務(wù)傳播行為類型

          說(shuō)明

          PROPAGATION_REQUIRED

          如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中。這是最常見(jiàn)的選擇。

          PROPAGATION_SUPPORTS

          支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行。

          PROPAGATION_MANDATORY

          使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常。

          PROPAGATION_REQUIRES_NEW

          新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。

          PROPAGATION_NOT_SUPPORTED

          以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。

          PROPAGATION_NEVER

          以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。

          PROPAGATION_NESTED

          如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行與PROPAGATION_REQUIRED類似的操作。

          當(dāng)使用PROPAGATION_NESTED時(shí),底層的數(shù)據(jù)源必須基于JDBC 3.0,并且實(shí)現(xiàn)者需要支持保存點(diǎn)事務(wù)機(jī)制。

          幾種容易引起誤解的組合事務(wù)傳播行為

          當(dāng)服務(wù)接口方法分別使用表1中不同的事務(wù)傳播行為,且這些接口方法又發(fā)生相互調(diào)用的情況下,大部分組合都是一目了然,容易理解的。但是,也存在一些容易引起誤解的組合事務(wù)傳播方式。

          下面,我們通過(guò)兩個(gè)具體的服務(wù)接口的組合調(diào)用行為來(lái)破解這一難點(diǎn)。這兩個(gè)服務(wù)接口分別是UserService和ForumService,UserSerice有一個(gè)addCredits()方法,F(xiàn)orumSerivce#addTopic()方法調(diào)用了UserSerice#addCredits()方法,發(fā)生關(guān)聯(lián)性服務(wù)方法的調(diào)用:

          public class ForumService {

          private UserService userService;

          public void addTopic(){①調(diào)用其它服務(wù)接口的方法

          //add Topic…

          userService.addCredits();②被關(guān)聯(lián)調(diào)用的業(yè)務(wù)方法

          }

          }

          嵌套調(diào)用的事務(wù)方法

          對(duì)Spring事務(wù)傳播行為最常見(jiàn)的一個(gè)誤解是:當(dāng)服務(wù)接口方法發(fā)生嵌套調(diào)用時(shí),被調(diào)用的服務(wù)方法只能聲明為PROPAGATION_NESTED。這種觀點(diǎn)犯了望文生義的錯(cuò)誤,誤認(rèn)為PROPAGATION_NESTED是專為方法嵌套準(zhǔn)備的。這種誤解遺害不淺,執(zhí)有這種誤解的開(kāi)發(fā)者錯(cuò)誤地認(rèn)為:應(yīng)盡量不讓Service類的業(yè)務(wù)方法發(fā)生相互的調(diào)用,Service類只能調(diào)用DAO層的DAO類,以避免產(chǎn)生嵌套事務(wù)。

          其實(shí),這種顧慮是完全沒(méi)有必要的,PROPAGATION_REQUIRED已經(jīng)清楚地告訴我們:事務(wù)的方法會(huì)足夠“聰明”地判斷上下文是否已經(jīng)存在一個(gè)事務(wù)中,如果已經(jīng)存在,就加入到這個(gè)事務(wù)中,否則創(chuàng)建一個(gè)新的事務(wù)。

          依照上面的例子,假設(shè)我們將ForumService#addTopic()和UserSerice#addCredits()方法的事務(wù)傳播行為都設(shè)置為PROPAGATION_REQUIRED,這兩個(gè)方法將運(yùn)行于同一個(gè)事務(wù)中。

          為了清楚地說(shuō)明這點(diǎn),可以將Log4J的日志設(shè)置為DEBUG級(jí)別,以觀察Spring事務(wù)管理器內(nèi)部的運(yùn)行情況。下面將兩個(gè)業(yè)務(wù)方法都設(shè)置為PROPAGATION_REQUIRED,Spring所輸出的日志信息如下:

          Using transaction object

          [org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject@e3849c]

          ①為ForumService#addTopic()新建一個(gè)事務(wù)

          Creating new transaction with name [com.baobaotao.service.ForumService.addTopic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

          Acquired Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5] for JDBC transaction

          Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5] to manual commit

          Bound value [org.springframework.jdbc.datasource.ConnectionHolder@ee1ede] for key [org.apache.commons.dbcp.BasicDataSource@4204] to thread [main]

          Initializing transaction synchronization

          Getting transaction for [com.baobaotao.service.ForumService.addTopic]

          Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@ee1ede] for key [org.apache.commons.dbcp.BasicDataSource@4204] bound to thread [main]

          Using transaction object [org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject@8b8a47]

          UserService#addCredits()簡(jiǎn)單地加入到已存在的事務(wù)中(即①處創(chuàng)建的事務(wù))

          Participating in existing transaction

          Getting transaction for [com.baobaotao.service.UserService.addCredits]

          Completing transaction for [com.baobaotao.service.UserService.addCredits]

          Completing transaction for [com.baobaotao.service.ForumService.addTopic]

          Triggering beforeCommit synchronization

          Triggering beforeCompletion synchronization

          Initiating transaction commit

          ③調(diào)用底層Connection#commit()方法提交事務(wù)

          Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5]

          Triggering afterCommit synchronization

          Triggering afterCompletion synchronization

          Clearing transaction synchronization

          嵌套事務(wù)

          將ForumService#addTopic()設(shè)置為PROPAGATION_REQUIRED時(shí),UserSerice#addCredits()設(shè)置為PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY時(shí),運(yùn)行的效果都是一致的(當(dāng)然,如果單獨(dú)調(diào)用addCredits()就另當(dāng)別論了)。

          當(dāng)addTopic()運(yùn)行在一個(gè)事務(wù)下(如設(shè)置為PROPAGATION_REQUIRED),而addCredits()設(shè)置為PROPAGATION_NESTED時(shí),如果底層數(shù)據(jù)源支持保存點(diǎn),Spring將為內(nèi)部的addCredits()方法產(chǎn)生的一個(gè)內(nèi)嵌的事務(wù)。如果addCredits()對(duì)應(yīng)的內(nèi)嵌事務(wù)執(zhí)行失敗,事務(wù)將回滾到addCredits()方法執(zhí)行前的點(diǎn),并不會(huì)將整個(gè)事務(wù)回滾。內(nèi)嵌事務(wù)是內(nèi)層事務(wù)的一部分,所以只有外層事務(wù)提交時(shí),嵌套事務(wù)才能一并提交。

          嵌套事務(wù)不能夠提交,它必須通過(guò)外層事務(wù)來(lái)完成提交的動(dòng)作,外層事務(wù)的回滾也會(huì)造成內(nèi)部事務(wù)的回滾。

          嵌套事務(wù)和新事務(wù)

          PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED也是容易混淆的兩個(gè)傳播行為。PROPAGATION_REQUIRES_NEW 啟動(dòng)一個(gè)新的、和外層事務(wù)無(wú)關(guān)的“內(nèi)部”事務(wù)。該事務(wù)擁有自己的獨(dú)立隔離級(jí)別和鎖,不依賴于外部事務(wù),獨(dú)立地提交和回滾。當(dāng)內(nèi)部事務(wù)開(kāi)始執(zhí)行時(shí),外部事務(wù)將被掛起,內(nèi)務(wù)事務(wù)結(jié)束時(shí),外部事務(wù)才繼續(xù)執(zhí)行。

          由此可見(jiàn), PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區(qū)別在于:PROPAGATION_REQUIRES_NEW 將創(chuàng)建一個(gè)全新的事務(wù),它和外層事務(wù)沒(méi)有任何關(guān)系,而 PROPAGATION_NESTED 將創(chuàng)建一個(gè)依賴于外層事務(wù)的子事務(wù),當(dāng)外層事務(wù)提交或回滾時(shí),子事務(wù)也會(huì)連帶提交和回滾。

          其它需要注意問(wèn)題

          以下幾個(gè)問(wèn)題值得注意:

          1) 當(dāng)業(yè)務(wù)方法被設(shè)置為PROPAGATION_MANDATORY時(shí),它就不能被非事務(wù)的業(yè)務(wù)方法調(diào)用。如將ForumService#addTopic()設(shè)置為PROPAGATION_MANDATORY,如果展現(xiàn)層的Action直接調(diào)用addTopic()方法,將引發(fā)一個(gè)異常。正確的情況是:addTopic()方法必須被另一個(gè)帶事務(wù)的業(yè)務(wù)方法調(diào)用(如ForumService#otherMethod())。所以PROPAGATION_MANDATORY的方法一般都是被其它業(yè)務(wù)方法間接調(diào)用的。

          2) 當(dāng)業(yè)務(wù)方法被設(shè)置為PROPAGATION_NEVER時(shí),它將不能被擁有事務(wù)的其它業(yè)務(wù)方法調(diào)用。假設(shè)UserService#addCredits()設(shè)置為PROPAGATION_NEVER,當(dāng)ForumService# addTopic()擁有一個(gè)事務(wù)時(shí),addCredits()方法將拋出異常。所以PROPAGATION_NEVER方法一般是被直接調(diào)用的。

          3)當(dāng)方法被設(shè)置為PROPAGATION_NOT_SUPPORTED時(shí),外層業(yè)務(wù)方法的事務(wù)會(huì)被掛起,當(dāng)內(nèi)部方法運(yùn)行完成后,外層方法的事務(wù)重新運(yùn)行。如果外層方法沒(méi)有事務(wù),直接運(yùn)行,不需要做任何其它的事。

          小結(jié)

          在Spring聲明式事務(wù)管理的配置中,事務(wù)傳播行為是最容易被誤解的配置項(xiàng),原因在于事務(wù)傳播行為名稱(如PROPAGATION_NESTED:嵌套式事務(wù))和代碼結(jié)構(gòu)的類似性上(業(yè)務(wù)類方法嵌套調(diào)用另一個(gè)業(yè)務(wù)類方法)。這種誤解在很多Spring開(kāi)發(fā)者中廣泛存在,本文深入講解了Spring事務(wù)傳播行為對(duì)業(yè)務(wù)方法嵌套調(diào)用的真實(shí)影響,希望能幫助讀者化解對(duì)事務(wù)傳播行為的困惑。

          posted on 2008-04-05 12:43 Eric-1001c 閱讀(792) 評(píng)論(0)  編輯  收藏 所屬分類: Hibernate
          主站蜘蛛池模板: 呼玛县| 广平县| 云南省| 玉龙| 柳河县| 鹿泉市| 莱芜市| 江城| 温泉县| 福泉市| 宜良县| 沈丘县| 宁安市| 墨江| 彭阳县| 河南省| 通河县| 班玛县| 扬州市| 皮山县| 乳山市| 镇雄县| 林口县| 汤阴县| 吴堡县| 铁力市| 宜春市| 安塞县| 安泽县| 枣庄市| 柳江县| 博客| 乌拉特前旗| 平乐县| 灵川县| 大足县| 梧州市| 团风县| 张家港市| 渭南市| 望城县|