<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>

          Spring是如何管理事務(wù)的之@Transactional注解詳解

          共 4171字,需瀏覽 9分鐘

           ·

          2022-08-11 23:38

          往期熱門文章:
          1、去騰訊面試,直接讓人出門左拐 :冪等性都不知道!
          2、面試官:如果要存 IP 地址,用什么數(shù)據(jù)類型比較好?99%人都會(huì)答錯(cuò)!
          3、面試官:為什么不能將實(shí)數(shù)作為 HashMap 的 key?
          4、Redis 官方可視化工具,高顏值,功能太強(qiáng)大!
          5、不好意思, Maven 該換了!

          背景


          前兩天在工作中忙的焦頭爛額,涉及到 @Transactional 對(duì)于事務(wù)的控制,便仔細(xì)研究了一下,頗有所獲,花費(fèi)好了幾天測(cè)試整理,今天才發(fā)表出來,希望老鐵們能有所獲吧。

          Spring 事務(wù)的傳播行為


          話不多說直奔正題。先簡(jiǎn)單介紹一下 Spring 事務(wù)的傳播行為。

          所謂事務(wù)的傳播行為是指,如果在開始當(dāng)前事務(wù)之前,一個(gè)事務(wù)上下文已經(jīng)存在,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為。

          在 TransactionDefinition 定義中包括了如下幾個(gè)表示傳播行為的常量:
          • TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。這是默認(rèn)值。
          • TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
          • TransactionDefinition.PROPAGATION_SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
          • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
          • TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
          • TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
          • TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。

          Spring事務(wù)的回滾機(jī)制


          然后說一下Spring事務(wù)的回滾機(jī)制:Spring 的 AOP 即聲明式事務(wù)管理默認(rèn)是針對(duì) unchecked exception 回滾。

          Spring 的事務(wù)邊界是在調(diào)用業(yè)務(wù)方法之前開始的,業(yè)務(wù)方法執(zhí)行完畢之后來執(zhí)行 commit or rollback(Spring 默認(rèn)取決于是否拋出 runtimeException)。

          如果你在方法中有 try{}catch(Exception e){} 處理,那么 try 里面的代碼塊就脫離了事務(wù)的管理,若要事務(wù)生效需要在 catch 中 throw new RuntimeException ("xxxxxx"); 這一點(diǎn)也是面試中會(huì)問到的事務(wù)失效的場(chǎng)景。

          @Transactional 注解底層實(shí)現(xiàn)


          再簡(jiǎn)單介紹一下 @Transactional 注解底層實(shí)現(xiàn)方式吧,毫無疑問,是通過動(dòng)態(tài)代理,那么動(dòng)態(tài)代理又分為 JDK 自身和 CGLIB,這個(gè)也不多贅述了,畢竟今天的主題是如何將 @Transactional 對(duì)于事物的控制應(yīng)用到爐火純青。哈哈~

          第一點(diǎn)要注意的就是在 @Transactional 注解的方法中,再調(diào)用本類中的其他方法 method2 時(shí),那么 method2 方法上的 @Transactional 注解是不!會(huì)!生!效!的!

          但是加上也并不會(huì)報(bào)錯(cuò),拿圖片簡(jiǎn)單幫助理解一下吧。這一點(diǎn)也是面試中會(huì)問到的事務(wù)失效的場(chǎng)景。
          通過代理對(duì)象在目標(biāo)對(duì)象前后進(jìn)行方法增強(qiáng),也就是事務(wù)的開啟提交和回滾。那么繼續(xù)調(diào)用本類中其他方法是怎樣呢?

          如下圖:
          可見目標(biāo)對(duì)象內(nèi)部的自我調(diào)用,也就是通過 this. 指向的目標(biāo)對(duì)象將不會(huì)執(zhí)行方法的增強(qiáng)。

          先說第二點(diǎn)需要注意的地方,等下說如何解決上面第一點(diǎn)的問題。第二點(diǎn)就是 @Transactional 注解的方法必須是公共方法,就是必須是 public 修飾符!!!

          至于這個(gè)的原因,發(fā)表下個(gè)人的理解吧,因?yàn)?JVM 的動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,通過代理類將目標(biāo)方法進(jìn)行增強(qiáng)。

          想一下也是啦,沒有權(quán)限訪問那么你讓我怎么進(jìn)行?好吧,這個(gè)我也沒有深入研究底層,個(gè)人理解個(gè)人理解。

          在這里我也放個(gè)問題吧,希望有高手可以回復(fù)指點(diǎn)指點(diǎn)我,因?yàn)?JVM 動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,那么是不是 service 層都要按照接口和實(shí)現(xiàn)類的開發(fā)模式,注解才會(huì)生效呢。

          就是說 controller 層直接調(diào)用沒有接口的 service 層,加了注解也一樣不起作用吧,這個(gè)懶了,沒有測(cè)試,其一是因?yàn)闆]有人會(huì)這么開發(fā)吧,其二是我就認(rèn)為是不起作用的,哈哈。

          下面來解決一下第一點(diǎn)的問題,如何在方法中調(diào)用本類中其他方法呢。

          通過 AopContext.currentProxy () 獲取到本類的代理對(duì)象,再去調(diào)用就好啦。

          因?yàn)檫@個(gè)是 CGLIB 實(shí)現(xiàn),所以要開啟 AOP,當(dāng)然也很簡(jiǎn)單,在 springboot 啟動(dòng)類上加上注解 @EnableAspectJAutoProxy(exposeProxy = true) 就可以啦,這個(gè)依賴大家自行搜一下就好啦。

          要注意,注意,代理對(duì)象調(diào)用的方法也要是 public 修飾符,否則方法中獲取不到注入的 bean,會(huì)報(bào)空指針錯(cuò)誤。

          emmmm,我先把調(diào)用的方式和結(jié)果說下吧。自己簡(jiǎn)單寫了代碼,有點(diǎn)粗糙,就不要介意啦,嘿嘿!

          Controller 中調(diào)用 Service:
          @RestController
          public class TransactionalController {

              @Autowired
              private TransactionalService transactionalService;

              @PostMapping("transactionalTest")
              public void transacionalTest(){
                  transactionalService.transactionalMethod();
              }
          }

          Service 中實(shí)現(xiàn)對(duì)事務(wù)的控制:接口
          public interface TransactionalService {
              void transactionalMethod();
          }

          Service 中實(shí)現(xiàn)對(duì)事務(wù)的控制:實(shí)現(xiàn)類(各種情況的說明都寫在圖片里了,這樣方便閱讀,有助于快速理解吧)
          上面兩種情況不管使不使用代理調(diào)用方法 1 和方法 2,方法 transactionalMethod 都處在一個(gè)事務(wù)中,四條更新操作全部失敗。

          那么有人可能會(huì)有疑問了,在方法 1 和方法 2 上都加 @Transactional 注解呢?答案是結(jié)果和上面是一致的。

          小結(jié):只要方法 transactionalMethod 上有注解,并且方法 1 和方法 2 都處于當(dāng)前事務(wù)中(不使用代理調(diào)用,方法 1 和方法 2 上的 @Transactional 注解是不生效的;使用代理,需要方法 1 和方法 2 都處在 transactionalMethod 方法的事務(wù)中,默認(rèn)或者嵌套事務(wù)均可,當(dāng)然也可以不加 @Transactional 注解),那么整體保持事務(wù)一致性。

          如果想要方法 1 和方法 2 均單獨(dú)保持事務(wù)一致性怎么辦呢,剛說過了,如果不是用代理調(diào)用 @Transactional 注解是不生效的,所以一定要使用代理調(diào)用實(shí)現(xiàn),然后讓方法 1 和方法 2 分別單獨(dú)開啟新的事務(wù),便 OK 啦。

          下面擺上圖片:
          這兩種情況都是方法 1 和方法 2 均處在單獨(dú)的事務(wù)中,各自保持事務(wù)的一致性。

          接下來進(jìn)行進(jìn)一步的優(yōu)化,可以在 transactionalMethod 方法中分別對(duì)方法 1 和方法 2 進(jìn)行控制。

          要將代碼的藝術(shù)發(fā)揮到極致嘛,下面裝逼開始。
          代碼太長(zhǎng)了,超過屏幕了,粘貼出來截的圖,紅框注釋需要仔細(xì)看,希望不要影響你的閱讀體驗(yàn),至此,本篇關(guān)于 @Transactioinal 注解的使用就到此為止啦,

          總結(jié)


          簡(jiǎn)單總結(jié)一下吧:
          • 就是 @Transactional 注解保證的是每個(gè)方法處在一個(gè)事務(wù),如果有 try 一定在 catch 中拋出運(yùn)行時(shí)異常。

          • 方法必須是 public 修飾符。否則注解不會(huì)生效,但是加了注解也沒啥毛病,不會(huì)報(bào)錯(cuò),只是沒卵用而已。

          • this. 本方法的調(diào)用,被調(diào)用方法上注解是不生效的,因?yàn)闊o法再次進(jìn)行切面增強(qiáng)。


          轉(zhuǎn)自:范學(xué)博

          鏈接:https://blog.csdn.net/fanxb92/article/details/81296005

          最近熱文閱讀:

          1、面試官:如果要存 IP 地址,用什么數(shù)據(jù)類型比較好?99%人都會(huì)答錯(cuò)!
          2、面試官:為什么不能將實(shí)數(shù)作為 HashMap 的 key?
          3、Redis 官方可視化工具,高顏值,功能太強(qiáng)大!
          4、不好意思, Maven 該換了!
          5、面試官 | Spring Boot 項(xiàng)目如何統(tǒng)一結(jié)果,統(tǒng)一異常,統(tǒng)一日志?
          6、基于SpringBoot+MyBatis+Vue的音樂網(wǎng)站
          7、聊聊接口優(yōu)化的幾種方法
          8、面試官 | Spring Boot 項(xiàng)目如何統(tǒng)一結(jié)果,統(tǒng)一異常,統(tǒng)一日志?
          9、為什么不建議使用ON DUPLICATE KEY UPDATE?
          10、Java8 Stream,過分絲滑!
          關(guān)注公眾號(hào),你想要的Java都在這里

          瀏覽 70
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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 |