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

          一個99%的人都說不清楚知識點(diǎn)——Spring 事務(wù)傳播行為

          共 13538字,需瀏覽 28分鐘

           ·

          2022-06-30 19:13

          面試過很多人,大部分都能把事務(wù)的四個特性及隔離級別說得七七八八,但當(dāng)問到 Spring 的傳播行為時(shí),就基本上沒人能說出個一二三了。

          我們都知道,一個事務(wù)要么成功,要么失敗。但當(dāng)若干個事務(wù)配合完成一個復(fù)雜任務(wù)時(shí),就不能簡單的這樣一刀切了。我們需要根據(jù)任務(wù)之間的親疏關(guān)系來指定哪些任務(wù)需要聯(lián)動回滾,哪些任務(wù)即使失敗也不會影響其他任務(wù)。要解決這個問題,就需要了解事務(wù)的傳播行為了。Spring 中有七種事務(wù)的傳播行為,如下表所示:

          事務(wù)傳播行為類型說明
          PROPAGATION_REQUIRED如果當(dāng)前沒有事務(wù),就新建一個事務(wù),如果已經(jīng)存在一個事務(wù)中,加入到這個事務(wù)中。這是最常見的選擇。
          PROPAGATION_SUPPORTS支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
          PROPAGATION_MANDATORY使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
          PROPAGATION_REQUIRES_NEW新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
          PROPAGATION_NOT_SUPPORTED以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
          PROPAGATION_NEVER以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
          PROPAGATION_NESTED如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與 PROPAGATION_REQUIRED 類似的操作。


          Spring 可以通過 @Transactional 注解的 propagation 屬性來設(shè)置不同的傳播行為策略。Spring 為此提供了一個枚舉類 Propagation,源碼如下:


          package org.springframework.transaction.annotation;

          import org.springframework.transaction.TransactionDefinition;

          public enum Propagation {

             /**
              * 需要事務(wù),它是默認(rèn)傳播行為,如果當(dāng)前存在事務(wù),就沿用當(dāng)前事務(wù),
              * 否則新建一個事務(wù)運(yùn)行內(nèi)部方法
              */
             REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

             /**
              * 支持事務(wù),如果當(dāng)前存在事務(wù),就沿用當(dāng)前事務(wù),
              * 如果不存在,則繼續(xù)采用無事務(wù)的方式運(yùn)行內(nèi)部方法
              */
             SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

             /**
              * 必須使用事務(wù),如果當(dāng)前沒有事務(wù),則會拋出異常,
              * 如果存在當(dāng)前事務(wù),則沿用當(dāng)前事務(wù)
              */
             MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

             /**
              * 無論當(dāng)前事務(wù)是否存在,都會創(chuàng)建新事務(wù)運(yùn)行方法,
              * 這樣新事務(wù)就可以擁有新的鎖和隔離級別等特性,與當(dāng)前事務(wù)相互獨(dú)立
              */
             REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

             /**
              * 不支持事務(wù),當(dāng)前存在事務(wù)時(shí),將掛起事務(wù),運(yùn)行方法
              */
             NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

             /**
              * 不支持事務(wù),如果當(dāng)前方法存在事務(wù),則拋出異常,否則繼續(xù)使用無事務(wù)機(jī)制運(yùn)行
              */
             NEVER(TransactionDefinition.PROPAGATION_NEVER),

             /**
              * 在當(dāng)前方法調(diào)用內(nèi)部方法時(shí),如果內(nèi)部方法發(fā)生異常,
              * 只回滾內(nèi)部方法執(zhí)行過的 SQL ,而不回滾當(dāng)前方法的事務(wù)
              */
             NESTED(TransactionDefinition.PROPAGATION_NESTED);

          ......

          }


          接下來我們通過對其中三種最常用的(REQUIRED、REQUIRES_NEW、NESTED)策略進(jìn)行對比來更深入的理解。以下測試均在外部方法開啟事務(wù)的情況下進(jìn)行,因?yàn)樵谕獠繘]有事務(wù)的情況下,三者都會新建事務(wù),效果一樣。


          REQUIRED


          當(dāng)內(nèi)部方法的事務(wù)傳播行為設(shè)置為 REQUIRED 時(shí),內(nèi)部方法會加入外部方法的事務(wù)。我們在 UserServiceImpl 中添加如下方法:


          @Service
          public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

             @Autowired
             private UserMapper mapper;
             
             @Override
             @Transactional(propagation = Propagation.REQUIRED)
             public void addWithRequired(User user) {
                 mapper.insert(user);
            }

             @Override
             @Transactional(propagation = Propagation.REQUIRED)
             public void addWithRequiredAndException(User user) {
                 mapper.insert(user);
                 throw new RuntimeException();
            }
          }


          創(chuàng)建 TransactionServiceImpl 類,并添加如下方法:

          @Slf4j
          @Service
          public class TransactionServiceImpl implements TransactionService {

             @Autowired
             private UserService userService;

             @Override
             public void noTransaction_required_required_externalException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequired(xiaoJing);
                 throw new RuntimeException();
                 
            }

             @Override
             public void noTransaction_required_requiredException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequiredAndException(xiaoJing);
                 
            }

             @Override
             @Transactional
             public void transaction_required_required_externalException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequired(xiaoJing);
                 throw new RuntimeException();
                 
            }

             @Override
             @Transactional
             public void transaction_required_requiredException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequiredAndException(xiaoJing);
                 
            }


             @Override
             @Transactional
             public void transaction_required_requiredException_try() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithRequired(xiaoShui);
                 try {
                     userService.addWithRequiredAndException(xiaoJing);
                } catch (Exception e) {
                    log.error("發(fā)生異常,事務(wù)回滾!");
                }
                 
            }
          }


          結(jié)果分析如下表所示:

          方法結(jié)果分析
          noTransaction_required_required_externalException小水和小鏡均成功入庫外部方法未開啟事務(wù),所以所有插入操作均未受到外部異常影響
          noTransaction_required_requiredException小水入庫,小鏡未入庫外部方法未開啟事務(wù),內(nèi)部方法事務(wù)各自獨(dú)立,互不影響,「小鏡」的插入方法發(fā)生異?;貪L,但「小水」的插入方法不受影響
          transaction_required_required_externalException小水和小鏡均未入庫外部方法開啟事務(wù),所有內(nèi)部方法均加入外部方法的事務(wù)中。而外部方法發(fā)生異常,所以導(dǎo)致所有操作都發(fā)生回滾
          transaction_required_requiredException小水和小鏡均未入庫外部方法開啟事務(wù),所有內(nèi)部方法均加入外部方法的事務(wù)中。由于「小鏡」的插入方法發(fā)生異常,此時(shí)所有方法都處于同一個事務(wù)中,所以導(dǎo)致所有操作都發(fā)生回滾
          transaction_required_requiredException_try小水和小鏡均未入庫外部方法開啟事務(wù),所有內(nèi)部方法均加入外部方法的事務(wù)中。由于「小鏡」的插入方法發(fā)生異常,此時(shí)所有方法都處于同一個事務(wù)中,即使發(fā)生異常的部分被 try-catch 住,所有操作仍然會回滾



          前面四種情況都比較好理解,很多人不能理解最后一種情況:我都 try-catch 了你還想怎樣?這里的關(guān)鍵點(diǎn)在于所有方法都處于同一個事務(wù)中,此時(shí)「小鏡」的插入方法發(fā)生異常,那么這個方法所在的事務(wù)就會被 Spring 設(shè)置為 rollback 狀態(tài)。因?yàn)楫惓1?catch 了,所以外部方法執(zhí)行完要進(jìn)行 commit 操作,這時(shí)卻發(fā)現(xiàn)當(dāng)前事務(wù)已經(jīng)處于 rollback 狀態(tài)了,雖然它不知道哪里出了問題,但也只能聽從指揮,回滾所有操作了。


          PS:由于外部方法不開啟事務(wù)的情況,在每種傳播行為下結(jié)果都是類似的,所以后面不再給出示例。


          REQUIRES_NEW


          當(dāng)內(nèi)部方法的傳播行為設(shè)置為 REQUIRES_NEW 時(shí),內(nèi)部方法會先將外部方法的事務(wù)掛起,然后開啟一個新的事務(wù) 。在 UserServiceImpl 中添加如下方法:


          @Service
          public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

            ...

             @Override
             @Transactional(propagation = Propagation.REQUIRES_NEW)
             public void addWithRequiredNew(User user) {
                 
                 mapper.insert(user);
                 
            }

             @Override
             @Transactional(propagation = Propagation.REQUIRES_NEW)
             public void addWithRequiredNewAndException(User user) {
                 
                 mapper.insert(user);
                 throw new RuntimeException();
                 
            }
          }


          在 TransactionServiceImpl 中添加如下方法:

          @Slf4j
          @Service
          public class TransactionServiceImpl implements TransactionService {

            ...

             @Override
             @Transactional
             public void transaction_required_requiredNew_externalException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequiredNew(xiaoJing);
                 throw new RuntimeException();
                 
            }

             @Override
             @Transactional
             public void transaction_required_requiredNew_requiredNewException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 User shuiJing = new User().setName("水鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequiredNew(xiaoJing);
                 userService.addWithRequiredNewAndException(shuiJing);
                 
            }

             @Override
             @Transactional
             public void transaction_required_requiredNewException_try() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 User shuiJing = new User().setName("水鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithRequiredNew(xiaoJing);
                 try {
                     userService.addWithRequiredNewAndException(shuiJing);
                } catch (Exception e) {
                    log.error("發(fā)生異常,事務(wù)回滾!");
                }
                 
            }
          }


          結(jié)果分析如下表所示:

          方法結(jié)果分析
          transaction_required_requiredNew_externalException小水未入庫,小鏡入庫外部方法開啟事務(wù),「小水」的插入方法和外部方法在同一個事務(wù)中,跟隨外部方法發(fā)生回滾;「小鏡」的插入方法開啟一個獨(dú)立的新事務(wù),不受外部方法異常的影響
          transaction_required_requiredNew_requiredNewException小水未入庫,小鏡入庫,水鏡未入庫外部方法開啟事務(wù),「水鏡」的插入方法開啟一個獨(dú)立的新事務(wù),因?yàn)榘l(fā)生異常,所以自己回滾了;「水鏡」的異常沒有做處理,因此會被外部方法感知到,「小水」的插入方法和外部方法在同一個事務(wù)中,跟隨外部方法發(fā)生回滾;「小鏡」的插入方法也會開啟一個獨(dú)立的新事務(wù),因此不會受到任何方法的影響,成功入庫
          transaction_required_requiredNewException_try小水和小鏡入庫,水鏡未入庫外部方法開啟事務(wù),「水鏡」的插入方法開啟一個獨(dú)立的新事務(wù),因?yàn)榘l(fā)生異常,所以自己回滾了;「水鏡」的異常被 try-catch 處理了,其他方法正常提交「小水」和「小鏡」成功入庫


          NESTED


          當(dāng)內(nèi)部方法的傳播行為設(shè)置為 NESTED 時(shí),內(nèi)部方法會開啟一個新的嵌套事務(wù)(子事務(wù))。在 UserServiceImpl 中添加如下方法:


          @Service
          public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

            ...

             @Override
             @Transactional(propagation = Propagation.NESTED)
             public void addWithNested(User user) {
                 
                 mapper.insert(user);
                 
            }

             @Override
             @Transactional(propagation = Propagation.NESTED)
             public void addWithNestedAndException(User user) {
                 
                 mapper.insert(user);
                 throw new RuntimeException();
                 
            }
          }


          在 TransactionServiceImpl 中添加如下方法:

          @Slf4j
          @Service
          public class TransactionServiceImpl implements TransactionService {

            ...

             @Override
             @Transactional
             public void transaction_nested_nested_externalException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithNested(xiaoShui);
                 userService.addWithNested(xiaoJing);
                 throw new RuntimeException();
                 
            }

             @Override
             @Transactional
             public void transaction_nested_nestedException() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 userService.addWithNested(xiaoShui);
                 userService.addWithNestedAndException(xiaoJing);
                 
            }

             @Override
             @Transactional
             public void transaction_nested_nestedException_try() {
                 
                 User xiaoShui = new User().setName("小水");
                 User xiaoJing = new User().setName("小鏡");
                 User shuiJing = new User().setName("水鏡");
                 userService.addWithRequired(xiaoShui);
                 userService.addWithNested(xiaoJing);
                 try {
                     userService.addWithNestedAndException(shuiJing);
                } catch (Exception e) {
                    log.error("發(fā)生異常,事務(wù)回滾!",e);
                }
                 
            }
          }


          結(jié)果分析如下表所示:

          方法結(jié)果分析
          transaction_nested_nested_externalException小水和小鏡均未入庫外部方法開啟事務(wù),內(nèi)部方法開啟各自的子事務(wù),外部方法發(fā)生異常,主事務(wù)回滾,子事務(wù)跟隨主事務(wù)回滾
          transaction_nested_nestedException小水和小鏡均未入庫外部方法開啟事務(wù),內(nèi)部方法開啟各自的子事務(wù),「小鏡」的插入方法發(fā)生異?;貪L自己的子事務(wù);「小鏡」的異常沒有做處理,因此會被外部方法感知到,「小水」的插入方法在外部方法的子事務(wù)中,所以跟隨主事務(wù)回滾
          transaction_nested_nestedException_try小水和小鏡入庫,水鏡未入庫外部方法開啟事務(wù),「小鏡」和「水鏡」開啟各自的子事務(wù),「小水」加入外部方法的事務(wù)?!杆R」的插入方法發(fā)生異?;貪L自己的子事務(wù);「水鏡」的異常被 try-catch 處理了,其他方法正常提交「小水」和「小鏡」成功入庫


          每個 NESTED 事務(wù)執(zhí)行前會將當(dāng)前操作保存下來,叫做 savepoint (保存點(diǎn)),如果當(dāng)前 NESTED 事務(wù)執(zhí)行失敗,則回滾到之前的保存點(diǎn),保存點(diǎn)使得子事務(wù)的回滾不對主事務(wù)造成影響。NESTED 事務(wù)在外部事務(wù)提交以后自己才會提交。


          總結(jié)


          REQUIRES_NEW 最為簡單,不管當(dāng)前有無事務(wù),它都會開啟一個全新事務(wù),既不影響外部事務(wù),也不會影響其他內(nèi)部事務(wù),真正的井水不犯河水,堅(jiān)定而獨(dú)立。

          REQUIRED 在沒有外部事務(wù)的情況下,會開啟一個獨(dú)立的新事務(wù),且不會對其他同級事務(wù)造成影響;而當(dāng)存在外部事務(wù)的情況下,則會與外部事務(wù)同生共死。

          NESTED 在沒有外部事務(wù)的情況下與 REQUIRED 效果相同;而當(dāng)存在外部事務(wù)的情況下,當(dāng)外部事務(wù)回滾時(shí),它會創(chuàng)建一個嵌套事務(wù)(子事務(wù))。外部事務(wù)回滾時(shí),子事務(wù)會跟著回滾;但子事務(wù)的回滾不會對外部事務(wù)和其他同級事務(wù)造成影響。

          - 完 -


          更多獨(dú)家精彩內(nèi)容盡在我的新書《Spring Boot趣味實(shí)戰(zhàn)課》中。

          掃碼了解本書詳情!

           

          如果喜歡本文
          歡迎 在看留言分享至朋友圈 三連

           熱文推薦  




          ▼點(diǎn)擊閱讀原文,了解本書詳情~

          瀏覽 17
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  色天天天天色 | 久久久久久无码精品大片 | 奇米影视久久 | 国产在线视频91 | 亚洲精品影视 |