<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ù)失效的場(chǎng)景有哪些?如何解決?

          共 12006字,需瀏覽 25分鐘

           ·

          2021-07-04 07:34

          實(shí)際項(xiàng)目開(kāi)發(fā)中,如果涉及到多張表操作時(shí),為了保證業(yè)務(wù)數(shù)據(jù)的一致性,大家一般都會(huì)采用事務(wù)機(jī)制;好多小伙伴可能只是簡(jiǎn)單了解一下,遇到事務(wù)失效的情況,便會(huì)無(wú)從下手,溪源此篇文章給大家整理了一下常見(jiàn)Spring事務(wù)失效的場(chǎng)景,希望開(kāi)發(fā)過(guò)程盡量避免踩坑,造成時(shí)間精力的浪費(fèi)。

          溪源按照最基本的使用方式以及常見(jiàn)失效場(chǎng)景優(yōu)先級(jí)整理,先簡(jiǎn)單介紹一下具體失效場(chǎng)景:

          • 注解@Transactional配置的方法非public權(quán)限修飾;
          • 注解@Transactional所在類(lèi)非Spring容器管理的bean;
          • 注解@Transactional所在類(lèi)中,注解修飾的方法被類(lèi)內(nèi)部方法調(diào)用;
          • 業(yè)務(wù)代碼拋出異常類(lèi)型非RuntimeException,事務(wù)失效;
          • 業(yè)務(wù)代碼中存在異常時(shí),使用try…catch…語(yǔ)句塊捕獲,而catch語(yǔ)句塊沒(méi)有throw new RuntimeExecption異常;(最難被排查到問(wèn)題且容易忽略)
          • 注解@Transactional中Propagation屬性值設(shè)置錯(cuò)誤即Propagation.NOT_SUPPORTED(一般不會(huì)設(shè)置此種傳播機(jī)制)
          • mysql關(guān)系型數(shù)據(jù)庫(kù),且存儲(chǔ)引擎是MyISAM而非InnoDB,則事務(wù)會(huì)不起作用(基本開(kāi)發(fā)中不會(huì)遇到);

          下面基于以上場(chǎng)景,溪源給小伙伴們?cè)敿?xì)解釋?zhuān)?/p>

          非public權(quán)限修飾

          參考Spring官方文檔介紹,摘要、譯文如下:

          When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

          譯文

          使用代理時(shí),您應(yīng)該只將@Transactional注釋?xiě)?yīng)用于具有公共可見(jiàn)性的方法。如果使用@Transactional注釋對(duì)受保護(hù)的、私有的或包可見(jiàn)的方法進(jìn)行注釋?zhuān)瑒t不會(huì)引發(fā)錯(cuò)誤,但帶注釋的方法不會(huì)顯示配置的事務(wù)設(shè)置。如果需要注釋非公共方法,請(qǐng)考慮使用AspectJ(見(jiàn)下文)。

          簡(jiǎn)言之:@Transactional 只能用于 public 的方法上,否則事務(wù)不會(huì)失效,如果要用在非 public 方法上,可以開(kāi)啟 AspectJ 代理模式。

          目前,如果@Transactional注解作用在非public方法上,編譯器也會(huì)給與明顯的提示,如圖:

          非Spring容器管理的bean

          基于這種失效場(chǎng)景,有工作經(jīng)驗(yàn)的大佬基本上是不會(huì)存在這種錯(cuò)誤的;@Service 注解注釋?zhuān)琒tudentServiceImpl 類(lèi)則不會(huì)被Spring容器管理,因此即使方法被@Transactional注解修飾,事務(wù)也亦然不會(huì)生效。

          簡(jiǎn)單舉例如下:

          /**
           * @Author:qxy
           */

          //@Service
          public class StudentServiceImpl implements StudentService {

              @Autowired
              private StudentMapper studentMapper;

              @Autowired
              private ClassService classService;

              @Override
              @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
              public void insertClassByException(StudentDo studentDothrows CustomException 
          {
                  studentMapper.insertStudent(studentDo);
                  throw new CustomException();
              }
          }

          注解修飾的方法被類(lèi)內(nèi)部方法調(diào)用

          這種失效場(chǎng)景是我們?nèi)粘i_(kāi)發(fā)中最常踩坑的地方;在類(lèi)A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法級(jí)別的事務(wù),在方法a里面 調(diào)用了方法b, 方法b里面的事務(wù)不會(huì)生效。為什么會(huì)失效呢?:

          其實(shí)原因很簡(jiǎn)單,Spring在掃描Bean的時(shí)候會(huì)自動(dòng)為標(biāo)注了@Transactional注解的類(lèi)生成一個(gè)代理類(lèi)(proxy),當(dāng)有注解的方法被調(diào)用的時(shí)候,實(shí)際上是代理類(lèi)調(diào)用的,代理類(lèi)在調(diào)用之前會(huì)開(kāi)啟事務(wù),執(zhí)行事務(wù)的操作,但是同類(lèi)中的方法互相調(diào)用,相當(dāng)于this.B(),此時(shí)的B方法并非是代理類(lèi)調(diào)用,而是直接通過(guò)原有的Bean直接調(diào)用,所以注解會(huì)失效。

          @Service
          public class ClassServiceImpl implements ClassService {

              @Autowired
              private ClassMapper classMapper;

              public void insertClass(ClassDo classDo) throws CustomException {
                  insertClassByException(classDo);
              }

              @Override
              @Transactional(propagation = Propagation.REQUIRED)
              public void insertClassByException(ClassDo classDo) throws CustomException {
                  classMapper.insertClass(classDo);
                  throw new RuntimeException();
              }
          }

          //測(cè)試用例:
          @Test
              public void insertInnerExceptionTest() throws CustomException {
                 classDo.setClassId(2);
                 classDo.setClassName("java_2");
                 classDo.setClassNo("java_2");

                 classService.insertClass(classDo);
              }

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

          java.lang.RuntimeException
           at com.qxy.common.service.impl.ClassServiceImpl.insertClassByException(ClassServiceImpl.java:34)
           at com.qxy.common.service.impl.ClassServiceImpl.insertClass(ClassServiceImpl.java:27)
           at com.qxy.common.service.impl.ClassServiceImpl$$FastClassBySpringCGLIB$$a1c03d8.invoke(<generated>) 

          雖然業(yè)務(wù)代碼報(bào)錯(cuò)了,但是數(shù)據(jù)庫(kù)中已經(jīng)成功插入數(shù)據(jù),事務(wù)并未生效;

          解決方案

          類(lèi)內(nèi)部使用其代理類(lèi)調(diào)用事務(wù)方法:以上方法略作改動(dòng)

          public void insertClass(ClassDo classDo) throws CustomException {
          //         insertClassByException(classDo);
                  ((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
              }
          //測(cè)試用例:
           @Test
              public void insertInnerExceptionTest() throws CustomException {
                 classDo.setClassId(3);
                 classDo.setClassName("java_3");
                 classDo.setClassNo("java_3");

                 classService.insertClass(classDo);
              }

          業(yè)務(wù)代碼拋出異常,數(shù)據(jù)庫(kù)未插入新數(shù)據(jù),達(dá)到我們的目的,成功解決一個(gè)事務(wù)失效問(wèn)題;

          數(shù)據(jù)庫(kù)數(shù)據(jù)未發(fā)生改變;

          注意:一定要注意啟動(dòng)類(lèi)上要添加@EnableAspectJAutoProxy(exposeProxy = true)注解,否則啟動(dòng)報(bào)錯(cuò):

          java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.

           at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)
           at com.qxy.common.service.impl.ClassServiceImpl.insertClass(ClassServiceImpl.java:28)

          異常類(lèi)型非RuntimeException

          這種事務(wù)失效場(chǎng)景也是非常難排查問(wèn)題的,如果沒(méi)有深究源碼實(shí)現(xiàn),估計(jì)要花費(fèi)一番功夫啦;推薦:250期面試題匯總

          @Service
          public class ClassServiceImpl implements ClassService {

              @Autowired
              private ClassMapper classMapper;

          //    @Override
          //    @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
              public void insertClass(ClassDo classDo) throws Exception {
          //        即使此處使用代理對(duì)象調(diào)用內(nèi)部事務(wù)方法,數(shù)據(jù)依然未發(fā)生回滾,事務(wù)機(jī)制亦然失效
                  ((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
              }

              @Override
              @Transactional(propagation = Propagation.REQUIRED)
              public void insertClassByException(ClassDo classDo) throws Exception {
                  classMapper.insertClass(classDo);
                  //拋出非RuntimeException類(lèi)型
                  throw new Exception();
              }
          //測(cè)試用例:
           @Test
              public void insertInnerExceptionTest() throws Exception {
                 classDo.setClassId(3);
                 classDo.setClassName("java_3");
                 classDo.setClassNo("java_3");

                 classService.insertClass(classDo);
              }
          }

          運(yùn)行結(jié)果:

          業(yè)務(wù)代碼拋出異常,但是數(shù)據(jù)庫(kù)發(fā)生更新操作;

          java.lang.Exception
           at com.qxy.common.service.impl.ClassServiceImpl.insertClassByException(ClassServiceImpl.java:35)
           at com.qxy.common.service.impl.ClassServiceImpl$$FastClassBySpringCGLIB$$a1c03d8.invoke(<generated>)
           at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

          數(shù)據(jù)庫(kù)依然插入數(shù)據(jù),不是我們想要的結(jié)果啊,趕緊修改吧,產(chǎn)品經(jīng)理來(lái)追啦~

          解決方案:

          @Transactional注解修飾的方法,加上rollbackfor屬性值,指定回滾異常類(lèi)型:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)

          @Override
              @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
              public void insertClassByException(ClassDo classDothrows Exception 
          {
                  classMapper.insertClass(classDo);
                  throw new Exception();
              }

          捕獲異常后,卻未拋出異常

          在事務(wù)方法中使用try-catch,導(dǎo)致異常無(wú)法拋出,自然會(huì)導(dǎo)致事務(wù)失效。

          @Service
          public class ClassServiceImpl implements ClassService {

              @Autowired
              private ClassMapper classMapper;

          //    @Override
              public void insertClass(ClassDo classDo) {
                  ((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);

              }

              @Override
              @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
              public void insertClassByException(ClassDo classDo
          {
                  classMapper.insertClass(classDo);
                  try {
                      int i = 1 / 0;
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }

          // 測(cè)試用例:
           @Test
              public void insertInnerExceptionTest() {
                 classDo.setClassId(4);
                 classDo.setClassName("java_4");
                 classDo.setClassNo("java_4");

                 classService.insertClass(classDo);
              }

          執(zhí)行結(jié)果:

          解決方案:捕獲異常并拋出異常

           @Override
              @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
              public void insertClassByException(ClassDo classDo
          {
                  classMapper.insertClass(classDo);
                  try {
                      int i = 1 / 0;
                  } catch (Exception e) {
                      e.printStackTrace();
                      throw new RuntimeException();
                  }
              }

          事務(wù)傳播行為設(shè)置異常

          此種事務(wù)傳播行為不是特殊自定義設(shè)置,基本上不會(huì)使用Propagation.NOT_SUPPORTED,不支持事務(wù)

           @Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
              public void insertClassByException(ClassDo classDo
          {
                  classMapper.insertClass(classDo);
                  try {
                      int i = 1 / 0;
                  } catch (Exception e) {
                      e.printStackTrace();
                      throw new RuntimeException();
                  }
              }

          數(shù)據(jù)庫(kù)存儲(chǔ)引擎不支持事務(wù)

          以MySQL關(guān)系型數(shù)據(jù)為例,如果其存儲(chǔ)引擎設(shè)置為 MyISAM,則事務(wù)失效,因?yàn)镸yISMA 引擎是不支持事務(wù)操作的;

          故若要事務(wù)生效,則需要設(shè)置存儲(chǔ)引擎為InnoDB ;目前 MySQL 從5.5.5版本開(kāi)始默認(rèn)存儲(chǔ)引擎是:InnoDB;

          來(lái)源:blog.csdn.net/xuan_lu/article/details/107797505

          我已經(jīng)更新了我的《10萬(wàn)字Springboot經(jīng)典學(xué)習(xí)筆記》中,點(diǎn)擊下面小卡片,進(jìn)入【Java禿頭哥】,回復(fù):筆記,即可免費(fèi)獲取。

          點(diǎn)贊是最大的支持 

          瀏覽 53
          點(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>
                  97人人干 | 超碰永久在线 | 免费收看一级黄色电影 | 欧美强奸视频 | 内射网站免费观看 |