問題
spring的事務(wù)處理為我們省掉了很多操作,如今只需要簡單的配置一下就可以完成對應(yīng)的事務(wù)操作况脆。不過停留在會用的層面上還是缺少一些遇見問題時處理的能力圈匆。下面我要總結(jié)一下spring事務(wù)處理時的設(shè)計中,傳播級別到底是如何工作的,比如
- REQUIRED為什么在try中catch住異常
外層事務(wù)
一樣會回滾纬傲? - REQUIRES_NEW是如何開啟新事物,并與
外層事務(wù)
獨(dú)立開來汁雷? - NESTED是如何做到
子事務(wù)
不影響外層事務(wù)?外層事務(wù)會影響子事務(wù)报咳?
名詞
外層事務(wù):事務(wù)A中調(diào)用了事務(wù)B侠讯,那么事務(wù)A就是B的外層事務(wù)。
新事務(wù):事務(wù)A中調(diào)用事務(wù)B暑刃,事務(wù)B的傳播級別為REQUIRES_NEW厢漩,那么事務(wù)B就是新事務(wù)。
子事務(wù):事務(wù)A中調(diào)用事務(wù)B岩臣,事務(wù)B的傳播級別為NESTED溜嗜,那么事務(wù)B就是新事務(wù)宵膨。
探究
先把這里org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
作為事務(wù)處理的入口,來看一下里面的行為炸宵。
前三行是一些配置信息與調(diào)用的方法信息涯曲,不多講。
這里的關(guān)鍵點在于if里面的內(nèi)容:
1.獲取TransactionInfo
信息零截,可能會新建事務(wù)弧哎。createTransactionIfNecessary
2.執(zhí)行事務(wù)代碼invocation.proceedWithInvocation();
3.發(fā)生異常的時候的處理completeTransactionAfterThrowing(txInfo, ex);
4.無論有無異常序攘,都會清理當(dāng)前的事務(wù)信息。cleanupTransactionInfo(txInfo);
5.正常執(zhí)行后的提交動作泛粹。commitTransactionAfterReturning(txInfo);
這里的提交并不保證完成提交碱呼,是問題1,3的關(guān)鍵舶斧。
先介紹幾個關(guān)鍵的元信息類:
TransactionDefinition
:就是被@Transaction中的一些屬性信息呀忧,包含傳播級別,隔離級別商佑,超時時間喂急,只讀設(shè)置懂诗,名字。
TransactionAttribute
:繼承了TransactionDefinition侯繁,包含了qualifier写穴,rollbackOn異扯韫埃回滾的支持信息。
DefaultTransactionStatus
:
transaction:事務(wù)對象昔逗,每一個事務(wù)方法都可能有自己獨(dú)立的對象卡乾,也可能公用一個陪踩。
newTransaction:是否是新事務(wù),用來影響事務(wù)的提交和回滾岂座。
newSynchronization: 新的同步器态蒂?沒找到一個很好的解釋,用來控制當(dāng)前線程與事務(wù)屬性的關(guān)系费什。
TransactionSynchronizationManager
就是這個關(guān)系的管理器钾恢。readOnly: 只讀屬性,這里不關(guān)心鸳址。
debug:就是一個debug的開啟緩存赘那,省去二次計算。
suspendedResources:掛起的資源氯质,比如REQUIRES_NEW需要掛起外層事務(wù)。
TransactionInfo:事務(wù)的信息概括祠斧,包含上面的TransactionAttribute闻察,TransactionStatus,和事務(wù)管理器對象,方法信息辕漂,重要的是oldTransactionInfo外層事務(wù)信息呢灶,在事務(wù)結(jié)束后會恢復(fù)執(zhí)行外層的事務(wù)邏輯。
回到方法TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
在449行用了一個委托的事務(wù)屬性對象來描述TransactionAttribute
钉嘹。就是關(guān)于事務(wù)方法的描述信息鸯乃。
再來看461的getTransaction
,這個過程就是對不同的傳播級別的處理方式跋涣。我們這里只關(guān)注DefaultTransactionStatus的變化缨睡。
可見這里處理的有存在事務(wù)與不存在事務(wù)的兩種處理流程。
isExistingTransaction
通過實現(xiàn)可以看到就是數(shù)據(jù)庫連接是否打開了事務(wù)陈辱。
先看在新建事務(wù)時的過程:
- 358行奖年,傳播級別為PROPAGATION_MANDATORY時,拋出異常沛贪,這就是這個傳播級別必須要在事務(wù)方法中調(diào)用的原因陋守。
- 362行,對于REQUIRED利赋,REQUIRES_NEW水评,NESTED級別的處理。重點關(guān)注
DefaultTransactionStatus
里面的newTransaction和newSynchronization
媚送。
370行中燥,在默認(rèn)情況下newSynchronization為true,因為getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS
371行季希,newTransactionStatus方法中褪那,再用
boolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();
來決定最終的newSynchronization。
TransactionSynchronizationManager.isSynchronizationActive()
就是獲取當(dāng)前現(xiàn)成的事務(wù)同步器的激活狀態(tài)式塌。如果已經(jīng)激活博敬,就不用重復(fù)激活了。
由此可見峰尝,最終的newSynchronization是和當(dāng)前的同步器激活狀態(tài)關(guān)系的偏窝。只要是已經(jīng)激活,那么就是false了武学。
373行祭往,doBegin就是開啟了事務(wù)連接的事務(wù)狀態(tài)。
374行火窒,prepareSynchronization準(zhǔn)備同步器的初始化硼补。進(jìn)入方法里面可以看見是設(shè)置了當(dāng)前線程的:
1.激活狀態(tài)。2.隔離級別熏矿。3.只讀已骇。4.事務(wù)名稱离钝。5.空的同步器集合(TransactionSynchronization)。
這里我把幾種級別的各個參數(shù)流程列一下褪储,以便講解:
1.REQUIRED:
在沒有外層事務(wù)時卵渴,它需要一個事務(wù)管理器transaction,此時的事務(wù)同步器還沒有激活鲤竹,因此需要激活當(dāng)前線程的事務(wù)同步器即actualNewSynchonization為true浪读,同時調(diào)用了doBegin來讓具體的實現(xiàn)打開線程狀態(tài)。最后準(zhǔn)備并初始化線程同步器的狀態(tài)prepareSynchronization辛藻,即設(shè)置了當(dāng)前線程事務(wù)的信息碘橘,并激活了同步器狀態(tài)initSynchronization
。
在有外層事務(wù)時揩尸,它不需要掛起事務(wù)資源蛹屿,因為要加入到當(dāng)前的事務(wù)。事務(wù)同步器與外層用同一個岩榆。由于不會掛起資源错负,也就不會重置同步器的狀態(tài),也不需要重新激活同步器勇边。這樣就加入到當(dāng)前的事務(wù)中犹撒。
之后在調(diào)用事務(wù)方法的時候,如果出現(xiàn)了異常粒褒,即使被外層捕獲到不拋出识颊,也會使外層事務(wù)回滾。這里我們就要看一下completeTransactionAfterThrowing這個方法里面的東西奕坟。
很簡單祥款,在事務(wù)有效的時候,符合需要回滾的異常時月杉,走rollback方法刃跛。否則繼續(xù)commit操作。
再來深入一下rollback方法苛萎,里面調(diào)用了processRollback桨昙,
這里走的是第三個if分支,兩個條件腌歉,1.TransactionStatus的rollbackOnly狀態(tài)蛙酪;2.全局的rollbackOnly狀態(tài)isGlobalRollbackOnParticipationFailure,默認(rèn)返回true翘盖,表示調(diào)用失敗時候桂塞,需要全局回滾。之后進(jìn)入到doSetRollbackOnly馍驯,其實就是讓各自的實現(xiàn)去設(shè)置連接的rollbackOnly狀態(tài)藐俺。
之后在外層事務(wù)提交的時候炊甲,具體看commit方法。
我們看見兩個分支欲芹,
defStatus.isLocalRollbackOnly()只回滾本事務(wù)
和
!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly(),shouldCommitOnGlobalRollbackOnly表示全局事務(wù)在標(biāo)記rollbackOnly時候是否需要繼續(xù)提交吟吝,默認(rèn)返回false菱父,只有JTa的實現(xiàn)返回了True。defStatus.isGlobalRollbackOnly()是獲取transaction的rollbackOnly標(biāo)記剑逃,應(yīng)該是標(biāo)記在對應(yīng)的連接狀態(tài)上浙宜。
由上面發(fā)現(xiàn)已經(jīng)將這個transaction標(biāo)記了rollbackOnly,所以就轉(zhuǎn)向了去處理回滾的操作蛹磺。processRollback->doRollback粟瞬。
描述有點多,總結(jié)一下就是:
1.REQUIRED參與到當(dāng)前的事務(wù)萤捆,融合進(jìn)來裙品。
2.當(dāng)它出現(xiàn)異常時,會標(biāo)記當(dāng)前的transaction管理器中連接的rollbackOnly狀態(tài)俗或,使得外層事務(wù)在提交的時候監(jiān)測到這個標(biāo)記市怎,發(fā)現(xiàn)不可以繼續(xù)提交,需要回滾辛慰。
2.REQUIRES_NEW:
在沒有外層事務(wù)時区匠,情況同REQUIRED,就是打開一個事務(wù)帅腌,激活同步器等操作驰弄。
在有外層事務(wù)時,它是需要掛起事務(wù)資源的速客,掛起資源時戚篙,會將同步器的狀態(tài)恢復(fù)為初始狀態(tài),通知具體實現(xiàn)進(jìn)行相應(yīng)的掛起資源操作挽封。這個時候此事務(wù)的同步器狀態(tài)為true已球,所以會準(zhǔn)備一個新的事務(wù)同步器狀態(tài),開啟新事務(wù)doBegin辅愿。
同1理來看智亮,這個新事物在processRollback中不會標(biāo)記transaction管理器的rollbackOnly狀態(tài),只是觸發(fā)了自己的doRollback操作点待,所以不會影響到外層事務(wù)的提交阔蛉。
3.NESTED:
在沒有外層事務(wù)時,情況同REQUIRED癞埠,就是打開一個事務(wù)状原,激活同步器等操作聋呢。
在有外層事務(wù)時,它不會掛起事務(wù)資源颠区,這里討論有支持savePoint的實現(xiàn)(除了Jta)削锰。不會重置同步器,也就是說與外層事務(wù)共用一個同步器毕莱。與REQUIRED類似器贩,與外層事務(wù)進(jìn)行了融合。只不過在發(fā)生異常的時候朋截,在processRollback走了status.hasSavepoint()分支蛹稍,這樣就優(yōu)先進(jìn)入了status.rollbackToHeldSavepoint()里面。
然后通過對應(yīng)的實現(xiàn)通知連接來回滾到某個savePoint部服,并且釋放掉唆姐。
這樣就完成了單獨(dú)的回滾操作,并不會向REQUIRED那樣影響到外層的提交廓八。
以上我們?nèi)齻€問題解答完了奉芦。
4.MANDATORY:
沒有外層事務(wù)時拋出異常,必須要在事務(wù)里面執(zhí)行瘫想。
有外層事務(wù)時仗阅,像REQUIRED那樣融合進(jìn)去。
5.SUPPORTS:
沒有外層事務(wù)時国夜,不會使用transaction管理器减噪,也就不會形成事務(wù)操作。
有外層事務(wù)時车吹,像REQUIRED那樣融合進(jìn)去筹裕。
6.NOT_SUPPORTED:
沒有外層事務(wù)時,以非事務(wù)方式運(yùn)行窄驹。
有外層事務(wù)時朝卒,把外層事務(wù)掛起,但是不會為自己新建事務(wù)乐埠,以非事務(wù)狀態(tài)運(yùn)行抗斤。
7.NEVER:
沒有外層事務(wù)時,以非事務(wù)方式運(yùn)行丈咐。
有外層事務(wù)時瑞眼,拋出一場。
總結(jié):
以上就是把所有傳播級別的具體流程簡單分析了一下棵逊,讓我們知道了這7中級別是如何工作的伤疙,也解決了REQUIRED,REQUIRES_NEW和NESTED里面對于異常是如何處理的。
1.REQUIRED在處理異常時是給對應(yīng)的連接設(shè)置了rollbackOnly狀態(tài),來影響外層事務(wù)的提交的徒像。外層事務(wù)發(fā)現(xiàn)了rollbackOnly為true黍特,就會轉(zhuǎn)向到回滾流程。
2.REQUIRES_NEW是一個單獨(dú)的事務(wù)操作锯蛀,回滾完自己的事務(wù)不會影響其他灭衷。
3.NESTED是融合在外層事務(wù)中的,只不過這里支持了savePoint的操作谬墙,使得子事務(wù)在回滾時會指定到一個savePoint上今布,不會影響外層事務(wù)。因為與外層事務(wù)融合了拭抬,所以外層事務(wù)會影響到子事務(wù)。