推荐学习肝了十天半月 , 献上纯手绘“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;作者:溪~源
原文链接:
推荐阅读
-
刘鹤在京检查2019年春运工作时强调 真正让旅客感受到像家一般的
-
-
娱顽童|这次不演偶像剧,往实力派转型,刘恺威终于营业
-
-
全民故事计划最后还是要父母来还,被网贷奴役的后浪们:欠了几十万
-
-
-
-
▲头一次见封阳台还能这样操作,看完邻居家,真想把我家的拆了!
-
北晚新视觉网|部分行业已重新开放,单日增加1241例!印尼确诊创新高
-
【吃货家大锤】你不可不知,巽卦万物类象代表的深刻含义
-
-
梧桐树下V|2过2!2019年净利润均同比下降,精选层首审
-
陈紫函|曾从“宠儿”沦为“弃子”,24年把好牌打得稀烂,导演:只能怪她太便宜
-
中年|盘点赛尔号中的五大口头禅,赛小息的尴尬,阿铁打的霸气
-
-
互联网运维如何正确的拒绝加班?腾讯云发布MySQL 8.0、读写性能超强;避免种族歧视、MySQL 删除黑名单等术语|一周IT资讯
-
-
Angelia妈咪|一个美如天仙,一个相貌平平,有种''不公平遗传''叫佟丽娅和姐姐
-
隐入尘烟|《隐入尘烟》是正式落幕了,可女演员海清的“春天”,才刚刚开始