生活随笔
收集整理的這篇文章主要介紹了
Spring事务传递性探讨
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本篇主要討論下面幾點:
一: Spring 事務的傳遞性介紹
二: 第三方調用含有事務的Service拋異常方法探討
?
一: Spring 事務的傳遞性介紹
? ? 事務傳播行為,所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
? ? TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
? ? TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
? ? TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
? ? TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
? ? TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
? ? TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
? ? TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價TransactionDefinition.PROPAGATION_REQUIRED。
?
? ? 這里需要指出的是,前面的六種事務傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 啟動的事務內嵌于外部事務中(如果存在外部事務的話),此時,內嵌事務并不是一個獨立的事務,它依賴于外部事務的存在,只有通過外部的事務提交,才能引起內部事務的提交,嵌套的子事務不能單獨提交。如果熟悉 JDBC 中的保存點(SavePoint)的概念,那嵌套事務就很容易理解了,其實嵌套的子事務就是保存點的一個應用,一個事務中可以包括多個保存點,每一個嵌套子事務。另外,外部事務的回滾也會導致嵌套子事務的回滾。
?
二: 第三方調用含有事務的Service拋異常方法探討
? ??原始數據:
? ?
?
? ?
? ??假設術語如下:
? ??a)正常情況
? ? ? 調用方為Conumer, ?調用ServiceA的methodA, methodA調用ServiceA的 methodB和
ServiceA的methodC。
Java代碼?
?
@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??public?void?modifyCommodityInfo(Commodity?commodity)?{??????updateCommodityCatalog1(commodity);??????updateCommodityCatalog2(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog1(Commodity?commodity)?{??????commodity.setCatalog("catalog222222222");??????commodityDao.updateCommodity(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog2(Commodity?commodity)?{??????commodity.setName("name222");??????commodityDao.updateCommodity(commodity);??}??
?
? ?運行結果為:
? ??
? ?
?
?
?
?
? ??b)事務中一個方法跑出異常
? ? ? ServiceA的methodA 調用ServiceA的methodB和ServiceA的methodC,代碼如下:
? ? ?
Java代碼?
?
@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??public?void?modifyCommodityInfo(Commodity?commodity)?{??????updateCommodityCatalog1(commodity);??????updateCommodityCatalog2(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog1(Commodity?commodity)?{??????commodity.setCatalog("catalog222222222");??????commodityDao.updateCommodity(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog2(Commodity?commodity)?{??????commodity.setName("name222");??????commodityDao.updateCommodity(commodity);??????throw?new?RuntimeException("222");??}??
? ?運行結果:
? ??
?
??? ??c)捕獲異常的情況
? ?ServiceA的methodA 調用ServiceA的methodB和ServiceA的methodC,代碼如下:
Java代碼?
?
@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??public?void?modifyCommodityInfo(Commodity?commodity)?{??????updateCommodityCatalog1(commodity);??????try?{??????????updateCommodityCatalog2(commodity);??????}?catch?(Exception?e)?{????????????????}???????}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog1(Commodity?commodity)?{??????commodity.setCatalog("catalog222222222");??????commodityDao.updateCommodity(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog2(Commodity?commodity)?{??????commodity.setName("name222");??????commodityDao.updateCommodity(commodity);??????throw?new?RuntimeException("222");??}??
?
? ? 運行結果:
?
? ??
?
?
??d)跨服務事務
? ServiceA的methodA 調用 ServiceA的methodB和ServiceA的methodC和ServiceB的methodD,代碼如下:
Java代碼?
?
@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)?????public?void?modifyCommodityInfo(Commodity?commodity)?{?????????updateCommodityCatalog1(commodity);?????????updateCommodityCatalog2(commodity);?????????updateCommodityCatalog3(commodity);?????}???????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)?????private?void?updateCommodityCatalog1(Commodity?commodity)?{?????????commodity.setCatalog("catalog222222222");?????????commodityDao.updateCommodity(commodity);?????}???????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)?????private?void?updateCommodityCatalog2(Commodity?commodity)?{?????????commodity.setName("name222");?????????commodityDao.updateCommodity(commodity);?????}??????????//另一個Service?InnerService的方法?????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)?????public?void?updateCommodityCatalog3(Commodity?commodity)?{?????????commodity.setDescription("desc333");?????????commodityDao.updateCommodity(commodity);?????????throw?new?RuntimeException("333");?????}???????
? ? 運行結果:
? ??
? ? 假設MethodD拋出異常,則Consumer調用MethodA會發生怎樣的情形??
? ? 發現Consumer調用MethodA的時候出現了運行時異常,UnexpectedRollbackException: “Transaction rolled back because it has been marked as rollback-only”。這是為什么呢?
?
? ? 網上搜索了下,終于發現了一個合理的解釋。當MethodA調用MethodD的時候,且兩個方法都為required屬性,根據事務傳播級別,則methodA和methodD共享一個事務,當methodD拋出了異常,則共享事務回滾,但是被MethodA catch了,而MethodA又沒有及時拋出異常,則MethodA正常執行到最后的時候,則會做提交事務的操作,但是事務已經被回滾了,所以才出現了上面的異常。
?
? ? 既然這樣,小弟就開始YY了一下,哪些情況會使調用方沒有這個異常呢?經過與小伙伴們的思維碰撞,發現有一下幾個方法。
? ?1) MethodA 不加事務,所以執行到最后就不會commit,SUPPORTS和NOT_SUPPORTED都可以實現這種功能。
? ?2) MethodD設置不共享事務,擁有自己單獨的事務。驗證發現,REQUIRES_NEW可以實現這種功能。
? ?
?
? ?e)
? ?代碼如下:
? ?
Java代碼?
?
@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??public?void?modifyCommodityInfo(Commodity?commodity)?{??????updateCommodityCatalog1(commodity);??????updateCommodityCatalog2(commodity);??????try?{???commodityInnerService.updateCommodityCatalog3(commodity);??????}?catch?(Exception?e)?{??????}??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog1(Commodity?commodity)?{??????commodity.setCatalog("catalog222222222");??????commodityDao.updateCommodity(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog2(Commodity?commodity)?{??????commodity.setName("name222");??????commodityDao.updateCommodity(commodity);??}????//另一個Service?InnerService的方法??@Transactional(propagation?=?Propagation.REQUIRES_NEW,?rollbackFor?=?Exception.class)??public?void?updateCommodityCatalog3(Commodity?commodity)?{??????commodity.setDescription("desc333");??????commodityDao.updateCommodity(commodity);??????throw?new?RuntimeException("333");??}??
?
?結果如下:
?
?
?
?
? ?然后又YY了下,既然一個回滾的事務不能提交了,那么這個回滾的事務可以重復回滾嗎?
? ?f)ServiceA的methodA 調用 ServiceA的methodB和ServiceA的methodC和ServiceB的methodD和ServiceB的methodE。代碼如下:
? ?
Java代碼?
?
@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??public?void?modifyCommodityInfo(Commodity?commodity)?{??????updateCommodityCatalog1(commodity);??????updateCommodityCatalog2(commodity);??????try?{???ommodityInnerService.updateCommodityCatalog3(commodity);??????}?catch?(Exception?e)?{??????}???????try?{???commodityInnerService.updateCommodityCatalog4(commodity);??????}?catch?(Exception?e)?{??????}??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog1(Commodity?commodity)?{??????commodity.setCatalog("catalog222222222");??????commodityDao.updateCommodity(commodity);??}????@Transactional(propagation?=?Propagation.REQUIRED,?rollbackFor?=?Exception.class)??private?void?updateCommodityCatalog2(Commodity?commodity)?{??????commodity.setName("name222");??????commodityDao.updateCommodity(commodity);??}????//另一個Service?InnerService的方法??@Transactional(propagation?=?Propagation.REQUIRES,?rollbackFor?=?Exception.class)??public?void?updateCommodityCatalog3(Commodity?commodity)?{??????commodity.setDescription("desc333");??????commodityDao.updateCommodity(commodity);??????throw?new?RuntimeException("333");??}?????//另一個Service?InnerService的方法??@Transactional(propagation?=?Propagation.REQUIRES,?rollbackFor?=?Exception.class)??public?void?updateCommodityCatalog4(Commodity?commodity)?{??????commodity.setDescription("desc333");??????commodityDao.updateCommodity(commodity);??????throw?new?RuntimeException("333");??}?????
運行結果:
? ?發現還是只拋出了一個Transaction rolled back because it has been marked as rollback-only
所以猜測一個被共享的事務拋出多個異常的時候只是標記下rollback-only,而在方法結束的時候判斷是執行事務還是回滾事務。
?
? ?總結:
? ? 1) 單個ServiceA 內部調用不存在事務傳播,相當于把methodB和methodC的代碼嵌套到methodA的代碼中,即使定義了也無法生效。
? ? 2) 跨Service調用存在事務傳播級別,需要考慮共享事務,還是新事務調用,即跨Service的調用是否需要需要回滾本Servcie的代碼。
? ?
?
?參考:https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
from:?http://labreeze.iteye.com/blog/2277261
總結
以上是生活随笔為你收集整理的Spring事务传递性探讨的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。