spring中@Transactional 注解失效的原因及解决办法

 更新时间:2024年06月05日 09:19:02   作者:睡个好觉  
面试中经常会被问到事务失效的场景有哪些,本文主要介绍了spring中@Transactional 注解失效的原因及解决办法,具有一定的参考价值,感兴趣的可以了解一下

前言

面试中经常会被问到事务失效的场景有哪些,其实在开发中,若是不了解事务失效的场景,当你觉得加了事务,就会回滚,就大错特错了,今天就来了解一下吧。

一、@Transactional 属性介绍

1.事务的传播行为:propagation

这就是我们常说的事务的七种传播行为,默认值:Propagation.REQUIRED

属性解释
Propagation.REQUIRED如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
Propagation.SUPPORTS如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
Propagation.MANDATORY如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
Propagation.REQUIRES_NEW重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
Propagation.NOT_SUPPORTED以非事务的方式运行,如果当前存在事务,暂停当前的事务。
Propagation.NEVER以非事务的方式运行,如果当前存在事务,则抛出异常。
Propagation.NESTED嵌套事务。

2.事务的隔离级别:isolation

这就是我们常说的事务的隔离级别,默认值:Isolation.DEFAULT

属性解释
Isolation.DEFAULT使用底层数据库默认的隔离级别
Isolation.READ_UNCOMMITTED读未提交
Isolation.READ_COMMITTED读已提交
Isolation.REPEATABLE_READ可重复读
Isolation.SERIALIZABLE串行化

3.事务的超时时间:timeout

  • timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

4.事务的回滚类型:rollbackFor

  • rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

二、@Transactional 失效场景

1.同一个类中方法调用,注解失效

场景重现:当调用 saveCabinet 方法时,save 方法中出异常不会回滚,如下代码

@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private CabinetMapper cabinetMapper;
    @Override
    public void saveCabinet(Cabinet cabinet) {
        save(cabinet);
    }
    @Transactional(rollbackFor = Exception.class)
    public void save(Cabinet cabinet) {
        cabinetMapper.insert(cabinet);
        int i = 1/0;	// 模拟出错
    }
}

原因分析:spring 中采用动态代理来实现事务,当同一个类调用 save 时,不是代理类在调用,所以扫描不到事务注解

解决方案1:将 save 方法提到另一个类中,saveCabinet 调用另一个类的 save 方法(推荐使用)

// 当前类
@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private MethodTest methodTest;
    @Override
    public void saveCabinet(Cabinet cabinet) {
        methodTest.save(cabinet);
    }
}
// 另一个类
@Service
public class MethodTest {
    @Autowired
    private CabinetMapper cabinetMapper;
    
    @Transactional(rollbackFor = Exception.class)
    public void save(Cabinet cabinet) {
        cabinetMapper.insert(cabinet);
        int i = 1/0; 	// 模拟出错
    }
}

解决方案2:在 CabinetServiceImpl 中自己注入自己,用自己的代理类调用 save 方法,即 cabinetServiceImpl.save()

@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private CabinetMapper cabinetMapper;
    
    @Autowired
    private CabinetServiceImpl cabinetServiceImpl;
    
    @Override
    public void saveCabinet(Cabinet cabinet) {
        cabinetServiceImpl.save(cabinet);
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void save(Cabinet cabinet) {
        cabinetMapper.insert(cabinet);
        int i = 1/0;	// 模拟出错
    }
}

2.异常被 catch “吃了”,注解失效

场景重现:当调用 saveCabinet 方法时,方法体被 try-catch 了,不会回滚,如下代码

@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private CabinetMapper cabinetMapper;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveCabinet(Cabinet cabinet) {
        try {
            cabinetMapper.insert(cabinet);
            int i = 1/0;	// 模拟出错
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

原因分析:异常直接捕获没有向上抛出,而是自己处理了,注解捕获不到异常,所以失效

解决方案:将 catch 到的异常向上抛出 throw new Exception(e);,异常交给注解处理

@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private CabinetMapper cabinetMapper;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveCabinet(Cabinet cabinet) throws Exception {
        try {
            cabinetMapper.insert(cabinet);
            int i = 1/0;	// 模拟出错
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
    }
}

3.@Transactional 应用在非 public 修饰的方法上

  • 场景重现:当调用的方法不是 public 修饰时,不会回滚,事务失效
  • 原因分析:在 spring aop 代理时,事务拦截器会在目标方法的执行前后进行拦截,其中的方法会去获取 Transactional 注解的一些信息,而其中的方法会去检查目标方法是否被 public 修饰,不是的话获取不到注解信息
  • 解决方案:将调用的方法访问修饰符改为 public

4.@Transactional 注解属性 rollbackFor 设置错误

场景重现:当注解没有指定 rollbackFor 属性时,如下若抛出 FileNotFoundException 异常则不会回滚

@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private CabinetMapper cabinetMapper;
    
    @Override
    @Transactional
    public void saveCabinet(Cabinet cabinet) throws IOException {
        cabinetMapper.insert(cabinet);
        // 模拟出错
        throw new FileNotFoundException();
    }
}

原因分析:spring 默认抛出未检查异常,继承 RuntimeException 或者 Error 才会回滚,其他异常如 FileNotFoundException 则不会回滚

解决方案:指定 rollbackFor 属性,一般指定 @Transactional(rollbackFor = Exception.class) 即可,也可以指定自定义异常

@Service
public class CabinetServiceImpl implements CabinetService {
    @Autowired
    private CabinetMapper cabinetMapper;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveCabinet(Cabinet cabinet) throws IOException {
        cabinetMapper.insert(cabinet);
        // 模拟出错
        throw new FileNotFoundException();
    }
}

5.@Transactional 注解属性 propagation 设置错误

  • 使用了不支持事务的传播属性

6.数据库引擎不支持事务

  • 数据库不支持事务

总结

到此这篇关于spring中@Transactional 注解失效的原因及解决办法的文章就介绍到这了,更多相关spring @Transactional 失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java递归遍历文件目录代码实例

    Java递归遍历文件目录代码实例

    这篇文章主要介绍了Java递归遍历文件目录代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 浅析Java中的虚拟线程

    浅析Java中的虚拟线程

    在本篇文章中,小编将带大家深入了解Java虚拟线程的原理、如何使用、使用的注意事项以及其他相似技术的差别,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10
  • Netty分布式高性能工具类recycler的使用及创建

    Netty分布式高性能工具类recycler的使用及创建

    这篇文章主要为大家介绍了Netty分布式高性能工具类recycler的使用和创建,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-03-03
  • 通过Java视角简单谈谈局部性原理

    通过Java视角简单谈谈局部性原理

    程序的局部性原理是指程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分,这篇文章主要给大家介绍了关于通过Java视角简单谈谈局部性原理的相关资料,需要的朋友可以参考下
    2021-07-07
  • SpringBoot自动配置类加载顺序与优先级

    SpringBoot自动配置类加载顺序与优先级

    本文详细介绍了Spring Boot自动配置类的加载顺序与优先级,首先说明了加载顺序的重要性及典型问题,然后拆解了Spring Boot的默认优先级体系,需要的朋友可以参考下
    2026-04-04
  • Java语言实现非递归实现树的前中后序遍历总结

    Java语言实现非递归实现树的前中后序遍历总结

    今天小编就为大家分享一篇关于Java语言实现非递归实现树的前中后序遍历总结,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Spring深入分析容器接口作用

    Spring深入分析容器接口作用

    Spring内部提供了很多表示Spring容器的接口和对象,我们今天来看看几个比较常见的容器接口和具体的实现类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • 数组在java中的扩容的实例方法

    数组在java中的扩容的实例方法

    在本篇文章里小编给大家分享的是一篇关于数组在java中的扩容的实例方法内容,有兴趣的朋友们可以学习下。
    2021-01-01
  • Java异常堆栈打印次数限制机制用法详解

    Java异常堆栈打印次数限制机制用法详解

    在Java开发中,异常处理是保证程序健壮性的重要手段,但当同一个异常被频繁抛出时,日志可能会被大量重复的堆栈信息淹没,影响问题排查效率,所以本文给大家介绍了Java异常堆栈打印次数限制机制的用法,需要的朋友可以参考下
    2025-04-04
  • SpringBoot事件监听器@EventListener的实现

    SpringBoot事件监听器@EventListener的实现

    @EventListener注解用于处理应用程序事件,提供了一种方便的方式来监听和响应各种事件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11

最新评论