<strike id="ca4is"><em id="ca4is"></em></strike>
  • <sup id="ca4is"></sup>
    • <s id="ca4is"><em id="ca4is"></em></s>
      <option id="ca4is"><cite id="ca4is"></cite></option>
    • 二維碼
      企資網

      掃一掃關注

      當前位置: 首頁 » 企資快訊 » 匯總 » 正文

      Spring事務傳播講解透徹的深度好文

      放大字體  縮小字體 發布日期:2021-08-22 01:03:23    作者:史舒文    瀏覽次數:46
      導讀

      事務傳播七種事務傳播行為詳解與示例在介紹七種事務傳播行為前,我們先設計一個場景,幫助大家理解,場景描述如下現有兩個方法A和B,方法A執行會在數據庫ATable插入一條數據,方法B執行會在數據庫BTable插入一條數據

      事務傳播

      七種事務傳播行為詳解與示例

      在介紹七種事務傳播行為前,我們先設計一個場景,幫助大家理解,場景描述如下

      現有兩個方法A和B,方法A執行會在數據庫ATable插入一條數據,方法B執行會在數據庫BTable插入一條數據,偽代碼如下:

      //將傳入參數a存入ATablepubilc void A(a){    insertIntoATable(a);    }//將傳入參數b存入BTablepublic void B(b){    insertIntoBTable(b);}

      接下來,我們看看在如下場景下,沒有事務,情況會怎樣

      public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      在這里要做一個重要提示:Spring中事務的默認實現使用的是AOP,也就是代理的方式,如果大家在使用代碼測試時,同一個Service類中的方法相互調用需要使用注入的對象來調用,不要直接使用this.方法名來調用,this.方法名調用是對象內部方法調用,不會通過Spring代理,也就是事務不會起作用

      以上偽代碼描述的一個場景,方法testMain和testB都沒有事務,執行testMain方法,那么結果會怎么樣呢?

      相信大家都知道了,就是a1數據成功存入ATable表,b1數據成功存入BTable表,而在拋出異常后b2數據存儲就不會執行,也就是b2數據不會存入數據庫,這就是沒有事務的場景。

      接下我們就開始理解七種不同事務傳播類型的含義

      REQUIRED(Spring默認的事務傳播類型)

      如果當前沒有事務,則自己新建一個事務,如果當前存在事務,則加入這個事務

      源碼說明如下:

          REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

      (示例1)根據場景舉栗子,我們在testMain和testB上聲明事務,設置傳播行為REQUIRED,偽代碼如下:

      @Transactional(propagation = Propagation.REQUIRED)public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}@Transactional(propagation = Propagation.REQUIRED)public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      該場景下執行testMain方法結果如何呢?

      數據庫沒有插入新的數據,數據庫還是保持著執行testMain方法之前的狀態,沒有發生改變。testMain上聲明了事務,在執行testB方法時就加入了testMain的事務(當前存在事務,則加入這個事務),在執行testB方法拋出異常后事務會發生回滾,又testMain和testB使用的同一個事務,所以事務回滾后testMain和testB中的操作都會回滾,也就使得數據庫仍然保持初始狀態

      (示例2)根據場景再舉一個栗子,我們只在testB上聲明事務,設置傳播行為REQUIRED,偽代碼如下:

      public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}@Transactional(propagation = Propagation.REQUIRED)public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      這時的執行結果又如何呢?

      數據a1存儲成功,數據b1和b2沒有存儲。由于testMain沒有聲明事務,testB有聲明事務且傳播行為是REQUIRED,所以在執行testB時會自己新建一個事務(如果當前沒有事務,則自己新建一個事務),testB拋出異常則只有testB中的操作發生了回滾,也就是b1的存儲會發生回滾,但a1數據不會回滾,所以最終a1數據存儲成功,b1和b2數據沒有存儲

      SUPPORTS

      當前存在事務,則加入當前事務,如果當前沒有事務,就以非事務方法執行

      源碼注釋如下(太長省略了一部分),其中里面有一個提醒翻譯一下就是:“對于具有事務同步的事務管理器,SUPPORTS與完全沒有事務稍有不同,因為她定義了可能應用同步的事務范圍”。這個是與事務同步管理器相關的一個注意項,這里不過多討論。

          SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

      (示例3)根據場景舉栗子,我們只在testB上聲明事務,設置傳播行為SUPPORTS,偽代碼如下:

      public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}@Transactional(propagation = Propagation.SUPPORTS)public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      這種情況下,執行testMain的最終結果就是,a1,b1存入數據庫,b2沒有存入數據庫。由于testMain沒有聲明事務,且testB的事務傳播行為是SUPPORTS,所以執行testB時就是沒有事務的(如果當前沒有事務,就以非事務方法執行),則在testB拋出異常時也不會發生回滾,所以最終結果就是a1和b1存儲成功,b2沒有存儲。

      那么當我們在testMain上聲明事務且使用REQUIRED傳播方式的時候,這個時候執行testB就滿足當前存在事務,則加入當前事務,在testB拋出異常時事務就會回滾,最終結果就是a1,b1和b2都不會存儲到數據庫

      MANDATORY

      當前存在事務,則加入當前事務,如果當前事務不存在,則拋出異常。

      源碼注釋如下:

          MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

      (示例4)場景舉栗子,我們只在testB上聲明事務,設置傳播行為MANDATORY,偽代碼如下:

      public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}@Transactional(propagation = Propagation.MANDATORY)public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      這種情形的執行結果就是a1存儲成功,而b1和b2沒有存儲。b1和b2沒有存儲,并不是事務回滾的原因,而是因為testMain方法沒有聲明事務,在去執行testB方法時就直接拋出事務要求的異常(如果當前事務不存在,則拋出異常),所以testB方法里的內容就沒有執行。

      那么如果在testMain方法進行事務聲明,并且設置為REQUIRED,則執行testB時就會使用testMain已經開啟的事務,遇到異常就正常的回滾了。

      REQUIRES_NEW

      創建一個新事務,如果存在當前事務,則掛起該事務。

      可以理解為設置事務傳播類型為REQUIRES_NEW的方法,在執行時,不論當前是否存在事務,總是會新建一個事務。

      源碼注釋如下

          REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

      (示例5)場景舉栗子,為了說明設置REQUIRES_NEW的方法會開啟新事務,我們把異常發生的位置換到了testMain,然后給testMain聲明事務,傳播類型設置為REQUIRED,testB也聲明事務,設置傳播類型為REQUIRES_NEW,偽代碼如下

      @Transactional(propagation = Propagation.REQUIRED)public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB    throw Exception;     //發生異常拋出}@Transactional(propagation = Propagation.REQUIRES_NEW)public void testB(){    B(b1);  //調用B入參b1    B(b2);  //調用B入參b2}

      這種情形的執行結果就是a1沒有存儲,而b1和b2存儲成功,因為testB的事務傳播設置為REQUIRES_NEW,所以在執行testB時會開啟一個新的事務,testMain中發生的異常時在testMain所開啟的事務中,所以這個異常不會影響testB的事務提交,testMain中的事務會發生回滾,所以最終a1就沒有存儲,而b1和b2就存儲成功了。

      與這個場景對比的一個場景就是testMain和testB都設置為REQUIRED,那么上面的代碼執行結果就是所有數據都不會存儲,因為testMain和testMain是在同一個事務下的,所以事務發生回滾時,所有的數據都會回滾

      NOT_SUPPORTED

      始終以非事務方式執行,如果當前存在事務,則掛起當前事務

      可以理解為設置事務傳播類型為NOT_SUPPORTED的方法,在執行時,不論當前是否存在事務,都會以非事務的方式運行。

      源碼說明如下

          NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

      (示例6)場景舉栗子,testMain傳播類型設置為REQUIRED,testB傳播類型設置為NOT_SUPPORTED,且異常拋出位置在testB中,偽代碼如下

      @Transactional(propagation = Propagation.REQUIRED)public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}@Transactional(propagation = Propagation.NOT_SUPPORTED)public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      該場景的執行結果就是a1和b2沒有存儲,而b1存儲成功。testMain有事務,而testB不使用事務,所以執行中testB的存儲b1成功,然后拋出異常,此時testMain檢測到異常事務發生回滾,但是由于testB不在事務中,所以只有testMain的存儲a1發生了回滾,最終只有b1存儲成功,而a1和b1都沒有存儲

      NEVER

      不使用事務,如果當前事務存在,則拋出異常

      很容易理解,就是我這個方法不使用事務,并且調用我的方法也不允許有事務,如果調用我的方法有事務則我直接拋出異常。

      源碼注釋如下:

          NEVER(TransactionDefinition.PROPAGATION_NEVER),

      (示例7)場景舉栗子,testMain設置傳播類型為REQUIRED,testB傳播類型設置為NEVER,并且把testB中的拋出異常代碼去掉,則偽代碼如下

      @Transactional(propagation = Propagation.REQUIRED)public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB}@Transactional(propagation = Propagation.NEVER)public void testB(){    B(b1);  //調用B入參b1    B(b2);  //調用B入參b2}

      該場景執行,直接拋出事務異常,且不會有數據存儲到數據庫。由于testMain事務傳播類型為REQUIRED,所以testMain是運行在事務中,而testB事務傳播類型為NEVER,所以testB不會執行而是直接拋出事務異常,此時testMain檢測到異常就發生了回滾,所以最終數據庫不會有數據存入。

      NESTED

      如果當前事務存在,則在嵌套事務中執行,否則REQUIRED的操作一樣(開啟一個事務)

      這里需要注意兩點:

    • 和REQUIRES_NEW的區別

      REQUIRES_NEW是新建一個事務并且新開啟的這個事務與原有事務無關,而NESTED則是當前存在事務時(我們把當前事務稱之為父事務)會開啟一個嵌套事務(稱之為一個子事務)。
      在NESTED情況下父事務回滾時,子事務也會回滾,而在REQUIRES_NEW情況下,原有事務回滾,不會影響新開啟的事務。

    • 和REQUIRED的區別

      REQUIRED情況下,調用方存在事務時,則被調用方和調用方使用同一事務,那么被調用方出現異常時,由于共用一個事務,所以無論調用方是否catch其異常,事務都會回滾
      而在NESTED情況下,被調用方發生異常時,調用方可以catch其異常,這樣只有子事務回滾,父事務不受影響

      (示例8)場景舉栗子,testMain設置為REQUIRED,testB設置為NESTED,且異常發生在testMain中,偽代碼如下

      @Transactional(propagation = Propagation.REQUIRED)public void testMain(){    A(a1);  //調用A入參a1    testB();    //調用testB    throw Exception;     //發生異常拋出}@Transactional(propagation = Propagation.NESTED)public void testB(){    B(b1);  //調用B入參b1    B(b2);  //調用B入參b2}

      該場景下,所有數據都不會存入數據庫,因為在testMain發生異常時,父事務回滾則子事務也跟著回滾了,可以與(示例5)比較看一下,就找出了與REQUIRES_NEW的不同

      (示例9)場景舉栗子,testMain設置為REQUIRED,testB設置為NESTED,且異常發生在testB中,偽代碼如下

      @Transactional(propagation = Propagation.REQUIRED)public void testMain(){    A(a1);  //調用A入參a1    try{        testB();    //調用testB    }catch(Exception e){    }    A(a2);}@Transactional(propagation = Propagation.NESTED)public void testB(){    B(b1);  //調用B入參b1    throw Exception;     //發生異常拋出    B(b2);  //調用B入參b2}

      這種場景下,結果是a1,a2存儲成功,b1和b2存儲失敗,因為調用方catch了被調方的異常,所以只有子事務回滾了。

      同樣的代碼,如果我們把testB的傳播類型改為REQUIRED,結果也就變成了:沒有數據存儲成功。就算在調用方catch了異常,整個事務還是會回滾,因為,調用方和被調方共用的同一個事務

      轉自于:https://zhuanlan.zhihu.com/p/148504094

    •  
      (文/史舒文)
      免責聲明
      本文僅代表作發布者:史舒文個人觀點,本站未對其內容進行核實,請讀者僅做參考,如若文中涉及有違公德、觸犯法律的內容,一經發現,立即刪除,需自行承擔相應責任。涉及到版權或其他問題,請及時聯系我們刪除處理郵件:weilaitui@qq.com。
       

      Copyright ? 2016 - 2025 - 企資網 48903.COM All Rights Reserved 粵公網安備 44030702000589號

      粵ICP備16078936號

      微信

      關注
      微信

      微信二維碼

      WAP二維碼

      客服

      聯系
      客服

      聯系客服:

      在線QQ: 303377504

      客服電話: 020-82301567

      E_mail郵箱: weilaitui@qq.com

      微信公眾號: weishitui

      客服001 客服002 客服003

      工作時間:

      周一至周五: 09:00 - 18:00

      反饋

      用戶
      反饋

      午夜久久久久久网站,99久久www免费,欧美日本日韩aⅴ在线视频,东京干手机福利视频
        <strike id="ca4is"><em id="ca4is"></em></strike>
      • <sup id="ca4is"></sup>
        • <s id="ca4is"><em id="ca4is"></em></s>
          <option id="ca4is"><cite id="ca4is"></cite></option>
        • 主站蜘蛛池模板: 欧美成人精品第一区首页| 97一区二区三区四区久久| 99久久er热在这里只有精品99| 精品视频一区二区观看| 成在人线AV无码免费| 四虎高清成人永久免费影院| 中文字幕激情视频| 色综合久久天天影视网| 挺进男同的屁股眼o漫画| 国产一区曰韩二区欧美三区| 中文字幕欧美日韩在线不卡| 老司机成人影院| 强行扒开双腿猛烈进入| 免费国产一级特黄久久| a毛片成人免费全部播放| 波多野结衣痴汉电车| 国产裸体歌舞一区二区| 亚洲伊人精品综合在合线| 国产精品久久久久鬼色| 日本高清不卡在线| 国产ts人妖视频| 一二三四在线观看高清| 狠狠久久永久免费观看| 国产经典三级在线| 亚洲ts人妖网站| 邱淑芬一家交换| 成人小视频在线观看| 免费一级毛片完整版在线看| 999国产精品999久久久久久| 欧美最猛性xxxx| 国产成人免费全部网站| 中文字幕日韩在线观看| 真实国产乱子伦在线观看| 国内精品久久人妻无码不卡| 亚洲人成7777影视在线观看| 高潮毛片无遮挡高清免费| 故意短裙公车被强好爽在线播放| 冬日恋歌国语版20集中文版| 999国产精品999久久久久久| 极品丝袜乱系列大全集目录| 国产一级视频播放|