Transactional失效的情况总结

news/2026/1/25 21:31:34/文章来源:https://www.cnblogs.com/guoyafenghome/p/19530535

@Transactional失效的情况总结

前言

@Transactional失效是实际开发中非常容易踩的坑,本文结合实际项目经验总结了常见的失效场景和解决方案。


一、最常见的:同类内部调用(占80%的坑)

这是最容易犯的错误,也是开发中最常遇到的问题。

问题描述

比如说,我有一个UserService类,里面有两个方法:methodA和methodB。methodA没加事务注解,methodB加了@Transactional。如果我在methodA里直接调用methodB,那methodB的事务就不会生效。

原因分析

因为Spring的事务是基于动态代理实现的。从外部调用methodB时,调用的是代理对象,代理对象会启动事务;但是在类内部,methodA调用methodB实际上是通过this.methodB()调用的,this是当前对象本身,不是代理对象,所以绕过了代理,事务就失效了。

解决方案

  • 最简单的办法就是把methodB提取到另一个Service类里
  • 或者自己注入自己(虽然有点奇怪但确实可以work)
  • 再或者用AspectJ的编译时织入,但这个比较复杂

实际项目中,我们都是拆分成不同的Service,这样代码职责也更清晰。


二、方法不是public的

这个也很容易犯错。

问题描述

如果你把@Transactional加在private、protected或者default(包级私有)的方法上,事务不会生效。

原因分析

还是因为代理机制。Spring默认用的是CGLIB代理,它是通过生成子类来实现的。子类只能覆盖父类的public方法,private和protected方法子类要么访问不到,要么访问受限,所以代理不了。

实际案例

之前有个同事写了一个private的保存方法,加了@Transactional,结果数据保存了一半出异常了也没回滚。调试了好久才发现是方法修饰符的问题。

解决方案

把方法改成public就行了。如果实在不想暴露这个方法,那就重新设计一下,把它提取到另一个内部使用的Service里。


三、异常被吞了(捕获了但没抛出)

这个坑非常隐蔽,很多人都踩过。

问题描述

你在方法里写了try-catch,捕获了异常但只是打印了个日志,没有往外抛,那事务就不会回滚。

原因分析

Spring的事务管理器是通过捕获异常来决定要不要回滚的。如果你把异常吞掉了,Spring根本不知道出问题了,就不会回滚。

实际案例

我之前做转账功能,扣款成功了,加款的时候失败了,我当时catch了异常只是打了个日志,结果钱被扣了但没加到对方账户,造成了资金不平。这个bug上线后被客户投诉了,幸好金额不大。

解决方案

  • 要么就不要catch异常,让它自然抛出去
  • 要么catch了之后,处理完再throw出去,比如抛个RuntimeException或者自定义的业务异常

四、抛的是受检异常(CheckedException)

这个比较容易被忽略。

问题描述

Spring默认只对RuntimeException和Error进行回滚,对于CheckedException(就是那种必须要catch或者在方法签名上throws的异常),默认不回滚。

原因分析

因为受检异常通常认为是可以预期和恢复的业务异常,不一定要回滚事务。这是Spring的一个设计理念。

解决方案

在@Transactional注解上加一个参数:rollbackFor = Exception.class,这样所有异常都会回滚。或者具体指定你要回滚的异常类型。

我现在基本都会习惯性地加上rollbackFor = Exception.class,避免遗漏。


五、数据库引擎不支持事务

这个现在比较少见了,但面试还是要说一下。

问题描述

MySQL有多种存储引擎,InnoDB支持事务,但MyISAM不支持事务。如果你的表用的是MyISAM引擎,加了@Transactional也没用。

实际情况

现在MySQL默认都是InnoDB了,基本不会遇到这个问题。但如果是很老的项目,或者从别的地方迁移过来的数据库,可能会有MyISAM表。

检查方法

可以在数据库里执行show create table命令,看一下ENGINE是什么。如果是MyISAM,改成InnoDB就行了。


六、没有被Spring管理(没有@Service等注解)

这个属于比较低级的错误,但新手容易犯。

问题描述

如果你的类没有加@Service、@Component这些注解,或者不是通过@Bean方式注册到Spring容器的,那Spring根本不管理这个类,@Transactional自然也不会生效。

常见场景

比如你自己new了一个Service对象,而不是通过@Autowired注入的,这个对象就不是Spring管理的,事务肯定不生效。

解决方案

  • 确保类被Spring容器管理,要么加@Service等注解,要么在配置类里用@Bean注册
  • 使用的时候一定要通过@Autowired或者@Resource注入,不要自己new

七、事务传播类型设置不当

这个需要对事务传播机制有一定理解。

问题描述

如果你把propagation设置成了NOT_SUPPORTED、NEVER这种,那方法就不会在事务中执行。或者设置成REQUIRES_NEW,会开启独立事务,可能不是你想要的效果。

实际案例

我见过有同事为了解决某个问题,把propagation改成了NOT_SUPPORTED,结果方法里的数据库操作没了事务保护,出现了数据不一致。

建议

如果不是特别清楚传播机制,就不要乱改,用默认的REQUIRED就好。需要修改的场景其实很少,主要是记录日志、批量处理这些特殊情况。


八、多线程调用

这个坑比较隐蔽,但实际项目中确实会遇到。

问题描述

Spring的事务是和线程绑定的,事务信息存在ThreadLocal里。如果你在一个事务方法里开了新线程,新线程里的数据库操作不会在原来的事务中。

实际案例

我之前做一个批量导入功能,为了提高性能,在事务方法里用了线程池并行处理。结果发现出错的时候,部分数据回滚了,部分数据提交了,数据乱了。

原因分析

因为新线程拿不到主线程的事务上下文,每个线程是独立的事务(如果有的话),或者根本没事务。

解决方案

  • 要么不用多线程
  • 要么把事务放到子线程里,每个子线程独立事务
  • 要么用分布式事务框架

总之,不能天真地以为开了多线程还能共享事务。


九、方法是final的(CGLIB代理时)

这个和第二点原因类似。

问题描述

如果用的是CGLIB代理(没有接口的情况),目标方法被声明为final,那这个方法无法被子类覆盖,事务就不会生效。

常见原因

有些开发者习惯把不希望被重写的方法标记为final,但如果这个方法需要事务,就会有问题。

解决方案

把final去掉。如果实在需要防止子类重写,那就定义一个接口,用JDK动态代理。


十、类是final的

这个更直接。

问题描述

如果你的Service类本身被声明为final,那CGLIB根本没法为它创建子类,代理就创建不了,所有事务注解都失效。

常见场景

不常见,但如果你从其他框架迁移代码过来,或者用了某些代码生成工具,可能会遇到。

解决方案

把final class改成普通class。


十一、没有配置事务管理器或者没开启事务支持

这个属于配置问题。

问题描述

Spring Boot项目一般会自动配置,但如果是传统Spring项目,需要手动配置TransactionManager,或者加@EnableTransactionManagement注解。如果这些配置缺失,@Transactional不会生效。

检查方法

  • 启动时看看Spring的日志,会提示有没有注册事务管理器
  • 或者故意制造一个异常,看看会不会回滚,就知道事务有没有生效了

十二、隔离级别或传播级别数据库不支持

这个比较少见,但也要提一下。

问题描述

比如你设置了某个隔离级别,但数据库不支持,事务行为可能不符合预期。或者用了NESTED传播级别,但数据库不支持Savepoint,也会有问题。

实际情况

大部分主流数据库都支持标准的隔离级别,这个问题现在不太常见。NESTED在MySQL InnoDB上是支持的,但某些老版本或其他数据库可能不支持。


总结

最常见的失效原因Top 5

  1. 同类内部调用 - 这是最最常见的,占了80%的坑
  2. 异常被catch了没抛出 - 很隐蔽,容易忽略
  3. 方法不是public - 初学者容易犯
  4. 抛的是CheckedException但没指定rollbackFor - 容易被忽略
  5. 没有被Spring管理 - 自己new的对象不生效

实践建议

  • 养成好习惯,事务方法都写成public
  • 不要在类内部直接调用事务方法,拆分到不同Service
  • try-catch后一定要把异常重新抛出去
  • @Transactional上加rollbackFor = Exception.class,保险起见
  • 确保类被@Service等注解标记,通过@Autowired注入使用
  • 多线程的场景,重新设计事务边界

排查技巧

如果发现事务不生效,可以按以下步骤排查:

  1. 先看类有没有被Spring管理
  2. 再看方法是不是public
  3. 然后看是不是内部调用
  4. 最后看异常有没有被吞掉

基本上90%的问题都能通过这四步找到。


附录:常见问题对照表

失效场景 原因 解决方案 常见程度
同类内部调用 没走代理对象 拆分到不同Service ⭐⭐⭐⭐⭐
方法不是public 代理无法覆盖 改为public ⭐⭐⭐⭐
异常被捕获 Spring感知不到异常 重新抛出异常 ⭐⭐⭐⭐
CheckedException 默认不回滚 加rollbackFor ⭐⭐⭐
数据库引擎 MyISAM不支持事务 改为InnoDB ⭐⭐
未被Spring管理 不是Spring Bean 加@Service注解 ⭐⭐⭐
传播类型不当 配置错误 用默认REQUIRED ⭐⭐
多线程调用 ThreadLocal限制 重新设计事务边界 ⭐⭐
final方法 无法覆盖 去掉final
final类 无法生成子类 去掉final
配置缺失 未启用事务支持 检查配置
数据库不支持 特性不兼容 更换配置或数据库

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

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

相关文章

Spark GIS:分布式计算框架下的空间数据分析

Spark GIS实战:用分布式计算破解大规模空间数据处理难题 副标题:基于GeoSpark的空间查询、分析与可视化全流程指南 摘要/引言 问题陈述 你是否遇到过这样的困境?用ArcGIS处理10GB的GPS轨迹数据时,软件崩溃了3次;用…

2023年NOC大赛创客智慧编程赛项Python复赛模拟题(一)

更多内容和历年真题请查看网站:【试卷中心 -----> NOC ----> Python ----> 复赛】 网站链接 青少年软件编程历年真题模拟题实时更新 2023年NOC大赛创客智慧编程赛项Python复赛模拟题(一) 一、编程题 第 1 题 NOC大赛创客智慧…

2023年NOC大赛创客智慧编程赛项Python复赛模拟题(二)

更多内容和历年真题请查看网站:【试卷中心 -----> NOC ----> Python ----> 复赛】 网站链接 青少年软件编程历年真题模拟题实时更新 2023年NOC大赛创客智慧编程赛项Python复赛模拟题(二) 一、编程题 第 1 题 NOC大赛创客智慧编程…

Python大数据项目推荐:基于Hadoop+Spark电商用户行为分析毕设 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡如果你遇到具体的…

Flutter for OpenHarmony 剧本杀组队App实战22:快速匹配功能实现

引言 快速匹配功能帮助用户自动寻找合适的队伍,无需手动浏览组队列表。本篇将实现带有匹配动画的快速匹配页面。快速匹配是现代社交应用的重要功能,通过算法自动配对用户,大大提升了用户体验和应用的活跃度。这个功能特别适合剧本杀组队场景…

【计算机毕设选题】基于Spark的双十一美妆数据可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡如果你遇到具体的…

Reap

Reap (/riːp/) is a verb. Its core concept revolves around *1) to cut and gather a crop; 2) to receive something as a consequence of your actions. It connects ideas of harvesting, effort, and conseque…

信号处理仿真:滤波器设计与仿真_23.滤波器设计与仿真在雷达系统中的应用

23. 滤波器设计与仿真在雷达系统中的应用 23.1 雷达系统中的基本滤波器需求 雷达系统在信号处理中对滤波器的需求非常具体。雷达信号通常受到多种噪声和干扰的影响,包括多路径效应、杂波、热噪声等。因此,滤波器的设计和仿真在雷达系统中发挥着关键作用…

信号处理仿真:滤波器设计与仿真_24.滤波器设计与仿真在控制工程中的应用

24. 滤波器设计与仿真在控制工程中的应用 在控制工程中,滤波器设计与仿真是一个非常重要的环节。滤波器可以用于信号的预处理、噪声的抑制、系统的稳定性分析以及控制律的实现等。本节将详细介绍滤波器在控制工程中的应用,包括常见的滤波器类型、设计方法…

性价比对比视角|四款热门机型性价比深度拆解

高中学习机市场,5000-9000元价位是主流,很多家长和学生在这个价位纠结,不知道该选哪款,担心“花高价买不到好产品”。本次聚焦性价比,从“价格、核心配置、实用性、长期价值”四大维度,深度拆解清北道远、学而思…

中国智能体应用现状与企业实践

中国智能体应用现状与企业实践中国智能体应用的行业全貌,核心逻辑是 “基础治理缺失制约落地,技术 + 方法论双轮驱动破局”,以下是分层解读:行业现状:“热市场” 与 “冷落地” 的矛盾突出市场热度高:涌入智能体…

信号处理仿真:滤波器设计与仿真_18.实时信号处理仿真

18. 实时信号处理仿真 18.1 实时信号处理的基本概念 实时信号处理是指在信号采集或生成的同时进行处理,以满足特定的时延要求。在许多应用中,如通信系统、音频处理、视频处理等,实时处理是必不可少的。实时信号处理的关键在于系统能够在限定…

大模型应用文本理解OpenAI使用入门篇

前言 本文介绍使用OpenAI完成特定文本理解任务。 构建环境 本地安装Ollama或通过阿里云百炼平台获取API_KEY ,https://bailian.console.aliyun.com/cn-beijing/?spma2c4g.11186623.0.0.2dab6323kanAoF&tabmodel#/api-key,配置到环境变量中后需要重启电脑生效。…

亲测好用自考必看TOP8AI论文软件测评

亲测好用自考必看TOP8AI论文软件测评 2026年自考论文写作工具测评:为何值得一看 随着人工智能技术的不断进步,越来越多的自考生开始借助AI论文软件提升写作效率。然而,面对市场上五花八门的产品,如何选择真正适合自己的工具成为一…

五大智能建站工具真实测评:不画饼,真能上线那种

最近在搞一个支付平台的项目,我前端水平一般,实在不想花几周去啃UI框架。干脆试了一圈AI建站工具,结果还真发现了几个能打的——不光能生成页面,连后台逻辑、数据对接都能搞定,几分钟就能搭出能跑起来的应用。 下面这…

从FT到DFT

1. 核心世界观:两个平行宇宙傅里叶变换(DFT)是连接这两个宇宙的传送门:左边:时域 (Time Domain)这是我们生活的世界。信号随时间变化(比如麦克风录到的电压波动)。主角: &#xff08…

Excel精准匹配的利刃:EXACT函数实战教程——从大小写判断到多条件统计

当简单的等号无法满足你的匹配需求时,EXACT函数就是那把打开精准数据世界的钥匙。 在日常数据处理中,你是否遇到过这些困扰:需要严格区分大小写来比对产品编码或用户名?在统计时,需要将大小写不同的同一厂家数据分别处…

Redis中的Lua使用

Lua语言是在1993年由巴西一个大学研究小组发明,其设计目标是作为嵌入式程序移植到其他应用程序,它是由C语言实现的,虽然简单小巧但是功能强大,所以许多应用都选用它作为脚本语言,尤其是在游戏领域,暴雪公司…

智能车辆检索系统解析

智能车辆检索系统解析 卡口车辆智能检索系统,基于海量卡口图像和视频数据,通过视频识别分析、模式匹配及快速搜索等智能图像处理技术,完成车辆、车型、车身颜色、车辆特征、相似车辆搜索等业务功能,扩展基于治安卡口数据的应用。视…

人脸比对技术助力破案

人脸比对技术助力破案 通过人脸识别和大数据技术,在大数据平台上建立人脸库(失踪人脸库、重点人口库、追逃人员库、在逃人员库、暂住人口库等)进行实时的人脸搜索比对,支持照片比照片、视频流比照片,对案件相关人员进行…