事务不回滚

本文深入探讨了在使用特定数据库引擎时,事务不回滚的问题。通过详细分析,指出MyISAM引擎不支持事务回滚,而InnoDB引擎则支持。文章提供了检查和解决问题的步骤,包括检查代码权限、异常处理、连接管理和事务代理。

事务不回滚解决日志

感谢博主
https://www.jianshu.com/p/ab70131452ce

先说明自己事务不会滚的原因是数据库引擎不对,项目中部分表结构使用的是ENGINE=MyISAM
使用命令:show create table table_name; 查看表使用的存储引擎

这里插入图片描述](https://img-blog.csdnimg.cn/2020062411205420.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Zlbmd6eWY=,size_16,color_FFFFFF,t_70)


按照网上的教程:事务不会滚的原因有一下几种,对于每一种,都要认真检查确保没有问题。

  1. @Transactional 注解必须作用在public的权限管理上,其余的权限管理关键字并不会使事物生效
  2. 检查数据库是否支持事务,注意有的表示innodb 有的是mysam的话那就大坑特坑了。使用命令:show create table table_name 查看表结构,查看引擎选择的是什么。
  3. 检查代码块是否抛出异常,且事务的rollback的异常是抛出异常或者是抛出异常的父类(默认是RuntimeException)
  4. 检查事务覆盖的代码块中的所有Connection是否都被这个事务覆盖(debug检查所有connection的autoCommit属性是不是被事务改成了false)
  5. @Transactional 的事务开启 ,或者是基于接口的 或者是基于类的代理被创建。在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。

针对以上第四条内容:将日志改为debug 级别,就可以查看到事务是否开启了,是否回滚了

例如:
在这里插入图片描述


解决这个问题还是有必要写一篇文章的。因为这个问题很有迷惑性:

1. 发现问题并检查代码
  1. 一个方法中,权限修饰符public用的是正确的
  2. rollbackFor = RuntimeException.class 正确
  3. 启动类配置也正确
  4. debug模式下看到了事务的开启,事务的回滚
  5. 并且方法中的第二个sql的确回滚了,但是就是第一个sql没有回滚
    在这里插入图片描述
2. 重新对可疑的点再排查

自己检查了网上了步骤后,对数据库的存储引擎有点怀疑的,第一开始自己查看的是数据库的引擎,感觉没有问题, 但是是感觉,不是证据,无法证明引擎使用的是对的。
在这里插入图片描述
看到上图中默认是InnoDB 感觉没有问题,就略过了。还是多亏最后检查表结构,才找打了原因!mylsam 引擎是不支持回滚的。
在这里插入图片描述

要实现“**父事务回滚时子事务必须回滚,但子事务回滚时父事务回滚**”的需求,需结合Spring的事务传播机制和异常处理策略。以下是具体方案: --- ### **核心机制:`REQUIRES_NEW` + 异常分层处理** 1. **子事务独立**: 子事务使用`REQUIRES_NEW`传播行为,确保其运行在独立的事务上下文中,与父事务隔离。 2. **父事务感知子事务失败**: 子事务需通过**自定义异常**将失败信号传递给父事务,父事务捕获该异常后决定是否回滚自身(但根据需求,父事务回滚)。 3. **关键点**: - 子事务回滚时,**抛出会触发父事务回滚的异常**(如`RuntimeException`)。 - 父事务通过返回值或状态判断子事务是否成功,**依赖异常传播**触发回滚。 --- ### **具体实现方案** #### 方案1:子事务捕获异常 + 父事务主动检查 ```java @Service public class ParentService { @Autowired private ChildService childService; @Transactional public void parentMethod() { // 业务逻辑1 boolean childSuccess = false; try { childSuccess = childService.childMethod(); // 子事务独立执行 } catch (CustomChildException e) { // 子事务失败,但父事务回滚(仅记录日志或处理) log.error("子事务失败: {}", e.getMessage()); } if (!childSuccess) { // 可选:父事务根据子事务结果执行其他逻辑(回滚) throw new ParentBusinessException("父事务因子事务失败终止,但回滚"); } // 业务逻辑2(仅当子事务成功时执行) } } @Service public class ChildService { @Transactional(propagation = Propagation.REQUIRES_NEW) public boolean childMethod() { try { // 业务逻辑(可能失败) if (someCondition) { throw new RuntimeException("模拟子事务业务失败"); } return true; // 成功 } catch (Exception e) { // 捕获所有异常,抛出到父事务 log.error("子事务内部错误: {}", e.getMessage()); return false; // 返回失败状态 } } } ``` #### 方案2:子事务抛出特定异常 + 父事务选择性回滚(需调整配置) ```java @Service public class ParentService { @Transactional(rollbackFor = ParentRollbackException.class) // 仅对特定异常回滚 public void parentMethod() { try { childService.childMethod(); // 子事务独立执行 } catch (ChildRollbackException e) { // 子事务回滚,但父事务回滚(因为未触发父事务的rollbackFor) log.error("子事务回滚: {}", e.getMessage()); throw new ParentBusinessException("父事务继续执行"); } } } @Service public class ChildService { @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public void childMethod() { // 业务逻辑 if (someCondition) { throw new ChildRollbackException("子事务强制回滚"); // 不会触发父事务回滚 } } } // 自定义异常 class ChildRollbackException extends RuntimeException {} class ParentRollbackException extends RuntimeException {} class ParentBusinessException extends RuntimeException {} ``` --- ### **关键配置说明** 1. **子事务传播行为**: ```java @Transactional(propagation = Propagation.REQUIRES_NEW) ``` - 确保子事务在独立事务中运行,回滚影响父事务。 2. **父事务回滚规则**: - 通过`rollbackFor`指定仅对特定异常回滚(如`ParentRollbackException`),避免被子事务异常触发。 3. **异常隔离**: - 子事务抛出的异常需与父事务的`rollbackFor`列表**重叠**,防止父事务回滚。 --- ### **方案对比** | 方案 | 优点 | 缺点 | 适用场景 | |------|------|------|----------| | **方案1**(返回值) | 逻辑清晰,异常隔离性强 | 需手动检查返回值 | 需要精确控制父子事务流程 | | **方案2**(特定异常) | 利用Spring默认机制,代码简洁 | 需定义多层异常类 | 复杂业务中需明确异常分类 | --- ### **最佳实践建议** 1. **优先使用方案1**: - 通过返回值明确控制流程,避免异常传播的复杂性。 - 示例: ```java // 父事务 @Transactional public void parentMethod() { if (!childService.childMethod()) { // 处理子事务失败(回滚) } } // 子事务 @Transactional(propagation = Propagation.REQUIRES_NEW) public boolean childMethod() { try { // 业务逻辑 return true; } catch (Exception e) { return false; } } ``` 2. **日志与监控**: - 记录子事务失败原因,便于排查问题。 3. **事务超时设置**: - 为子事务配置合理的超时时间,避免长时间阻塞父事务。 --- ### **总结** - **父事务回滚 → 子事务回滚**: 默认行为(若子事务在父事务中且传播行为为`REQUIRED`)。但根据需求,子事务应独立(`REQUIRES_NEW`),因此需通过其他方式实现父事务回滚时强制终止子事务(如调用子事务的取消接口)。 - **子事务回滚 → 父事务回滚**: 通过`REQUIRES_NEW` + 异常隔离或返回值控制实现。 最终方案需根据业务对一致性的要求权衡设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值