推荐学习肝了十天半月 , 献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图微服务架构秘籍:SpringCloud+SpringCloudAlibaba , 全网疯传
注解@Transactional配置的方法非public权限修饰;注解@Transactional所在类非Spring容器管理的bean;注解@Transactional所在类中 , 注解修饰的方法被类内部方法调用;业务代码抛出异常类型非RuntimeException , 事务失效;业务代码中存在异常时 , 使用try…catch…语句块捕获 , 而catch语句块没有thrownewRuntimeExecption异常;(最难被排查到问题且容易忽略)注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)mysql关系型数据库 , 且存储引擎是MyISAM而非InnoDB , 则事务会不起作用(基本开发中不会遇到);下面基于以上场景 , 溪源给小伙伴们详细解释;非public权限修饰参考Spring官方文档介绍 , 摘要、译文如下:Whenusingproxies,youshouldapplythe@Transactionalannotationonlytomethodswithpublicvisibility.Ifyoudoannotateprotected,privateorpackage-visiblemethodswiththe@Transactionalannotation,noerrorisraised,buttheannotatedmethoddoesnotexhibittheconfiguredtransactionalsettings.ConsidertheuseofAspectJ(seebelow)ifyouneedtoannotatenon-publicmethods.译文使用代理时 , 您应该只将@Transactional注释应用于具有公共可见性的方法 。 如果使用@Transactional注释对受保护的、私有的或包可见的方法进行注释 , 则不会引发错误 , 但带注释的方法不会显示配置的事务设置 。 如果需要注释非公共方法 , 请考虑使用AspectJ(见下文) 。简言之:@Transactional只能用于public的方法上 , 否则事务不会失效 , 如果要用在非public方法上 , 可以开启AspectJ代理模式 。
目前 , 如果@Transactional注解作用在非public方法上 , 编译器也会给与明显的提示 , 如图:
java.lang.RuntimeExceptionatcom.qxy.common.service.impl.ClassServiceImpl.insertClassByException(ClassServiceImpl.java:34)atcom.qxy.common.service.impl.ClassServiceImpl.insertClass(ClassServiceImpl.java:27)atcom.qxy.common.service.impl.ClassServiceImpl$$FastClassBySpringCGLIB$$a1c03d8.invoke()虽然业务代码报错了 , 但是数据库中已经成功插入数据 , 事务并未生效;
publicvoidinsertClass(ClassDoclassDo)throwsCustomException{//insertClassByException(classDo);((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);}测试用例:@TestpublicvoidinsertInnerExceptionTest()throwsCustomException{classDo.setClassId(3);classDo.setClassName("java_3");classDo.setClassNo("java_3");classService.insertClass(classDo);}业务代码抛出异常 , 数据库未插入新数据 , 达到我们的目的 , 成功解决一个事务失效问题;
java.lang.IllegalStateException:Cannotfindcurrentproxy:Set'exposeProxy'propertyonAdvisedto'true'tomakeitavailable.atorg.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)atcom.qxy.common.service.impl.ClassServiceImpl.insertClass(ClassServiceImpl.java:28)异常类型非RuntimeException这种事务失效场景也是非常难排查问题的 , 如果没有深究源码实现 , 估计要花费一番功夫啦;@ServicepublicclassClassServiceImplimplementsClassService{@AutowiredprivateClassMapperclassMapper;//@Override//@Transactional(propagation=Propagation.NESTED,rollbackFor=Exception.class)publicvoidinsertClass(ClassDoclassDo)throwsException{//即使此处使用代理对象调用内部事务方法 , 数据依然未发生回滚 , 事务机制亦然失效((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);}@Override@Transactional(propagation=Propagation.REQUIRED)publicvoidinsertClassByException(ClassDoclassDo)throwsException{classMapper.insertClass(classDo);//抛出非RuntimeException类型thrownewException();}测试用例:@TestpublicvoidinsertInnerExceptionTest()throwsException{classDo.setClassId(3);classDo.setClassName("java_3");classDo.setClassNo("java_3");classService.insertClass(classDo);}}运行结果:业务代码抛出异常 , 但是数据库发生更新操作;
java.lang.Exceptionatcom.qxy.common.service.impl.ClassServiceImpl.insertClassByException(ClassServiceImpl.java:35)atcom.qxy.common.service.impl.ClassServiceImpl$$FastClassBySpringCGLIB$$a1c03d8.invoke()atorg.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)数据库依然插入数据 , 不是我们想要的结果啊 , 赶紧修改吧 , 产品经理来追啦~
@Override@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)publicvoidinsertClassByException(ClassDoclassDo)throwsException{classMapper.insertClass(classDo);thrownewException();}捕获异常后 , 却未抛出异常在事务方法中使用try-catch , 导致异常无法抛出 , 自然会导致事务失效 。 @ServicepublicclassClassServiceImplimplementsClassService{@AutowiredprivateClassMapperclassMapper;//@OverridepublicvoidinsertClass(ClassDoclassDo){((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);}@Override@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)publicvoidinsertClassByException(ClassDoclassDo){classMapper.insertClass(classDo);try{inti=1/0;}catch(Exceptione){e.printStackTrace();}}}测试用例:@TestpublicvoidinsertInnerExceptionTest(){classDo.setClassId(4);classDo.setClassName("java_4");classDo.setClassNo("java_4");classService.insertClass(classDo);}执行结果:
@Override@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)publicvoidinsertClassByException(ClassDoclassDo){classMapper.insertClass(classDo);try{inti=1/0;}catch(Exceptione){e.printStackTrace();thrownewRuntimeException();}}事务传播行为设置异常此种事务传播行为不是特殊自定义设置 , 基本上不会使用Propagation.NOT_SUPPORTED , 不支持事务@Transactional(propagation=Propagation.NOT_SUPPORTED,rollbackFor=Exception.class)publicvoidinsertClassByException(ClassDoclassDo){classMapper.insertClass(classDo);try{inti=1/0;}catch(Exceptione){e.printStackTrace();thrownewRuntimeException();}}数据库存储引擎不支持事务以MySQL关系型数据为例 , 如果其存储引擎设置为MyISAM , 则事务失效 , 因为MyISMA引擎是不支持事务操作的;故若要事务生效 , 则需要设置存储引擎为InnoDB;目前MySQL从5.5.5版本开始默认存储引擎是:InnoDB;作者:溪~源
原文链接:
推荐阅读
-
#趣味社会学#社会活动家和毒贩一起行动,疫情下的巴西:城市边缘的贫民窟
-
健身王老汉|会增加心血管死亡率?对40万人研究给出这样的答案,每天吃鸡蛋
-
一家十一口,一家二十口,两家合一起,万事都不愁打一字 一家十一口打一字
-
易乐笑笑■谢可寅开始热搜,王承渲被切断,宠物群变化太快,同样是放弃位置C
-
「洛杉矶奥组委」洛杉矶奥组委发布2028洛杉矶夏季奥运会与残奥会会徽
-
韩都定制|数亿人因此丧命,否则中国人数早超20亿!,我国最大的一次天灾
-
骗子|【反电信网络诈骗系列微视频012】采访受害者
-
-
-
第一纺织网|赶趟新基建!最赚钱的服装品牌比音勒芬携手腾讯按下数字化快进键
-
法案|本周外盘丨美新刺激法案谈判继续,网易腾讯音乐发布财报
-
-
-
科学家|韩国凌晨天降火球,如月亮般大小,坠毁时传出巨响
-
-
龚俊|龚俊杂志大片上热搜,透视渔网服太辣眼,过分聚焦身体部位惹争议
-
「汽车大咖」17:26,因车辆交通事故,津蓟高速驶往蓟州方向K29处占用第1行车道、第2行车道,4-3
-
青年|LPL今日首发:SofM大战雷达哥,阿Bin对线369,谁才是真正的三阵上单?
-
早财经 | 闯入当当撬开保险柜,李国庆等4人被行拘
-
空白键|至少领先世界30年,新一代隐轰首飞进入倒计时,军方下令连夜生产