Java的@Transactional事务回滚

@Transactional基本原理概述在应用系统调用声明@Transactional的目标方法时,SpringFramework默认使用AOP代理,在代码运行时生成一个代理对象,根据@Transactional的属性配置信息,这个代理对象决定该声明@Transactional的目标方法是否由拦截器TransactionInterceptor来使用拦截,在Transacti…

大家好,又见面了,我是你们的朋友全栈君。

@Transactional 基本原理概述

在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。

处理Springboot下提交事务异常,数据库没有回滚的问题

Spring文档中说道,Spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。

什么是检查型异常和非检查型异常?

最简单的判断点有两个:
1、继承自runtimeException或error的是非检查型异常,而继承自exception的是检查型异常。
2、对非检查型异常可以不用捕获,而检查型异常必须用try语句块进行处理或者把异常交给上级方法处理,总之就是必须写代码处理它。所以必须service捕获异常,然后再次抛出,这样事务才能生效。

默认规则:

1、让检查型异常也回滚,@Transactional(rollbackFor=Exception.class),一般只需添加这个即可

2、让非检查型异常不回滚,@Transactional(notRollbackFor=RunTimeException.class)

3、不需要事务管理的(就是只是查询用)方法,@Transactional(propagation=Propagation.NOT_SUPPORTED),或者不添加

4、手动回滚,TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
例如:

try {
        String name = (String) list.get(j - 1).get("name");
      } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        return ResultUtil.error(500, "文件解析错误");
       }

你需要注意的事

1.@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能
2.Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。
3.当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,还是在具体的类上使用 @Transactional 注解比较好。
4.避免 Spring 的 AOP 的自调用问题:自调用就是方法A内调用本类的另一个加上事务注解的方法B时,方法B中对数据库的操作是不带事务的。

Spring AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。
失效原因:
在这里插入图片描述
方法one方法two都是public的:

classA中 ,任意要调用classB的方法,是通过spring代理的方式,那么spring的注解才会生效
classA中,方法one 调用同class内的方法two,即this调用,spring注解不会生效(例如@Cachable,@Transaction)

解决方法

方案一:使用AspectJ代理

@Service
public class OrderService { 
   
    private void insert() { 
   
        insertOrder();
    }
@Transactional
    public void insertOrder() { 
   
        //insert log info
        //insertOrder
        //updateAccount
       }
}

insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。

上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,可以使用 AspectJ取代 Spring AOP 代理,但现在有更好的解决方法。

方案二:利用AopContext.currentProxy()方法获得代理

方法的意思是尝试返回当前AOP代理。这种做法非常简洁,但是在默认情况下是不起作用的!因为AopContext中拿不到currentProxy,会报空指针。需要一些额外的配置,但不能对所有的注解拦截都有效,这是因为这些注解不是用的AspectJ代理,如果是@Transactional事务注解的话, 则是生效的,具体细节要翻源码了,这里不推荐使用。

方案三:通过ApplicationContext来获得动态代理对象(推荐)

@Component
public class AsyncService implements ApplicationContextAware { 
   

    private ApplicationContext applicationContext;

    public void async1() { 
   
        System.out.println("1:" + Thread.currentThread().getName());
        // 使用AppicationContext来获得动态代理的bean,然后再执行你调用的方法
        this.applicationContext.getBean(AsyncService.class).async2();
    }

    @Async
    public void async2() { 
   
        System.out.println("2:" + Thread.currentThread().getName());
    }

    // 注入ApplicationContext
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
   
        this.applicationContext = applicationContext;
    }
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/127133.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • xray安全漏洞检测(高效能)

    xray安全漏洞检测(高效能)风险告知:请勿在未授权情况下,对网站进行安全扫描,存在很大的法律风险,详细请了解网络安全法。血的案例:”白帽黑客”找漏洞被抓引争议世纪佳缘否认”钓鱼”–传媒–人民网在正式运行xray生成配置文件config.yaml后,请优先修改配置项,确认扫描范围,再重新启动xray如不预先配置,默认扫描所有站点,存在非常大的法律风险,请知悉!!!xray是一款功能强大的安全评估工具,主要特性有:检测速度快。发包速度快;漏洞检测算法高效。 支持范围广。大至OWASPTo.

    2022年5月15日
    51
  • 通过label点击触发隐藏input:file上传文件或者图片[通俗易懂]

    通过label点击触发隐藏input:file上传文件或者图片

    2022年2月11日
    45
  • vue的双向绑定是什么_vue绑定数据的两种方式

    vue的双向绑定是什么_vue绑定数据的两种方式<img:src=”`http://localhost:8088/java/publicCarUse/activitiFlow?processInstanceId=${processInstanceId}`”/>

    2022年9月14日
    0
  • java面向接口编程的例子_大二java期末考试试题

    java面向接口编程的例子_大二java期末考试试题转载:https://blog.csdn.net/l1028386804/article/details/43761615 我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有没有这样的疑惑:接口有什么用途?它和抽象类有什么区别?能不能用抽象类代替接口呢?而且,作为程序员,一定经常听到“面向接口编程”这个短语,那么它是什么意思?有什么思想内涵?和面向对…

    2025年6月2日
    0
  • STUN详解

    STUN详解STUN是一个简单的客户端-服务器协议。客户端发送一个请求到一台服务器,而服务器返回一个响应。有两种类型的请求:绑定请求(通过UDP发送)和共享密钥请求(发送TLS(通过TCP))。共享秘密请求服务器返回一个临时的用户名和密码。此用户名和密码用于在随后的绑定请求和绑定响应,身份验证和消息完整性的目的。STUN客户和STUN服务器之间可能有一个或多个NAT。

    2022年7月17日
    36
  • ccs5可以软件仿真吗(ccs软件仿真)

    起因:我用的是C6748板子,在用CCS进行软件仿真和硬件仿真时,发现矩阵的计算结果不同。查看内存,发现软件仿真内存中没有初值,但连接上板子后debug时发现有的内存中存在数据。经过学习与思索,确定是flash中的数据在板子被连接启动时bootbin选择了nor启动,会把nor中的数据传入DDR中,污染了原本的数组,会导致输出错误。解决办法:1:新添加:赋0循环,在使用数组前先都初始化为0,解决…

    2022年4月17日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号