@Transactional注解失效的解决方案

一、前言

  开发中我们经常使用 @Transactional注解来启用Spring事务管理,但是如果使用方法不当,会遇到注解不生效该事务回滚的地方却没有回滚的问题。
总结下一般是以下几个原因:

  1. @Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。
  2. 默认情况下,spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。针对这种情况,可以try catch checked异常后进行手动事务回滚。
  3. 数据库引擎要支持事务,如果是mysql,注意表要使用支持事务的引擎,比如InnoDB,如果是MyISAM,事务是不起作用的。
  4. 同一个类中, 一个no-transactional的方法去调用transactional的方法, 事务会失效。

本文主要讲第四种情况如何处理。

二、示例

事务失效示例1

事务失效示例1

事务失效示例2

事务失效示例2

执行add() 方法后数据库插入了两条数据,也就说明以上两段代码中,doAdd()方法的事务增强都不会执行,具体原因可以参考这篇文章

那么如果想让doAdd()方法在发生异常时数据库事务回滚,有什么解决办法吗?

第一步开启配置expose-proxy

如果使用的xml配置文件的方式,则添加如下内容:

<aop:aspectj-autoproxy  expose-proxy="true"  />

如果用的SpringBoot框架,则直接在启动类上添加如下注解即可。

@EnableAspectJAutoProxy(proxyTargetClass = true,exposeProxy = true)

第二步修改代码
将原来的调用代码this.doAdd(person); 改为 ((PersonService)AopContext.currentProxy()).doAdd(person);

enter description here

enter description here

再进行测试发现数据库只插入了一条数据(person2),说明doAdd() 方法里的事务增强生效了。

三、源码分析

点进 @EnableAspectJAutoProxy 注解代码如下:

1@Target(ElementType.TYPE) 2@Retention(RetentionPolicy.RUNTIME) 3@Documented 4@Import(AspectJAutoProxyRegistrar.class) 5public @interface EnableAspectJAutoProxy { 6 7 boolean proxyTargetClass() default false; 8 9 boolean exposeProxy() default false; 10 11}

发现它注入了一个Bean AspectJAutoProxyRegistrar,再看看这个AspectJAutoProxyRegistrar是什么。

1class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { 2 3 public void registerBeanDefinitions( 4 AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 5 6 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); 7 // 解析注解EnableAspectJAutoProxy 8 AnnotationAttributes enableAspectJAutoProxy = 9 AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); 10 if (enableAspectJAutoProxy != null) { 11 if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { 12 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); 13 } 14 if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { 15 // 如果设置了exposeProxy=true就强制使用 16 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); 17 } 18 } 19 } 20 21}

AopConfigUtils#forceAutoProxyCreatorToExposeProxy

1// 强制使用的过程其实也是一个属性设置的过程 2 public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) { 3 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { 4 BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); 5 definition.getPropertyValues().add("exposeProxy", Boolean.TRUE); 6 } 7 } 8 9 /** 10 * The bean name of the internally managed auto-proxy creator. 11 */ 12 public static final String AUTO_PROXY_CREATOR_BEAN_NAME = 13 "org.springframework.aop.config.internalAutoProxyCreator";
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

springboot+mybatis 使用事务

一、一些概念声明式的事务管理是基于AOP的,在springboot中可以通过@Transactional注解的方式获得支持,这种方式的优点是:1)非侵入式,业务逻辑不受事务管理代码的污染。2)方法级别的事务回滚,合理划分方法的粒度可以做到符合各种业务场景的事务管理。本文使用目前最常用的mybatis框架来配置springboot的事务

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )