<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么catch了異常,但事務(wù)還是回滾了?

          共 5776字,需瀏覽 12分鐘

           ·

          2021-07-04 11:03


          前幾天我發(fā)了這篇文章《我來出個題:這個事務(wù)會不會回滾?》(https://blog.didispace.com/will-this-transcation-rollback/)

          得到了很多不錯的反饋,也有不少讀者通過微信、群或者郵件的方式,給了我一些關(guān)于test4的回復(fù)。其中還有直接發(fā)給我測試案例,來證明我的答案是錯的。

          今天,我們就來一起看看test4這個爭議很大的問題。如果您是剛打開這篇文章,不了解我們在討論啥,那可以先點擊查看之前的這篇《我來出個題:這個事務(wù)會不會回滾?》(https://blog.didispace.com/will-this-transcation-rollback/)。

          通過這兩篇文章的解析,相信你會對Spring Data JPA下的事務(wù)執(zhí)行機制有質(zhì)的飛躍。

          為什么沒回滾

          先來說說,那些寫了代碼驗證"不會回滾"的情況,把這些錯誤答案的原因先說清楚,然后再細說test4會回滾的情況。

          根據(jù)這兩天讀者給我的案例或者描述清楚的一些情況,歸結(jié)了一下,大家寫的驗證代碼之所以不會回滾,主要有以下三個原因:

          1. 沒有按照我題目開頭說的,采用InnoDB存儲引擎,用了MyISAM,不支持事務(wù),自然不會復(fù)現(xiàn)。
          2. 沒用按照我題目開頭說的,采用JPA和JSR 303校驗注解,比如:用了MyBaits,所以自然也不會復(fù)現(xiàn)。
          3. 定義事務(wù)的函數(shù)不是public類型,這個基礎(chǔ)用法就不對了,事務(wù)本身就沒生效

          歸家一下出現(xiàn)這些疑問的原因:沒審題事務(wù)基礎(chǔ)掌握不牢導(dǎo)致。關(guān)于事務(wù)基礎(chǔ)使用的一些常見注意點,之前寫過一篇文章,如果覺得這方面知識還不扎實的,建議讀一讀:《為什么加了@Transactional注解,事務(wù)沒有回滾?》(https://blog.didispace.com/transactional-not-rollback/)

          為什么寫了catch,還會回滾

          先來看看執(zhí)行時候報的異常:

          javax.validation.ConstraintViolationException: Validation failed for classes [com.didispace.chapter310.User] during persist time for groups [javax.validation.groups.Default, ]
          List of constraint violations:[
           ConstraintViolationImpl{interpolatedMessage='個數(shù)必須在0和5之間', propertyPath=name, rootBeanClass=class com.didispace.chapter310.User, messageTemplate='{javax.validation.constraints.Size.message}'}
          ]
           at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:209) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:83) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
           at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]

          這個異常是這個回滾的關(guān)鍵。這個異常javax.validation.ConstraintViolationException是哪里的呢?還記得以前說的JSR 303不?對的,是Bean Validation中的異常。

          有的讀者說這個不是RuntimeException,所以不會回滾。很顯然,這類判斷的都沒有實際嘗試一下,只要點開源碼可以馬上發(fā)現(xiàn),這個異常就是屬于RunTimeException的。

          實際上,之所以會回滾,與這里使用Spring Data JPA以及Hibernate Validator有直接關(guān)系。從JPA 2.0開始,就默認支持了這些Bean Validation的實現(xiàn),它提供了實體生命周期中pre-persistpre-update,pre-remove三個事件發(fā)生時來執(zhí)行校驗的功能。而在校驗的時候,當(dāng)校驗失敗,拋出javax.validation.ConstraintViolationException時,當(dāng)前事務(wù)就會被標(biāo)記為rollback

          源碼解析

          要想了解,這其中到底發(fā)生了什么,跟蹤源碼是最好的方式。那么源碼從哪里開始看呢?從異常日志中找線索吧。


          從異常棧中找到最近的一個錯誤,點開看看。

          錯誤行數(shù)在532行tx.commit(),習(xí)慣性的加上斷點,這樣下一次進來的時候可以看看當(dāng)前情況下的各種參數(shù)情況。

          同時看到下面還有個catch,既然532行出錯了,那這里肯定會進,所以也加個端點,到時候可以進去看看。

          執(zhí)行程序,調(diào)用一下test4,執(zhí)行到532行,然后進入下一步,看看會到哪里?

          這個時候,會進入到org.hibernate.engine.transaction.internal.TransactionImpl,具體位置如下:

          還是習(xí)慣性的,在下面兩行重要位置加上斷點,以便下次可以快速到這里。

          繼續(xù)按上看的步驟嘗試下去,可以來到下圖的位置:

          可以看到校驗異常是從271行出來的,結(jié)合278行和280行,是不是清楚這里回滾的原因了呢?

          小結(jié)

          當(dāng)我把上一篇問題推到很多地方之后,其實還是收到了不少負面的反饋,甚至還有說我誤導(dǎo)讀者,順便問候了下我的祖宗。這些我就不跟愛噴的讀者互杠了,我是一直都推崇碰到問題,盡量多深挖一些的學(xué)習(xí)方式。雖然有的時候出現(xiàn)問題,確實是由于不恰當(dāng)?shù)膶懛▽?dǎo)致,但如果你沒有理解這個錯誤的原因,本質(zhì)還是對其底層邏輯不夠了解。如果你能從問題中找到線索,并順藤摸瓜地探究和思考,你能夠收獲到的東西,遠比噴我來的強。

          實踐出真知,當(dāng)你覺得困惑的時候,不如動手寫一寫,調(diào)一調(diào),很多答案就能自然浮現(xiàn)!如果對于test4會回滾還不夠理解的讀者,那就跟著我上面的步驟,一步步嘗試一下,可以觀察的更深入一些,你對這部分邏輯的理解就更全面了。

          我們正在組建高質(zhì)量的Spring技術(shù)交流群,最近這個分享的源頭也是來源于群里的一次討論。歡迎各種熱愛技術(shù)的開發(fā)者加入?yún)⑴c討論。這里的每個人都有自己的閃光點,互相學(xué)習(xí),取長補短,長期堅持,愿大家都會成為自己領(lǐng)域里的佼佼者!

          往期推薦

          騷操作!阿里云直接買www.huaweicloud.com的關(guān)鍵詞來搶生意?

          Dubbo 3.0.0正式發(fā)布:應(yīng)用級服務(wù)注冊,跨語言的RPC協(xié)議、更好支持Kubernetes!

          我來出個題:這些事務(wù)會不會回滾?大概率你會錯!

          警惕 Spring Boot Actuator 引發(fā)的安全問題

          來活兒了!趕緊檢查下代碼里有沒有臟話...


          關(guān)注我回復(fù)「加群」,加入Spring技術(shù)交流群

          原創(chuàng)不易,如果您覺得本文不錯

          歡迎轉(zhuǎn)發(fā)到朋友圈,支持一下我的努力吧!

          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花无码AV在线 | 天天色天天日 | 色黄视频网站 | 久久久九九九 | 狠狠V日韩V欧美 |