当前位置: 首页 > news >正文

Java-141 深入浅出 MySQL Spring事务失效的常见场景与解决方案详解(3)

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!“快的模型 + 深度思考模型 + 实时路由”,持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年09月29日更新到:
Java-136 深入浅出 MySQL Spring Boot @Transactional 使用指南:事务传播、隔离级别与异常回滚策略
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

请添加图片描述

事务失效场景(3)

参考来源

● https://heapdump.cn/article/5542790

续接上节

rollbackFor 配置错误

在 Spring 事务管理中,@Transactional 注解的 rollbackFor 属性用于指定哪些异常类型会触发事务回滚。以下是更详细的说明:

  1. 默认回滚规则

    • 默认情况下,Spring 事务会在遇到 RuntimeException 或其子类以及 Error 及其子类时自动回滚。
    • 例如:NullPointerExceptionRuntimeException 子类)或 OutOfMemoryErrorError 子类)都会触发自动回滚。
  2. rollbackFor 的明确指定

    • 当显式指定 rollbackFor=Error.class 时,只有 Error 及其子类异常会触发回滚。
    • 其他异常类型(包括 Exception)不会触发回滚,即使它们是未检查异常。
  3. 异常继承关系的影响

    • ErrorException 都是 Throwable 的子类,但彼此没有继承关系。
    • 如果抛出的异常是 Exception 或其子类(如 IOException),但 rollbackFor 仅指定了 Error.class,则事务不会回滚。
  4. 典型问题示例

   @Transactional(rollbackFor = Error.class)public void updateData() throws Exception {// 业务逻辑throw new Exception("自定义异常"); // 不会触发回滚}
  • 这里抛出的是 Exception,而 rollbackFor 仅覆盖 Error,因此事务不会回滚。
  1. 解决方案
    • 如果需要回滚 Exception,应明确指定:
     @Transactional(rollbackFor = {Error.class, Exception.class})```- 或者使用更通用的方式:```java@Transactional(rollbackFor = Throwable.class) // 覆盖所有异常
  1. 实际应用场景
    • 在需要严格区分异常处理的场景中(如仅对系统级错误回滚),可以限定 rollbackFor=Error.class
    • 在大多数业务场景中,建议至少包含 RuntimeException 和自定义业务异常。

这里我们自定了几个异常类,BaseException 继承了 Exception,同时有两个子类:
● ParamException 参数异常
● ResultException 结果异常

此时,rollbackFor 只有 ResultException,这种情况下,会导致事务失效不会回滚:

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {private final UnitInfoMapper unitInfoMapper;@Transactional(rollbackFor = ResultException.class)@Overridepublic String test01() throws Exception {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);if (1 == 1) {throw new ParamException("ERROR");}return "ok";}
}

执行完后,数据库的情况:
在这里插入图片描述

事务注解被覆盖导致事务失效

事务失效问题分析:子类覆盖父类事务的情况

问题描述

在Spring框架的事务管理中,当子类重写父类带有事务注解(@Transactional)的方法时,可能会出现事务失效的情况。这是因为Spring的事务管理是基于AOP代理实现的,而子类方法的覆盖行为可能导致事务注解未被正确识别和应用。

典型场景示例

@Service
public class ParentService {@Transactionalpublic void parentMethod() {// 业务逻辑}
}@Service
public class ChildService extends ParentService {@Overridepublic void parentMethod() {// 重写父类方法,但未添加@Transactional注解super.parentMethod();}
}

失效原因分析

  1. 代理机制问题:Spring默认使用JDK动态代理或CGLIB代理来实现事务管理
  2. 注解继承:@Transactional注解默认不会被子类继承
  3. 调用方式:如果子类通过this调用方法,会绕过代理对象

解决方案

  1. 显式添加事务注解
@Override
@Transactional  // 显式添加事务注解
public void parentMethod() {super.parentMethod();
}
  1. 使用接口定义事务方法
public interface TransactionalService {@Transactionalvoid transactionalMethod();
}@Service
public class ActualService implements TransactionalService {@Overridepublic void transactionalMethod() {// 实现逻辑}
}
  1. 配置代理模式
@EnableTransactionManagement(proxyTargetClass = true)  // 强制使用CGLIB代理
  1. 避免内部调用
@Service
public class SomeService {public void outerMethod() {innerMethod();  // 错误:内部调用会绕过代理this.innerMethod();  // 同样错误}@Transactionalpublic void innerMethod() {// 业务逻辑}
}

最佳实践

  1. 将事务方法定义在接口中
  2. 避免在类内部调用事务方法
  3. 使用final修饰不打算被重写的事务方法
  4. 考虑使用AspectJ模式替代代理模式
  5. 在重写方法时显式声明事务属性

调试技巧

  1. 开启DEBUG日志查看事务创建和提交情况
  2. 检查实际调用的方法是否经过代理
  3. 使用TransactionSynchronizationManager.isActualTransactionActive()验证事务状态

比如,在 Service 上,我们定义了正常的类:

public interface TestService {@TransactionalString test01() throws Exception;}

而在实现类上,我们重新定义了事务的情况:

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {private final UnitInfoMapper unitInfoMapper;@Transactional(rollbackFor = ResultException.class)@Overridepublic String test01() throws Exception {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);if (1 == 1) {throw new ParamException("ERROR");}return "ok";}
}

此时执行后,没有回滚,数据库写入了数据:
在这里插入图片描述

嵌套事务

事务失效问题:嵌套事务场景分析

嵌套事务的基本原理

嵌套事务是指在一个事务方法(A)中调用另一个事务方法(B)时发生的事务交互。在Spring等框架中,默认的事务传播行为是REQUIRED,即:

  • 如果当前存在事务,就加入该事务
  • 如果当前没有事务,就新建一个事务

典型失效场景

当方法A调用方法B时:

  1. 方法A开启事务(事务A)
  2. 方法B被调用,由于传播行为是REQUIRED,方法B会加入事务A
  3. 方法B执行过程中发生异常并回滚
  4. 由于方法B是加入方法A的事务,所以整个事务A都会被回滚

具体示例

@Service
public class OrderService {@Transactionalpublic void processOrder(Order order) { // 方法A// 订单处理逻辑inventoryService.reduceStock(order); // 调用方法B// 其他订单处理逻辑}
}@Service
public class InventoryService {@Transactionalpublic void reduceStock(Order order) { // 方法B// 库存扣减逻辑if (stock < order.getQuantity()) {throw new RuntimeException("库存不足"); // 触发回滚}// ...}
}

解决方案

  1. 修改传播行为:将方法B的事务传播行为改为REQUIRES_NEW,这样方法B会开启新事务
   @Transactional(propagation = Propagation.REQUIRES_NEW)public void reduceStock(Order order) { ... }
  1. 捕获内部异常:在方法A中捕获方法B的异常,避免异常传播到方法A
   try {inventoryService.reduceStock(order);} catch (Exception e) {// 处理异常但不抛出}
  1. 使用编程式事务:在需要更细粒度控制时使用编程式事务管理

实际影响

这种嵌套事务问题会导致:

  • 主业务逻辑被意外回滚
  • 数据不一致性
  • 难以追踪的问题根源
  • 业务逻辑与预期不符
    比如如下情况,会导致两条数据都没有插入,回滚掉了:
@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {private final UnitInfoMapper unitInfoMapper;@Transactional@Overridepublic String test01() throws Exception {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);test02();return "ok";}@Overridepublic String test02() {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);if (1 == 1) {throw new RuntimeException("ERROR");}return "ok";}
}

下面这种写法结果也是一样的,都回滚掉了:

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {private final UnitInfoMapper unitInfoMapper;@Transactional@Overridepublic String test01() throws Exception {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);test02();if (1 == 1) {throw new RuntimeException("ERROR");}return "ok";}@Overridepublic String test02() {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);return "ok";}
}
http://www.dtcms.com/a/442025.html

相关文章:

  • 多功能集成工具软件,图片音视频处理一体化
  • 大型网络建站公司响应式网站的意义
  • linux使用yum安装数据库
  • php-cve篇(CVE-2019-11043CVE-2012-1823)
  • PyTorch, TensorFlow, FastAPI, LangChain, Hugging Face深度学习框架
  • 单片机学习中的一些简单总结
  • icp备案域名购买seo品牌优化
  • Oracle OCP认证考试题目详解082系列第46题
  • day85——区域和的检索(LeetCode-303)
  • 安德烈·卡帕西:深入探索像ChatGPT这样的大语言模型内容列表
  • 网站框架
  • 网站做电商销售需要注册吗有没有帮别人做网站
  • 【人工智能-03-04】20250920 人工智能第二学期课程视频《计算机网络技术 》根据视频时长刷分,手动+Python+IDM下载,更改视屏名字
  • LTU-AS:一种具备音频感知、识别、理解的大模型架构
  • Photoshop - Photoshop 工具栏(2)矩形框选工具
  • 湖南网站seo自己免费做网站有什么用
  • 理解Vivado的IP综合策略:“Out-of-Context Module Runs
  • CSS 预处理器:Sass的基本用法、核心特性
  • 【Ai改变生活】PotPlayer 史诗级更新!实时字幕生成+实时翻译!
  • 各大网站vip接口建设一个网站如何做推广
  • 深入理解ArrayList与LinkedList:Java集合框架核心对比(含实战案例+面试考点)
  • Gorm学习笔记 - 概述
  • wordpress 双分页北京朝阳区优化
  • 7-1 社会工程学攻击
  • 浪浪山 iOS 奇遇记:给 APP 裹上 Liquid Glass “琉璃罩”(下集)
  • Leetcode 215. 数组中的第K个最大元素 快速排序 / 堆排序
  • 网站建设排名奇艺地域邢台建设一个企业网站
  • 电子商务网站建设员网站建设维护文档
  • QT肝8天18--用户角色管理
  • 【开题答辩实录分享】以《基于Python的新能源汽车管理系统的设计与实现》为例进行答辩实录分享