<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ù)傳播行為?

          共 16962字,需瀏覽 34分鐘

           ·

          2021-05-08 00:16

          ?
          • 大家好,這里是公眾號(hào):「java小杰要加油」
          • 我們都知道 「spring」 有七種事務(wù)傳播行為,面試也經(jīng)常被問(wèn)道,不過(guò)他們長(zhǎng)得都太像啦,老虎老鼠傻傻分不清楚,今天我們就用這篇文章來(lái)徹底搞懂他們!
          • 文中有「大量代碼」論證,建議「收藏在電腦端食用」
          ?
          • 話不多說(shuō),直接開(kāi)車

          spring的七種事務(wù)傳播行為

          ?

          以下事務(wù)傳播屬性都是打在B方法上的事務(wù)注解

          ?
          • 「Propagation.REQUIRED:」 spring默認(rèn)的事務(wù)傳播行為,A方法調(diào)用B方法,如果A方法有事務(wù),則B方法加入到A方法中的事務(wù)中,否則B方法自己開(kāi)啟一個(gè)新事務(wù)

          • 「Propagation.SUPPORTS:」 A方法調(diào)用B方法,如果A方法有事務(wù),則B方法加入到A方法中的事務(wù)中,否則B方法自己使用非事務(wù)方式執(zhí)行

          • 「Propagation.MANDATORY:」 只能在存在事務(wù)的方法中被調(diào)用,A方法調(diào)用B方法,如果A方法沒(méi)事務(wù),則B方法會(huì)拋出異常

          • 「Propagation.REQUIRES_NEW:」 A方法調(diào)用B方法,如果A方法有事務(wù),則B方法把A方法的事務(wù)掛起,B方法自己重新開(kāi)啟一個(gè)新事務(wù)

          • 「Propagation.NOT_SUPPORTED:」 A方法調(diào)用B方法,如果A方法有事務(wù),則B方法掛起A方法中的事務(wù)中,否則B方法自己使用非事務(wù)方式執(zhí)行

          • 「Propagation.NEVER:」 不支持事務(wù),A方法調(diào)用B方法,如果A方法有事務(wù),則B方法會(huì)拋出異常

          • 「Propagation.NESTED:」「Propagation.REQUIRED」,不過(guò)此傳播屬性還可以,「保存狀態(tài)節(jié)點(diǎn),從而避免所有嵌套事務(wù)都回滾」

          我們看完了每個(gè)傳播屬性的一些解釋,腦子里應(yīng)該是還是蒙蒙的,下面來(lái)看下真正的代碼

          實(shí)戰(zhàn)

          Propagation.REQUIRED

          • spring 默認(rèn)的事務(wù)傳播屬性,A方法調(diào)用B方法,如果A方法有事務(wù),則B方法加入到A方法中的事務(wù)中,否則B方法自己開(kāi)啟一個(gè)新事務(wù)

          A接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用B接口的insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          我們?cè)賮?lái)看下B接口

           @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          假如說(shuō),我代碼這么寫(xiě)的話,那么這個(gè)數(shù)據(jù)庫(kù)里最終是會(huì)有什么數(shù)據(jù)呢?

          @PostMapping("/update")
              public Object updateTest(@RequestBody Test updateVO){
                  Integer flag = ATestService.updateTest(updateVO);
                  return flag;
              }

          原數(shù)據(jù)庫(kù)內(nèi)容

          postman來(lái)一發(fā)看看

          可以看到控制臺(tái)的結(jié)果是這樣的,他們共用一個(gè)事務(wù)(sqlSession是一樣的)

          此時(shí)數(shù)據(jù)庫(kù)的內(nèi)容也并沒(méi)有發(fā)生變化,說(shuō)明A,B接口都回滾了

          ?

          這個(gè)時(shí)候就會(huì)出現(xiàn)一個(gè)常見(jiàn)的面試題:如果B方法拋出的異常被「A方法try catch」 捕獲了,那么A方法的操作還會(huì)回滾嗎?

          ?

          答案是:「會(huì)回滾」

          來(lái)看下測(cè)試代碼,我們?cè)贏方法中添加了捕獲B方法拋出異常的代碼

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                    try {
                         // 調(diào)用insertTest方法事務(wù)方法
                         BTestService.insertTest(test);
                     }catch (Exception e){
                         System.out.println("A方法補(bǔ)獲了異常"+e.getMessage());
                     }
                  return i;
              }

          再次來(lái)一發(fā)postman,控制臺(tái)輸出測(cè)試結(jié)果如下

          我們看數(shù)據(jù)庫(kù)數(shù)據(jù)也沒(méi)有變

          ?

          那么問(wèn)題又來(lái)了,如果「A沒(méi)有捕獲,B方法自己捕獲了異?!?/strong>,那么事務(wù)還會(huì)回滾嗎?答案是:「不會(huì)」

          ?

          把B接口的代碼改一下

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = 0;

                  try {
                       i = testMapper.insert(updateVO);
                      //  拋出異常
                      int a = 1 / 0 ;
                  }catch (Exception e){
                      System.out.println("B方法補(bǔ)獲了異常"+e.getMessage());
                  }

                  return i;
              }

          同時(shí)把A方法的捕獲異常去掉

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          這個(gè)時(shí)候的結(jié)果是

          數(shù)據(jù)庫(kù)的數(shù)據(jù)是

          由此可見(jiàn),A和B兩個(gè)接口都生效了都操作數(shù)據(jù)庫(kù)了,都沒(méi)有回滾

          A方法捕獲和B方法捕獲有什么區(qū)別嗎(指捕獲異常)
          • 區(qū)別就是,A方法捕獲異常的話,B方法的事務(wù)注解會(huì)感知到異常的發(fā)生,從而回滾
          • 而B(niǎo)方法自己捕獲了,那么B方法的事務(wù)注解就不會(huì)感知到異常了,所以不會(huì)回滾
          ?

          只要理解了上面這個(gè)例子,我們以后各種異常/傳播屬性到底回滾不回滾就好分析啦!

          ?

          Propagation.SUPPORTS

          • A方法調(diào)用B方法,如果A方法有事務(wù),則B方法加入到A方法中的事務(wù)中,否則B方法自己使用非事務(wù)方式執(zhí)行

          我們把B接口的事務(wù)傳播屬性換成 Propagation.SUPPORTS

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          A方法

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          測(cè)試結(jié)果是

          數(shù)據(jù)庫(kù)的值也沒(méi)有被改變 , 所以兩個(gè)操作都被回滾了 那我們把A方法的事務(wù)注解去掉后再看一下

           @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          測(cè)試結(jié)果是是

          數(shù)據(jù)庫(kù)的值是

          由此可見(jiàn),兩個(gè)操作都沒(méi)有被回滾,B方法是以非事務(wù)方式進(jìn)行的操作

          Propagation.MANDATORY

          ?

          只能在存在事務(wù)的方法中被調(diào)用,A方法調(diào)用B方法,如果A方法沒(méi)事務(wù),則B方法會(huì)拋出異常

          ?

          A接口如下

          @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          B接口如下

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          數(shù)據(jù)庫(kù)的值也沒(méi)有變,由此可見(jiàn),B方法的事務(wù)注解為 Propagation.MANDATORY 當(dāng)A方法沒(méi)事務(wù)時(shí),則直接報(bào)錯(cuò)。

          Propagation.REQUIRES_NEW

          • A方法調(diào)用B方法,如果A方法有事務(wù),則B方法把A方法的事務(wù)掛起,B方法自己重新開(kāi)啟一個(gè)新事務(wù)

          A方法

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          B方法

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          結(jié)果是其中可以發(fā)現(xiàn) 兩個(gè)接口的 DefaultSqlSession  不一樣,那么就表明,這兩個(gè)不是一個(gè)事務(wù),所以就是,當(dāng)A接口存在事務(wù)的時(shí)候,B接口將其掛起并且重新開(kāi)啟一個(gè)新的事務(wù)

          ?
          • B方法拋出了異常,那么A方法沒(méi)有捕獲的話,則A,B方法都會(huì)回滾
          • A方法捕獲了異常,則A方法不回滾
          ?

          「還是那句話,如果在方法內(nèi)捕獲了異常,則此方法上的事務(wù)注解就感知不到這個(gè)異常的存在了,那么此方法的操作就不會(huì)回滾!」

          Propagation.NOT_SUPPORTED

          ?

          A方法調(diào)用B方法,如果A方法有事務(wù),則B方法掛起A方法中的事務(wù)中,否則B方法自己使用非事務(wù)方式執(zhí)行

          ?

          A接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          B接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          測(cè)試結(jié)果是

          數(shù)據(jù)庫(kù)的結(jié)果是

          ?

          我們可以看到,B接口生效了,確實(shí)插入了一條數(shù)據(jù),A接口沒(méi)有生效,沒(méi)有更改數(shù)據(jù),這是因?yàn)椋惓T贐接口內(nèi)拋出來(lái)了,由于B接口的事務(wù)傳播行為是 Propagation.NOT_SUPPORTED 則會(huì)掛起A接口的事務(wù),B接口以非事務(wù)情況操作(所以報(bào)錯(cuò)也不回滾),異常刨到了A接口內(nèi),A接口是有事務(wù)的,則會(huì)回滾,所以就沒(méi)有更改數(shù)據(jù)

          ?

          Propagation.NEVER

          • 不支持事務(wù),A方法調(diào)用B方法,如果A方法有事務(wù),則B方法會(huì)拋出異常

          A接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                  // 調(diào)用insertTest方法事務(wù)方法
                  BTestService.insertTest(test);
                  return i;
              }

          B接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.NEVER)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          結(jié)果是

          數(shù)據(jù)庫(kù)也沒(méi)有被改變, 可見(jiàn),當(dāng)A接口有事務(wù)的情況下調(diào)用B接口,直接報(bào)錯(cuò)

          Propagation.NESTED

          • 「Propagation.REQUIRED」,不過(guò)此傳播屬性還可以,「保存狀態(tài)節(jié)點(diǎn),從而避免所有嵌套事務(wù)都回滾」

          A接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
              @Override
              public Integer updateTest(Test updateVO) {
                  System.out.println("A updateTest方法");
                  int i = testMapper.updateByPrimaryKey(updateVO);
                  Test test =new Test("小杰",24);
                    try {
                         // 調(diào)用insertTest方法事務(wù)方法
                         BTestService.insertTest(test);
                     }catch (Exception e){
                         System.out.println("A方法補(bǔ)獲了異常"+e.getMessage());
                     }
                  return i;
              }

          B接口

          @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
              @Override
              public Integer insertTest(Test updateVO) {
                  System.out.println("B insertTest方法");
                  int i = testMapper.insert(updateVO);
                  //  拋出異常
                  int a = 1 / 0 ;
                  return i;
              }

          結(jié)果是

          數(shù)據(jù)庫(kù)的變化如下

          A接口的操作沒(méi)有回滾,B操作的回滾了,這就是因?yàn)椤皊avePoint”安全點(diǎn),在進(jìn)行B接口操作時(shí),當(dāng)前的狀態(tài)(A接口已經(jīng)操作完了)被保存至安全點(diǎn),B接口失敗的話,回滾只會(huì)回滾到這個(gè)安全點(diǎn)

          ?

          注:需要在A接口里try catch B接口的異常

          ?

          這里是公眾號(hào):「java小杰要加油」,我們下期見(jiàn)

          好文推薦


          瀏覽 66
          點(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>
                  伊人一区二区三区 | 黄片叉蛋的视频在线播放免费看 | daxiangjiaojiujiu | 久久久三级片电影 | 国产免费黄色电影在线观看 |