最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談Spring嵌套事務(wù)是怎么回滾的

 更新時(shí)間:2021年11月26日 15:53:37   作者:JavaEdge.  
本文主要介紹了Spring嵌套事務(wù)是怎么回滾的,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

更深入理解 Spring 事務(wù)。

用戶注冊(cè)完成后,需要給該用戶登記一門PUA必修課,并更新該門課的登記用戶數(shù)。
為此,我添加了兩個(gè)表。
課程表 course,記錄課程名稱和注冊(cè)的用戶數(shù)。

用戶選課表 user_course,記錄用戶表 user 和課程表 course 之間的多對(duì)多關(guān)聯(lián)。

同時(shí)為課程表初始化了一條課程信息

接下來我們完成用戶的相關(guān)操作,主要包括兩部分:

新增用戶選課記錄

課程登記學(xué)生數(shù) + 1

新增業(yè)務(wù)類 CourseService實(shí)現(xiàn)相關(guān)業(yè)務(wù)邏輯,分別調(diào)用了上述方法保存用戶與課程的關(guān)聯(lián)關(guān)系,并給課程注冊(cè)人數(shù)+1

為避免注冊(cè)課程的業(yè)務(wù)異常導(dǎo)致用戶信息無法保存,這里 catch 注冊(cè)課程方法中拋出的異常。希望當(dāng)注冊(cè)課程發(fā)生錯(cuò)誤時(shí),只回滾注冊(cè)課程部分,保證用戶信息依然正常。

為驗(yàn)證異常是否符合預(yù)期,在 regCourse() 里拋一個(gè)注冊(cè)失敗異常:

執(zhí)行代碼:

注冊(cè)失敗部分的異常符合預(yù)期,但是后面又多了一個(gè)這樣的錯(cuò)誤提示:Transaction rolled back because it has been marked as rollback-only

最后用戶和選課的信息都被回滾了,顯然這不符預(yù)期。
期待結(jié)果是即便內(nèi)部事務(wù)regCourse()發(fā)生異常,外部事務(wù)saveStudent()俘獲該異常后,內(nèi)部事務(wù)應(yīng)自行回滾,不影響外部事務(wù)。
這是什么原因造成的呢?

源碼解析

偽代碼梳理整個(gè)事務(wù)的結(jié)構(gòu):

整個(gè)業(yè)務(wù)包含2層事務(wù):

  • 外層 saveUser() 的事務(wù)
  • 內(nèi)層 regCourse() 事務(wù)

Spring聲明式事務(wù)中的propagation屬性,表示對(duì)這些方法使用怎樣的事務(wù),即:
一個(gè)帶事務(wù)的方法調(diào)用了另一個(gè)帶事務(wù)的方法,被調(diào)用的方法它怎么處理自己事務(wù)和調(diào)用方法事務(wù)之間的關(guān)系。

propagation 有7種配置:

  • REQUIRED:默認(rèn)值,如果本來有事務(wù),則加入該事務(wù),如果沒有事務(wù),則創(chuàng)建新的事務(wù)。
  • SUPPORTS
  • MANDATORY
  • REQUIRES_NEW
  • NOT_SUPPORTED
  • NEVER
  • NESTED

因?yàn)椋?/p>

  • 在 saveUser() 上聲明了一個(gè)外部的事務(wù),就已經(jīng)存在一個(gè)事務(wù)了
  • 在propagation值為默認(rèn)REQUIRED時(shí)

regCourse() 就會(huì)加入到已有的事務(wù)中,兩個(gè)方法共用一個(gè)事務(wù)。

Spring 事務(wù)處理的核心:

TransactionAspectSupport.invokeWithinTransaction()

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {
 
   TransactionAttributeSource tas = getTransactionAttributeSource();
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 是否需要?jiǎng)?chuàng)建一個(gè)事務(wù)
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
         // 調(diào)用具體的業(yè)務(wù)方法
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // 當(dāng)發(fā)生異常時(shí)進(jìn)行處理
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
      // 正常返回時(shí)提交事務(wù)
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
   //......省略非關(guān)鍵代碼.....
}

整個(gè)方法完成了事務(wù)的一整套處理邏輯,如下:

  • 檢查是否需要?jiǎng)?chuàng)建事務(wù)
  • 調(diào)用具體的業(yè)務(wù)方法進(jìn)行處理
  • 提交事務(wù)
  • 處理異常

當(dāng)前案例是兩個(gè)事務(wù)嵌套,外層事務(wù) saveUser()和內(nèi)層事務(wù) regCourse(),每個(gè)事務(wù)都會(huì)調(diào)用到這個(gè)方法。所以,該方法會(huì)被調(diào)兩次。

內(nèi)層事務(wù)

當(dāng)捕獲了異常,會(huì)調(diào)用

TransactionAspectSupport.completeTransactionAfterThrowing()

進(jìn)行異常處理:

對(duì)異常類型做了一些檢查,當(dāng)符合聲明中的定義后,執(zhí)行具體的 rollback 操作,這個(gè)操作是通過如下方法完成:

AbstractPlatformTransactionManager rollback()

該回滾實(shí)現(xiàn)負(fù)責(zé)處理正參與到已有事務(wù)集的事務(wù)。委托執(zhí)行Rollback和doSetRollbackOnly。

繼續(xù)調(diào)用

processRollback()

該方法里區(qū)分了三種場(chǎng)景:

  • 是否有保存點(diǎn)
  • 是否為一個(gè)新的事務(wù)
  • 是否處于一個(gè)更大的事務(wù)中

因?yàn)槟J(rèn)傳播類型REQUIRED,嵌套的事務(wù)并未開啟一個(gè)新事務(wù),所以屬于當(dāng)前事務(wù)處于一個(gè)更大事務(wù)中,所以會(huì)走到分支1。

如下的判斷條件確定是否設(shè)置為僅回滾:

if (status.isLocalRollbackOnly() ||
	 isGlobalRollbackOnParticipationFailure())

滿足任一,都會(huì)執(zhí)行 doSetRollbackOnly():

isLocalRollbackOnly

默認(rèn) false,當(dāng)前場(chǎng)景為 falseisGlobalRollbackOnParticipationFailure()

所以,就只由該方法來確定了,默認(rèn)值為 true, 即是否回滾交由外層事務(wù)統(tǒng)一決定

條件得到滿足,執(zhí)行

DataSourceTransactionManager#doSetRollbackOnly

最終調(diào)用

DataSourceTransactionObject#setRollbackOnly()

內(nèi)層事務(wù)操作執(zhí)行完畢。

外層事務(wù)

外層事務(wù)中,業(yè)務(wù)代碼就捕獲了內(nèi)層所拋異常,所以該異常不會(huì)繼續(xù)往上拋,最后的事務(wù)會(huì)在 TransactionAspectSupport.invokeWithinTransaction() 中的

TransactionAspectSupport#commitTransactionAfterReturning()

該方法里執(zhí)行了commit 操作:

AbstractPlatformTransactionManager#commit

當(dāng)滿足 !shouldCommitOnGlobalRollbackOnly() &&defStatus.isGlobalRollbackOnly(),就會(huì)回滾,否則繼續(xù)提交事務(wù):

shouldCommitOnGlobalRollbackOnly()
若發(fā)現(xiàn)事務(wù)被標(biāo)記了全局回滾,且在發(fā)生全局回滾時(shí),判斷是否應(yīng)該提交事務(wù),這個(gè)方法的默認(rèn)返回 false,這里無需關(guān)注

isGlobalRollbackOnly()

該方法最終進(jìn)入

DataSourceTransactionObject#isRollbackOnly()

之前內(nèi)部事務(wù)處理最終調(diào)用到DataSourceTransactionObject#setRollbackOnly()

public void setRollbackOnly() {
   getConnectionHolder().setRollbackOnly();
}
  • isRollbackOnly()
  • setRollbackOnly()

兩個(gè)方法本質(zhì)都是對(duì)ConnectionHolder.rollbackOnly屬性標(biāo)志位的存取
但ConnectionHolder則存在于DefaultTransactionStatus#transaction屬性。

綜上:外層事務(wù)是否回滾的關(guān)鍵,最終取決于DataSourceTransactionObject#isRollbackOnly(),該方法返回值正是在內(nèi)層異常時(shí)設(shè)置的。
所以最終外層事務(wù)也被回滾,從而在控制臺(tái)中打印上述日志。

這就明白了,Spring默認(rèn)事務(wù)傳播屬性為REQUIRED:若已有事務(wù),則加入該事務(wù),若無事務(wù),則創(chuàng)建新事務(wù),因而內(nèi)外兩層事務(wù)都處于同一事務(wù)。
在 regCourse()中拋異常,并觸發(fā)回滾操作時(shí),這個(gè)回滾會(huì)繼續(xù)傳播,從而把 saveUser() 也回滾,最終整個(gè)事務(wù)都被回滾!

修正

Spring事務(wù)默認(rèn)傳播屬性 REQUIRED,在整個(gè)事務(wù)的調(diào)用鏈上,任一環(huán)節(jié)拋異常都會(huì)導(dǎo)致全局回滾。

所以只需將傳播屬性改成 REQUIRES_NEW

運(yùn)行:

異常正常拋出,注冊(cè)課程部分的數(shù)據(jù)沒有保存,但用戶還是正常注冊(cè)成功。這意味著此時(shí)Spring 只對(duì)注冊(cè)課程這部分的數(shù)據(jù)進(jìn)行了回滾,并沒有傳播到外層:

當(dāng)子事務(wù)聲明為 Propagation.REQUIRES_NEW 時(shí),在 TransactionAspectSupport.invokeWithinTransaction() 中調(diào)用 createTransactionIfNecessary() 就會(huì)創(chuàng)建一個(gè)新的事務(wù),獨(dú)立于外層事務(wù)而在 AbstractPlatformTransactionManager.processRollback() 進(jìn)行 rollback 處理時(shí),因?yàn)?status.isNewTransaction() 會(huì)因?yàn)樗幱谝粋€(gè)新的事務(wù)中而返回 true,所以它走入到了另一個(gè)分支,執(zhí)行了 doRollback() 操作,讓這個(gè)子事務(wù)單獨(dú)回滾,不會(huì)影響到主事務(wù)。

到此這篇關(guān)于淺談Spring嵌套事務(wù)是怎么回滾的的文章就介紹到這了,更多相關(guān)Spring嵌套事務(wù)回滾內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于Spring Boot對(duì)jdbc的支持問題

    關(guān)于Spring Boot對(duì)jdbc的支持問題

    這篇文章主要介紹了關(guān)于Spring Boot對(duì)jdbc的支持問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • java實(shí)現(xiàn)文件上傳的詳細(xì)步驟

    java實(shí)現(xiàn)文件上傳的詳細(xì)步驟

    文件上傳是用戶將本地文件通過Web頁面提交到服務(wù)器的過程,涉及客戶端、服務(wù)器端、上傳表單等組件,在SpringBoot中,通過MultipartFile接口處理上傳文件,并將其保存在服務(wù)器,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • 30分鐘入門Java8之方法引用學(xué)習(xí)

    30分鐘入門Java8之方法引用學(xué)習(xí)

    在Java8中,我們可以直接通過方法引用來簡寫lambda表達(dá)式中已經(jīng)存在的方法,這篇文章主要介紹了30分鐘入門Java8之方法引用學(xué)習(xí),有興趣可以了解一下。
    2017-04-04
  • 關(guān)于Java中的klass和class

    關(guān)于Java中的klass和class

    這篇文章主要介紹了關(guān)于Java中klass和class的區(qū)別,vm加載的字節(jié)碼,也就是.class文件,被加載到方法區(qū)里面,叫Kclass,是一個(gè)C++對(duì)象,含有類的信息、虛方法表等,需要的朋友可以參考下
    2023-08-08
  • Java Web開發(fā)防止多用戶重復(fù)登錄的完美解決方案

    Java Web開發(fā)防止多用戶重復(fù)登錄的完美解決方案

    在web項(xiàng)目開發(fā)中,很多情況下都可以讓同一個(gè)賬號(hào)信息在不同的登錄入口登錄很多次,這樣子做的不是很完善。一般解決這種情況有兩種解決方案,小編呢主要以第二種方式給大家介紹具體的實(shí)現(xiàn)方法,對(duì)java web 防止多用戶重復(fù)登錄的解決方案感興趣的朋友一起看看吧
    2016-11-11
  • java中sleep方法和wait方法的五個(gè)區(qū)別

    java中sleep方法和wait方法的五個(gè)區(qū)別

    這篇文章主要介紹了java中sleep方法和wait方法的五個(gè)區(qū)別,sleep?方法和?wait?方法都是用來將線程進(jìn)入休眠狀態(tài),但是又有一些區(qū)別,下面我們就一起來看看吧
    2022-05-05
  • idea新建mapper.xml文件詳細(xì)步驟如:mybatis-config

    idea新建mapper.xml文件詳細(xì)步驟如:mybatis-config

    這篇文章主要介紹了idea新建xml模板設(shè)置,例如:mybatis-config,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Java實(shí)現(xiàn)的zip壓縮及解壓縮工具類示例

    Java實(shí)現(xiàn)的zip壓縮及解壓縮工具類示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的zip壓縮及解壓縮工具類,結(jié)合實(shí)例形式分析了java對(duì)文件的進(jìn)行zip壓縮及解壓縮的具體操作技巧,需要的朋友可以參考下
    2018-01-01
  • Java微信退款開發(fā)

    Java微信退款開發(fā)

    這篇文章主要為大家詳細(xì)介紹了Java微信退款開發(fā)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn)

    SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn)

    這篇文章主要介紹了SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06

最新評(píng)論

大理市| 康定县| 祥云县| 莱阳市| 商水县| 平乐县| 确山县| 江都市| 综艺| 凤台县| 友谊县| 交口县| 和田市| 梁河县| 潮州市| 资溪县| 阳山县| 丰城市| 同德县| 虞城县| 公主岭市| 宣城市| 漳浦县| 安国市| 平果县| 西乌珠穆沁旗| 卢湾区| 巴彦淖尔市| 巨野县| 读书| 宜都市| 伊宁市| 绩溪县| 普兰县| 南康市| 铜梁县| 虎林市| 自贡市| 封丘县| 肃南| 太仓市|