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

          手把手利用實(shí)戰(zhàn)代碼帶你讀懂Spring的事務(wù)傳播行為

          共 14080字,需瀏覽 29分鐘

           ·

          2021-09-21 16:59

          概念

          首先簡(jiǎn)單了解一下 Spring 中事務(wù)傳播行為是什么?聽起來(lái)很高端,但是真正用起來(lái)的時(shí)候,稍有不慎,就會(huì)讓自己陷入困境之中,所以在使用之前,我們必須要十分耐心認(rèn)真的學(xué)習(xí)它。

          從名字理解起來(lái),事務(wù)傳播行為,既然為傳播就肯定發(fā)生在兩個(gè)實(shí)體之間,否則單個(gè)實(shí)體又如何發(fā)生行為呢。通俗點(diǎn)講就是“一個(gè)巴掌拍不響”。下面進(jìn)入正規(guī)話題。

          ?

          事務(wù)傳播行為主要用來(lái)描述由某一個(gè)事務(wù)傳播行為修飾的方法被嵌套進(jìn)另一個(gè)方法的事務(wù)中,該事務(wù)如何傳播。這個(gè)概述可能不好理解,換句話就是當(dāng)一個(gè)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),這個(gè)事務(wù)方法應(yīng)該如何進(jìn)行。

          ?

          下面用代碼+文字說(shuō)明解釋上面的概念。

          @Transaction(Propagation=XXX)
          public void methodA(){
              methodB();
              //doSomething
           }
           
           @Transaction(Propagation=XXX)
           public void methodB(){
              //doSomething
           }

          methodA 事務(wù)方法調(diào)用 methodB 事務(wù)方法時(shí),methodB 是繼續(xù)在調(diào)用者 methodA 的事務(wù)中運(yùn)行呢,還是為自己開啟一個(gè)新事務(wù)運(yùn)行,這就是由 methodB 的事務(wù)傳播行為決定的。

          注意:methodA 和 methodB 都加了事務(wù)。methodA()也可以不用開啟事務(wù),某一個(gè)事務(wù)傳播行為修飾的方法并不是必須要在開啟事務(wù)的外圍方法中調(diào)用。

          Spring 中七種事務(wù)傳播行為

          通過(guò)上面?zhèn)未a加文字解釋了解到事務(wù)傳播行為的相關(guān)概念,下面就要學(xué)習(xí)事務(wù)傳播行為的類型和運(yùn)行機(jī)制。

          Spring 中七種事務(wù)傳播行為

          驗(yàn)證

          Propagation_Required

          調(diào)用者方法不存在事務(wù)傳播行為

          「1.調(diào)用者方法內(nèi)部存在異常時(shí),被調(diào)用者方法均存在事務(wù),那么結(jié)果如何呢?」

          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }

          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertClass(ClassDo classDo
          {
              classMapper.insertClass(classDo);
              System.out.println("----------------------->Class插入成功!");
          }

          單元測(cè)試

          // 單元測(cè)試
          @SpringBootTest
          @RunWith(SpringRunner.class)
          public class PropagationTest 
          {

              private final static StudentDo studentDo = new StudentDo();

              private final static ClassDo classDo = new ClassDo();
              
              static {
                  studentDo.setClassId(1);
                  studentDo.setStudentName("student1");
                  studentDo.setAddress("測(cè)試");

                  classDo.setClassName("class_1");
                  classDo.setClassNo("Class01");
              }

              @Autowired
              private StudentService studentService;

              @Autowired
              private ClassService classService;

              @Test
              public void insertTest() {
                  studentService.insertStudent(studentDo);
                  classService.insertClass(classDo);
                  
              }
          }
          Propagation_Required事務(wù)傳播機(jī)制

          結(jié)果:兩條數(shù)據(jù)均被插入數(shù)據(jù)庫(kù)。由于外部方法并沒有開啟事務(wù),所以內(nèi)部方法均在自己的事務(wù)提交或者回滾,因此外部方法中存在異常,內(nèi)部方法事務(wù)不會(huì)回滾。

          「2.被調(diào)用者均存在事務(wù),而在被調(diào)用者中存在異常,那么結(jié)果如何?」

          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }

          //此方法中拋出異常
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertClassByException(ClassDo classDothrows CustomException 
          {
              classMapper.insertClass(classDo);
              throw new CustomException();
          }

          單元測(cè)試代碼

          //單元測(cè)試代碼
          private final static StudentDo studentDo = new StudentDo();
          private final static ClassDo classDo = new ClassDo();
          static {
              studentDo.setClassId(2);
              studentDo.setStudentName("student2");
              studentDo.setAddress("測(cè)試2");

              classDo.setClassName("class_2");
              classDo.setClassNo("Class02");
          }

          @Test
          public void insertExceptionTest() throws CustomException {
              studentService.insertStudent(studentDo);
              classService.insertClassByException(classDo);
          }
          Propagation_Required

          結(jié)果:第一數(shù)據(jù)成功插入,第二條數(shù)據(jù)因異常存在,事務(wù)回滾。內(nèi)部方法均在各個(gè)的事務(wù)中運(yùn)行,class 事務(wù)回滾,student 數(shù)據(jù)不會(huì)受到影響。

          事務(wù)回滾

          結(jié)合 1 和 2 我們可以得出結(jié)論:

          1. 通過(guò)這兩個(gè)方法我們證明了在外圍方法未開啟事務(wù)的情況下Propagation_Required修飾的內(nèi)部方法會(huì)新開啟自己的事務(wù),且開啟的事務(wù)相互獨(dú)立,互不干擾。

          2. 調(diào)用者開啟事務(wù)傳播行為。

          內(nèi)部方法同上

          //單元測(cè)試方法
          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertInnerExceptionThrowsTest() throws CustomException 
          {
              studentService.insertStudent(studentDo);
              classService.insertClassByException(classDo);
          }

          結(jié)果:內(nèi)部方法雖然存在事務(wù)傳播行為,但是外部方法也存在事務(wù)且使用Propagation.REQUIRED修飾,所有內(nèi)部方法不會(huì)新建事務(wù),直接運(yùn)行在當(dāng)前事務(wù)中,所以 student、class 均會(huì)被回滾。

          1. 調(diào)用者開啟事務(wù)傳播行為,但是捕獲內(nèi)部方法異常。
          /**
           * 內(nèi)部方法發(fā)生異常情況,外部方法即使捕獲處理該異常,依然數(shù)據(jù)會(huì)被回滾
           */

          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertInnerExceptionTest() 
          {
              studentService.insertStudent(studentDo);
              try {
                  classService.insertClassByException(classDo);
              } catch (CustomException e) {
                  e.printStackTrace();
              }
          }

          結(jié)果:外圍方法開啟事務(wù),內(nèi)部方法加入外圍方法事務(wù),內(nèi)部方法拋出異常回滾,即使方法被 catch 不被外圍方法感知,整個(gè)事務(wù)依然回滾。同 2 一樣,調(diào)用者方法執(zhí)行操作和被調(diào)用者中的方法操作結(jié)果均被回滾。

          Propagation_Supports

          @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }

          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertSupportsTest() 
          {
              studentService.insertStudent(studentDo);
          }

          解釋:如果單純的調(diào)用insertStudent()方法,則以非事務(wù)執(zhí)行,即使后面存在異常情況,執(zhí)行操作結(jié)果不會(huì)觸發(fā)事務(wù)回滾機(jī)制。當(dāng)調(diào)用insertSupportsTest()方法時(shí),該方法以 REQUIRED 修飾,則會(huì)新建一個(gè)事務(wù),內(nèi)部調(diào)用insertStudent()方法,所以insertStudent()會(huì)加入到當(dāng)前事務(wù)中執(zhí)行。

          Propagation_Mandatory

          @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }

          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertSupportsTest() 
          {
              studentService.insertStudent(studentDo);
          }
          Propagation_Mandatory

          解釋結(jié)果:MANDATORY 表示被修飾的方法必須在事務(wù)中運(yùn)行。當(dāng)單獨(dú)調(diào)用 insertStudent 時(shí),因?yàn)楫?dāng)前沒有一個(gè)活動(dòng)的事務(wù),則會(huì)拋出異常。

          throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);

          當(dāng)調(diào)用 insertSupportsTest 時(shí),insertStudent 則加入到 insertSupportsTest 的事務(wù)中,事務(wù)地執(zhí)行。

          Propagation_Required_New

          表示被修飾的方法必須運(yùn)行在它自己的事務(wù)中。一個(gè)新的事務(wù)會(huì)被啟動(dòng)。如果調(diào)用者存在當(dāng)前事務(wù),則在該方法執(zhí)行期間,當(dāng)前事務(wù)會(huì)被掛起。

          private final static StudentDo studentDo = new StudentDo();
          private final static ClassDo classDo = new ClassDo();
          static {
              studentDo.setClassId(2);
              studentDo.setStudentName("requireNew");
              studentDo.setAddress("requireNew");
          }
          @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }
          @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
          public void insertClassByException(ClassDo classDothrows CustomException 
          {
              classMapper.insertClass(classDo);
              throw new CustomException();
          }

          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertInnerExceptionTest() 
          {
              studentService.insertStudent(studentDo);
              try {
                  classService.insertClassByException(classDo);
              } catch (CustomException e) {
                  e.printStackTrace();
              }
          }

          結(jié)果解析:insertStudent(),insertClassByException() 方法執(zhí)行時(shí),外部方法事務(wù)被掛起,內(nèi)部方法會(huì)新建事務(wù),直至該方法執(zhí)行結(jié)束,恢復(fù)外部方法事務(wù)執(zhí)行。兩者之間事務(wù)存在隔離性,insertClassByException() 方法遇到異常,觸發(fā)事務(wù)回滾機(jī)制,但 insertStudent() 執(zhí)行結(jié)果并受到影響。

          如圖所示:

          觸發(fā)事務(wù)回滾機(jī)制
          Propagation_Required_New

          Propagation_Not_Supported

          表示被修飾的方法不應(yīng)該運(yùn)行在事務(wù)中。如果調(diào)用者存在當(dāng)前事務(wù),則該方法運(yùn)行期間,當(dāng)前事務(wù)將被掛起。

          private final static ClassDo classDo = new ClassDo();
          static {

              classDo.setClassName("notSupport");
              classDo.setClassNo("notSupport");
          }
          @Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
          public void insertClassByException(ClassDo classDothrows CustomException 
          {
              classMapper.insertClass(classDo);
              throw new CustomException();
          }
          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertInnerExceptionTest() 
          {
              try {
                  classService.insertClassByException(classDo);
              } catch (CustomException e) {
                  e.printStackTrace();
              }
          }

          結(jié)果解釋:即使外部方法開啟事務(wù),但是 insertClassByException() 執(zhí)行,當(dāng)前事務(wù)會(huì)掛起,not_support 以非事務(wù)方式運(yùn)行,所以即使遇到異常情況,執(zhí)行結(jié)果也不會(huì)觸發(fā)回滾。

          Propagation_Not_Supported

          Propagation_Never

          表示被修飾的方法不應(yīng)該運(yùn)行事務(wù)上下文中。如果調(diào)用者或者該方法中存在一個(gè)事務(wù)正在運(yùn)行,則會(huì)拋出異常。

          @Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }

          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertInnerExceptionTest() 
          {
              studentService.insertStudent(studentDo);
          }

          結(jié)果如圖:

          Propagation_Never

          Propagation_Nested

          • 表示當(dāng)前方法已經(jīng)存在一個(gè)事務(wù),那么該方法將會(huì)在嵌套事務(wù)中運(yùn)行。
          • 嵌套的事務(wù)可以獨(dú)立與當(dāng)前事務(wù)進(jìn)行單獨(dú)地提交或者回滾。
          • 如果當(dāng)前事務(wù)不存在,那么其行為與 Propagation_Required 一樣。
          • 嵌套事務(wù)的概念就是內(nèi)層事務(wù)依賴于外層事務(wù)。外層事務(wù)失敗時(shí),會(huì)回滾內(nèi)層事務(wù)所做的動(dòng)作。而內(nèi)層事務(wù)操作失敗并不會(huì)引起外層事務(wù)的回滾。

          「1.外部未開啟事務(wù)時(shí),內(nèi)部方法則新建事務(wù)執(zhí)行」

          private final static StudentDo studentDo = new StudentDo();
          private final static ClassDo classDo = new ClassDo();
          static {
              studentDo.setClassId(2);
              studentDo.setStudentName("NESTED");
              studentDo.setAddress("NESTED");

              classDo.setClassName("NESTED");
              classDo.setClassNo("NESTED");
          }
          @Test
          public void insertTest() {
              studentService.insertStudent(studentDo);
              classService.insertClass(classDo);
              throw new RuntimeException();

          }
          @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
          public void insertStudent(StudentDo studentDo
          {
              studentMapper.insertStudent(studentDo);
              System.out.println("----------------------->Student插入成功!");
          }
          @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
          public void insertClass(ClassDo classDo
          {
              classMapper.insertClass(classDo);
              System.out.println("----------------------->Class插入成功!");
          }

          結(jié)果:

          Propagation_Nested
          Propagation_Nested

          「2.外部方法開啟事務(wù):」

          • 如果外部方法發(fā)生異常,則內(nèi)部事務(wù)一起發(fā)生回滾操作;
          • 如果外部無(wú)異常情況,內(nèi)部被調(diào)用方法存在異常情況,則內(nèi)部方法獨(dú)立回滾
          //單測(cè)代碼
          private final static StudentDo studentDo = new StudentDo();
          private final static ClassDo classDo = new ClassDo();
          static {
              studentDo.setClassId(2);
              studentDo.setStudentName("NESTED_InnerException");
              studentDo.setAddress("NESTED_InnerException");

              classDo.setClassName("NESTED_InnerException");
              classDo.setClassNo("NESTED_InnerException");
          }
          @Test
          @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
          public void insertInnerExceptionThrowsTest() throws CustomException 
          {
              studentMapper.insertStudent(studentDo);
              classService.insertClassByException(classDo);
          }
          //NESTED事務(wù)傳播行為
          @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
          public void insertClassByException(ClassDo classDothrows CustomException 
          {
              classMapper.insertClass(classDo);
              throw new RuntimeException();
          }

          最后,需要本文完整源碼的可以加我微信:codedq,免費(fèi)獲取!

          瀏覽 50
          點(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>
                  韩国一级黄 | www天堂A v | 99色图| 国产一级婬片A片免费看狼牙 | 国产尻屄视频 |