事务不回滚

一.问题

      今天在项目中遇到一个奇怪的问题,大家都知道Spring有事务回滚的机制,可是自己的事务却没有回滚,导致程序异常之后还是对数据库进行了操作,产生了一系列的脏数据,这是一件非常可怕的事。尤其一个方法里对多个表进行操作并且会产生很多数据,一旦不回滚后果可想而知。

二.起因

 1.首先事务回滚自己在方法上面加了注解

2.下面有图有一个信息订阅的方法 :首先保存订阅信息  然后调用回调方法  

结果发生异常了,按道理来说事务会回滚,可是奇怪的是订阅信息被保存了,多尝试了几次都是这样很奇怪。后来查了下事务回滚原理,才发现问题。原来默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚,而自己把异常捕捉了既没有处理也没有往外Throw所以Spring根本不知道发生异常,自然不会帮你回滚。

3.后来经try{}catch{}去除之后,如下图

再去尝试了一下,果然和自己想的一样,事务进行了回滚。

三.总结

   1.有些事情知道是知道,但是在用起来还是会踩坑,不要去想当然。

   2.异常不要随意去捕捉,如果没有特殊需要的时候,或者自己没有一些自定义异常的时候,往往好心办坏事。

   3.写东西要上心,不是实现功能就可以,一定要考虑后果,不然没问题还好,一旦出问题,就会很严重,虽然是小事,还是想     写出来和大家共勉,不要踩同样的坑。

要实现“**父事务回滚时子事务必须回滚,但子事务回滚时父事务回滚**”的需求,需结合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` + 异常隔离或返回值控制实现。 最终方案需根据业务对一致性的要求权衡设计。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值