【Spring】Spring事务什么情况下会失效

前言:

上篇文章中讲到了Spring事务的传播机制,顺便谈了下事务在什么情况下会失效,spring事务失效有多方面的原因,上篇文章讲的失效情况比较浅,所以决定单出一篇文章讲一下。对事务传播机制感兴趣的朋友可以转过去观看下。

失效原因

一:数据库引擎不支持事务。

以mysql为例,不同的数据引擎对事务的支持情况是不同的。
MyISAM: 不支持事务
InnoDB: 支持事务,如果想使用事务需要更换到innodb引擎(mysql5.5之后版本默认是innodb)

二:没通过spring 动态代理来获取支持事务的实现类

导致这个问题的情况有很多种。下面写点示例

1. 事务方法被final、static、private修饰

当方法被 final、static、private 修饰时,无论是使用 JDK 动态代理还是 CGLIB 代理,这些方法都不能被 Spring 代理。

以下来自 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.
中文:在使用代理时,应该只将 @Transactional 注解应用于具有公共可见性的方法。如果您确实为受保护、私有或包可见的方法添加了 @Transactional 注解,虽然不会引发错误,但被注解的方法并不会展示配置的事务设置。如果您需要注解非公共方法,请考虑使用 AspectJ(见下文)。

2. 调用方法方式导致未被spring代理

调用事务方法时需要注意使用spring代理方式,同类中调用不能直接使用this.method()方式。

错误示例: 这种情况下不会执行事务

在这里插入图片描述

正确示例: 同类中调用需要先通过applicationContext.getBean()获取当前类的bean对象,通过bean对象调用事务方法。

在这里插入图片描述

三:事务传播机制配置不正确

在这里插入图片描述

当事务的传播机制选择的是requires_new、not_supported、never时当前事务不生效。
详细spring传播机制可以查看spring事务的传播机制,这里就不赘述了

四:异常类型未正确处理

Spring 事务默认仅在运行时遇到非检查型异常 (unchecked exceptions,如 RuntimeException 及其子类) 时执行回滚。如果配置不当,例如方法抛出检查型异常(如 IOException)而没有在 @Transactional 注解中声明回滚,那么事务不会回滚,导致事务失效。
这种情况可以使用自定义回滚异常方式。
1. 自定义回滚异常:
在这里插入图片描述
2.捕获异常之后再抛出RuntimeException异常
不推荐,一是有点脱裤子放屁了,二是抛出具体的异常信息容易定位问题。

五:异步方法调用:

当在 Spring 中进行异步调用(如使用 @Async 注解)时,可能导致事务失效。这是因为异步方法将在另一个线程中运行, Spring 事务是基于线程绑定的,因此原始线程的事务上下文无法传递到新线程中。

所以可以给异步方法新建一个事务,但是需要注意此事务是与上层事务分离的,异步方法内如果进行了回滚,上层事务时不会回滚的。

如果需要解决这个问题,可以使用下述方法:

@Service
public class UserServiceImpl implements IUserService {

    @Resource
    ApplicationContext applicationContext;

    @Transactional
    //@Transactional(rollbackFor = Exception.class) 或者直接抛出Exception捕获通用异常
    public void saveUser(User order) throws IOException {
        //业务逻辑
        UserServiceImpl bean = applicationContext.getBean(UserServiceImpl.class);
        try {
            bean.asyncMethod();
        } catch (Exception e) {
            // 当异步方法抛出异常时,捕获此异常,并将其转抛为 RuntimeException,以触发 parentMethod 的事务回滚
            throw new RuntimeException("Async method failed", e);
        }
        // saveUser 的其他业务逻辑
    }

    @Async
    public void asyncMethod() {
        UserServiceImpl myService = applicationContext.getBean(UserServiceImpl.class);
        myService.childMethod();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void childMethod() {
        // 异步方法执行的逻辑
        // ...
        // 在需要回滚事务的情况下抛出异常
        throw new RuntimeException("Async method exception");
    }
}

为方便理解,这里贴上gpt对这段代码的解析
在这里插入图片描述
原理:就是通过异步方法回滚时,抛出RuntimeException异常。致使上层事务也触发回滚事件,解决异步方法事务不生效问题。
好了,本片文章至此结束,多谢观看!

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小王笃定前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值