Spring事务失效的全面剖析

news2025/12/16 9:36:44

文章目录

    • 1. Spring事务基础
      • 1.1 什么是Spring事务
      • 1.2 Spring事务的实现原理
      • 1.3 `@Transactional`注解的主要属性
      • 1.4 使用Spring事务的简单示例
    • 2. Spring事务失效的常见场景及解决方案
      • 2.1 方法不是public的
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.2 自调用问题(同一个类中的方法调用)
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.3 异常被捕获而未被抛出
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.4 抛出的异常类型不正确
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.5 事务传播行为设置不当
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.6 未被Spring管理的类
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.7 数据库引擎不支持事务
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.8 使用了错误的事务管理器
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.9 多线程环境下的事务问题
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
      • 2.10 代理的限制(CGLIB代理和final方法)
        • 问题描述
        • 问题示例
        • 解决方案
        • 技术原理解释
    • 3. 常见问题与解答
      • Q1: 为什么在同一个类中调用`@Transactional`方法会导致事务失效?
      • Q2: Spring事务默认对哪些异常回滚?
      • Q3: 为什么有时候抛出异常后事务没有回滚?
      • Q4: `@Transactional`注解应该放在接口上还是实现类上?
      • Q5: 有没有办法在一个事务中部分回滚?
      • Q6: 如何在事务提交前或回滚后执行某些操作?
      • Q7: 如何在测试中验证事务是否正常工作?
    • 4. 最佳实践
      • 4.1 设计和开发阶段
      • 4.2 实现阶段
      • 4.3 测试阶段
    • 5. 总结

1. Spring事务基础

1.1 什么是Spring事务

事务是指对数据库执行的一系列操作,这些操作要么全部成功执行,要么全部不执行,以保证数据的一致性和完整性。Spring事务管理是Spring框架中一个强大的功能,它提供了一套完整的事务管理机制,使开发人员能够以声明式或编程式的方式管理事务。

Spring事务管理的核心是抽象出了一套事务管理的API,无论使用JDBC、Hibernate还是JPA,Spring都能以一致的方式提供事务支持。

1.2 Spring事务的实现原理

Spring事务的实现原理主要基于AOP(面向切面编程)和代理模式。当我们在一个方法上添加@Transactional注解时,Spring会使用AOP创建一个代理对象,这个代理对象会在目标方法执行前开启事务,在方法执行后提交事务,如果发生异常则回滚事务。

具体流程如下:

  1. Spring容器初始化时,扫描带有@Transactional注解的方法
  2. 通过BeanPostProcessor创建代理对象(默认使用JDK动态代理,如果类没有实现接口则使用CGLIB代理)
  3. 当调用带有@Transactional注解的方法时,会首先执行代理对象的逻辑
  4. 代理对象负责开启事务、执行原方法、提交或回滚事务

1.3 @Transactional注解的主要属性

@Transactional(
    propagation = Propagation.REQUIRED,          // 事务传播行为
    isolation = Isolation.DEFAULT,               // 事务隔离级别
    timeout = -1,                                // 事务超时时间
    readOnly = false,                            // 是否只读事务
    rollbackFor = Exception.class,               // 遇到哪些异常回滚
    noRollbackFor = FileNotFoundException.class  // 遇到哪些异常不回滚
)

1.4 使用Spring事务的简单示例

下面是一个基本的Spring事务使用示例:

@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void createUser(User user) {
   
        userRepository.save(user);
        // 如果后续操作抛出异常,该操作会回滚
    }
}

2. Spring事务失效的常见场景及解决方案

2.1 方法不是public的

问题描述

Spring官方文档明确指出,@Transactional注解只能应用于public方法上。如果将其应用于非public方法,该注解不会生效。

问题示例
@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    // 事务无效,因为方法不是public的
    @Transactional
    protected void createUser(User user) {
   
        userRepository.save(user);
        throw new RuntimeException("故意抛出异常");
    }
}
解决方案

确保所有使用@Transactional注解的方法都是public的。

@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    // 正确使用,方法是public的
    @Transactional
    public void createUser(User user) {
   
        userRepository.save(user);
        // 如果抛出异常,事务会回滚
    }
}
技术原理解释

Spring的事务管理是通过AOP实现的,而Spring AOP的默认行为是只拦截public方法。这是因为在代理对象中,非public方法无法被外部调用,所以Spring AOP也就无法为其创建代理。

2.2 自调用问题(同一个类中的方法调用)

问题描述

当一个类中的方法调用同一个类中的另一个事务方法时,事务不会生效。这是因为在这种情况下,调用的是目标对象的方法,而不是代理对象的方法。

问题示例
@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    // 事务生效的方法
    @Transactional
    public void createUserWithRoles(User user, List<Role> roles) {
   
        // 直接调用同类中的另一个事务方法
        this.createUser(user);  // 这里的调用不会使用事务代理
        
        // 添加角色逻辑
        for (Role role : roles) {
   
            userRepository.saveUserRole(user.getId(), role.getId());
        }
        
        // 如果这里抛出异常,createUser方法不会回滚
        throw new RuntimeException("故意抛出异常");
    }
    
    @Transactional
    public void createUser(User user) {
   
        userRepository.save(user);
    }
}

在上面的例子中,如果createUserWithRoles方法抛出异常,通过调用this.createUser(user)创建的用户不会回滚,因为这是一个自调用,没有通过Spring的事务代理。

解决方案

有以下几种解决方案:

  1. 将方法移到其他类中
@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RoleService roleService;
    
    @Transactional
    public void createUserWithRoles(User user, List<Role> roles) {
   
        // 调用其他类的方法,事务会正常工作
        roleService.createUser(user);
        
        for (Role role : roles) {
   
            userRepository.saveUserRole(user.getId(), role.getId());
        }
        
        throw new RuntimeException("故意抛出异常");
    }
}

@Service
public class RoleService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void createUser(User user) {
   
        userRepository.save(user);
    }
}
  1. 使用自我注入
@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    // 自我注入,获取代理对象
    @Autowired
    private UserService self;
    
    @Transactional
    public void createUserWithRoles(User user, List<Role> roles) {
   
        // 通过代理对象调用方法,事务会正常工作
        self.createUser(user);
        
        for (Role role : roles) {
   
            userRepository.saveUserRole(user.getId(), role.getId());
        }
        
        throw new RuntimeException("故意抛出异常");
    }
    
    @Transactional
    public void createUser(User user) {
   
        userRepository.save(user);
    }
}
  1. 使用AopContext获取代理对象(需要配置exposeProxy = true):
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class Application {
   
    public static void main(String[] args) {
   
        SpringApplication.run(Application.class, args);
    }
}

@Service
public class UserService {
   
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void createUserWithRoles(User user, List<Role> roles) {
   
        // 获取当前代理对象,调用其方法
        ((UserService) AopContext.currentProxy()).createUser(user);
        
        for (Role role : roles) {
   
            userRepository.saveUserRole(user.getId(), role.getId());
        }
        
        throw new RuntimeException("故意抛出异常");
    }
    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2375171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg多路节目流复用为一路包含多个节目的输出流

在音视频处理领域&#xff0c;将多个独立的节目流&#xff08;如不同频道的音视频内容&#xff09;合并为一个包含多个节目的输出流是常见需求。FFmpeg 作为功能强大的多媒体处理工具&#xff0c;提供了灵活的流复用能力&#xff0c;本文将通过具体案例解析如何使用 FFmpeg 实现…

分子动力学模拟揭示点突变对 hCFTR NBD1结构域热稳定性的影响

囊性纤维化&#xff08;CF&#xff09; 作为一种严重的常染色体隐性遗传疾病&#xff0c;全球约有 10 万名患者深受其害。它会累及人体多个器官&#xff0c;如肺部、胰腺等&#xff0c;严重影响患者的生活质量和寿命。CF 的 “罪魁祸首” 是 CFTR 氯离子通道的突变&#xff0c;…

关于SIS/DCS点检周期

在中国化工行业&#xff0c;近几年在设备维护上有个挺有意思的现象&#xff0c;即SIS和DCS这两个系统的点检周期问题&#xff0c;隔三差五就被管理层会议讨论&#xff0c;可以说是企业管理层关注的重要方向与关心要素。 与一般工业行业中设备运维不同&#xff0c;SIS与DCS的点…

【PmHub后端篇】PmHub中基于Redis加Lua脚本的计数器算法限流实现

1 限流的重要性 在高并发系统中&#xff0c;保护系统稳定运行的关键技术有缓存、降级和限流。 缓存通过在内存中存储常用数据&#xff0c;减少对数据库的访问&#xff0c;提升系统响应速度&#xff0c;如浏览器缓存、CDN缓存等多种应用层面。降级则是在系统压力过大或部分服务…

CST软件仿真案例——太阳能薄膜频谱吸收率

CST软件中的太阳能薄膜的功率吸收可用光频电磁波在介质材料中的损耗来计算。本案例计算非晶硅的功率吸收&#xff0c;然后考虑真实太阳频谱&#xff0c;计算有效吸收频谱。 用太阳能单元模板&#xff0c;时域求解器&#xff1a; 材料库提取四个材料&#xff0c;非晶硅&#xf…

ABAP+旧数据接管的会计年度未确定

导资产主数据时&#xff0c;报错旧数据接管的会计年度未确定 是因为程序里面使用了下列函数AISCO_CALCULATE_FIRST_DAY&#xff0c;输入公司代码&#xff0c;获取会计年度&#xff0c;这个数据是在后台表T093C表中取数的&#xff0c;通过SE16N可以看到后台表数据没有数&#xf…

养生:打造健康生活的全方位策略

在生活节奏不断加快的当下&#xff0c;养生已成为提升生活质量、维护身心平衡的重要方式。从饮食、运动到睡眠&#xff0c;再到心态调节&#xff0c;各个方面的养生之道共同构建起健康生活的坚实基础。以下为您详细介绍养生的关键要点&#xff0c;助您拥抱健康生活。 饮食养生…

贪吃蛇游戏排行榜模块开发总结:从数据到视觉的实现

一、项目背景与成果概览 在完成贪吃蛇游戏核心玩法后,本次开发重点聚焦于排行榜系统的实现。该系统具备以下核心特性: 🌐 双数据源支持:本地存储(localStorage)与远程API自由切换 🕒 时间维度统计:日榜/周榜/月榜/全时段数据筛选 🎮 模式区分:闯关模式(关卡进度…

屏幕与触摸调试

本章配套视频介绍: 《28-屏幕与触摸设置》 【鲁班猫】28-屏幕与触摸设置_哔哩哔哩_bilibili LubanCat-RK3588系列板卡都支持mipi屏以及hdmi显示屏的显示。 19.1. 旋转触摸屏 参考文章 触摸校准 参考文章 旋转触摸方向 配置触摸旋转方向 1 2 # 1.查看触摸输入设备 xinput…

使用 百度云大模型平台 做 【提示词优化】

1. 百度云大模型平台 百度智能云千帆大模型平台 &#xfeff; 平台功能&#xff1a;演示了阿里云大模型的百炼平台&#xff0c;该平台提供Prompt工程功能&#xff0c;支持在线创建和优化Prompt模板模板类型&#xff1a;平台提供多种预制模板&#xff0c;同时也支持用户自定义…

IJCAI 2025 | 高德首个原生3D生成基座大模型「G3PT」重塑3D生成的未来

国际人工智能联合会议&#xff08;IJCAI&#xff09;是人工智能领域最古老、最具权威性的学术会议之一&#xff0c;自1969年首次举办以来&#xff0c;至今已有近六十年的历史。它见证了人工智能从萌芽到蓬勃发展的全过程&#xff0c;是全球人工智能研究者、学者、工程师和行业专…

Samtec助力电视广播行业

【摘要前言】 现代广播电视技术最有趣的方面之一就是界限的模糊。过去&#xff0c;音频和视频是通过射频电缆传输的模拟技术采集的&#xff0c;而现在&#xff0c;数字世界已经取代了模拟技术。物理胶片和磁带已让位于数字存储设备和流媒体。 在这个过程中&#xff0c;连接器…

密码学--仿射密码

一、实验目的 1、通过实现简单的古典密码算法&#xff0c;理解密码学的相关概念 2、理解明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。 二、实验内容 1、题目内容描述 ①随机生成加密密钥&#xff0c;并验证密钥的可行性 ②从plain文件读入待…

SpringBoot整合MQTT实战:基于EMQX实现双向设备通信(附源码)

简言&#xff1a; 在万物互联的时代&#xff0c;MQTT协议凭借其轻量级、高效率的特性&#xff0c;已成为物联网通信的事实标准。本教程将带领您在Ubuntu系统上搭建EMQX 5.9.0消息服务器&#xff0c;并使用Spring Boot快速实现两个客户端的高效通信。通过本指南&#xff0c;您将…

从零开始掌握FreeRTOS(2)链表之节点的定义

目录 节点 节点定义 节点实现 根节点 根节点定义 精简节点定义 根节点实现 在上篇文章,我们完成了 FreeRTOS 的移植。在创建任务之前,我们需要先了解FreeRTOS的运转机制。 FreeRTOS是一个多任务系统,由操作系统来管理执行每个任务。这些任务全都挂载到一个双向循…

【数据结构】——双向链表

一、链表的分类 我们前面学习了单链表&#xff0c;其是我们链表中的其中一种&#xff0c;我们前面的单链表其实全称是单向无头不循环链表&#xff0c;我们的链表从三个维度进行分类&#xff0c;一共分为八种。 1、单向和双向 可以看到第一个链表&#xff0c;其只能找到其后一个…

mybatis中${}和#{}的区别

先测试&#xff0c;再说结论 userService.selectStudentByClssIds(10000, "wzh or 11");List<StudentEntity> selectStudentByClssIds(Param("stuId") int stuId, Param("field") String field);<select id"selectStudentByClssI…

抗量子计算攻击的数据安全体系构建:从理论突破到工程实践

在“端 - 边 - 云”三级智能协同理论中&#xff0c;端 - 边、边 - 云之间要进行数据传输&#xff0c;网络的安全尤为重要&#xff0c;为了实现系统总体的安全可控&#xff0c;将构建安全网络。 可先了解我的前文&#xff1a;“端 - 边 - 云”三级智能协同平台的理论建构与技术实…

uniapp|实现手机通讯录、首字母快捷导航功能、多端兼容(H5、微信小程序、APP)

基于uniapp实现带首字母快捷导航的通讯录功能,通过拼音转换库实现汉字姓名首字母提取与分类,结合uniapp的scroll-view组件与pageScrollTo API完成滚动定位交互,并引入uni-indexed-list插件优化索引栏性能。 目录 核心功能实现动态索引栏生成​联系人列表渲染​滚动定位联动性…

【Linux】基础IO(二)

&#x1f4dd;前言&#xff1a; 上篇文章我们对Linux的基础IO有了一定的了解&#xff0c;这篇文章我们来讲讲IO更底层的东西&#xff1a; 重定向及其原理感受file_operation文件缓冲区 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux…