Spring-Java事物回滚失效处理

本文探讨了Java中Spring事务管理的回滚机制,特别是检查型异常与非检查型异常的区别及对事务的影响,并提供了几种解决方案。

spring-Java事物回滚失效处理最近在做项目中,无意间发现有个类在抛事物回滚操作,数据也正常的插入到数据库当中了,于是仔细查看看一下具体原因。

一切还是要从Java的检查型异常和非检查型异常说起。

那么什么是检查型异常什么又是非检查型异常呢?
 最简单的判断点有两个:
    1.继承自RuntimeException或Error的是非检查型异常,而继承自Exception的则是检查型异常(当然,RuntimeException本身也是Exception的子类)。
    2.对非检查型类异常可以不用捕获,而检查型异常则必须用try……catch语句块进行处理或者把异常交给上级方法处理,总之就是必须写代码处理它。

Java 的异常结构如下图。其中直接继承Exception的异常,必须捕获,属于检查型异常。


再回过来看我的代码:

1、方法名前面有

  1. @Transactional  

2、Spring的配置文件applicationContext-XXX.xml当中也有Spring事物的相关配置

  1. <bean id="transactionManager"  
  2.     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  3.     <property name="dataSource" ref="dataSource" />  
  4.     <property name="rollbackOnCommitFailure" value="true"></property>  
  5. </bean>  


但是为什么在Service层方法调用的时候,try……catch抛Exception异常已经提交的事物却没有回滚?

查看相关spring的文档后发现,原来spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。

代码中try……catch抛出的Exception异常,属于检查型异常,Spring的框架默认是不会进行回滚的。

在编程中对非检查型类异常可以不用捕获,而检查型异常则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。

所以必须在service捕获异常,然后再次手动 throw 一个非检查型异常,这样事务方才起效。例如:

  1. try{  
  2.     …………  
  3. catch (Exception e) {  
  4.     …………  
  5.     throw new BusinessException(e.getMessage());  
  6. }  

或者

catch (Exception e) {  
          e.printStackTrace();     
          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是这一句了,加上之后,如果doDbStuff2()抛了异常,                                                                                       //doDbStuff1()是会回滚的  
     } 

当然我们还有更简便的方法来解决这个问题,那就是通过注解参数改变默认的回滚方式 。

在@Transaction注解中定义了noRollbackFor和RollbackFor来指定某种异常是否回滚。

使用例:

    @Transaction(noRollbackFor=RuntimeException.class)

    @Transaction(RollbackFor=Exception.class)

所以上述的问题可以直接将@Transaction添加回滚参数@Transaction(RollbackFor=Exception.class) ,这样就改变了默认的事务处理方式。


启示 :

这就要求我们在自定义异常的时候,让自定义的异常继承自RuntimeException,这样抛出的时候才会被Spring默认的事务处理准确处理。
### 事务回滚失效问题分析与解决 在 Spring 应用中,事务回滚失效通常发生在事务被多次标记为回滚状态的情况下。例如,在一个事务被标记为 `rollback-only` 后,再次尝试回滚该事务可能导致异常[^1]。这种情况通常出现在多个事务边界嵌套或重复开启事务的场景中,尤其是在 AOP 切面、控制器和业务逻辑层中同时开启事务时[^2]。 #### 事务传播行为与隔离级别 Spring 中的事务传播行为 `PROPAGATION_REQUIRED` 表示如果当前有事务,则新建一个事务;如果已经存在事务,则加入该事务。这种行为可能导致事务在多个层级中被重复使用,进而导致事务状态被多次修改。例如,AOP 切面捕获异常并尝试回滚事务时,如果该事务已经在业务层被标记为回滚状态,则会导致事务状态冲突[^2]。 事务的隔离级别 `ISOLATION_DEFAULT` 表示使用数据库默认的隔离级别。虽然该设置通常不会直接导致事务回滚问题,但如果数据库本身对事务状态的管理存在限制,也可能间接影响事务的正常回滚。 #### 事务状态的底层结构 从底层事务状态的结构来看,事务的状态信息(如 `state = TRANS_DEFAULT` 和 `blockState = TBLOCK_DEFAULT`)表明事务尚未进入活跃状态或已回滚[^3]。如果事务在多个层级中被多次操作,可能导致状态变更不一致,从而导致事务回滚失败。 ```java @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT) public void someTransactionalMethod() { // 业务逻辑 } ``` #### 事务回滚失败的常见原因 1. **多层事务嵌套**:在多个层级(如 AOP、Controller、Service)中开启事务,可能导致事务状态被多次修改。 2. **异常处理不当**:捕获异常后未正确处理事务状态,导致事务被多次回滚。 3. **事务传播行为配置不当**:使用 `PROPAGATION_REQUIRED` 时,事务可能被多个方法共享,导致状态冲突。 4. **事务未正确提交或回滚**:事务状态未在适当的位置提交或回滚,导致状态不一致。 #### 解决方案建议 1. **避免多层事务嵌套**:确保事务仅在业务逻辑层开启,避免在 AOP 或控制器中重复开启事务。 2. **使用 `PROPAGATION_REQUIRES_NEW`**:在需要独立事务的场景中,使用 `PROPAGATION_REQUIRES_NEW` 传播行为,确保事务独立开启和提交。 3. **检查事务状态**:在捕获异常后,检查事务状态是否已被标记为回滚,避免重复回滚。 4. **合理配置事务隔离级别**:根据业务需求合理选择事务隔离级别,避免因隔离级别不当导致事务状态冲突。 ```java @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED) public void isolatedTransactionalMethod() { // 独立事务逻辑 } ``` 5. **日志追踪与调试**:通过日志追踪事务状态的变化,确保事务在预期的状态下执行,避免状态冲突。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值