完美解决春声明式事务不回滚的问题

  

疑问,确实像往常一样在服务上添加了注解,为什么查询数据库时还是发现有数据不一致的情况,想想肯定是事务没起作用,出现异常的时候数据没有回滚。于是就对相关代码进行了一番测试,结果发现一下踩进了两个坑,确实是事务未回滚导致的数据不一致。

  

  

  

  

实际应用中很少使用

  

通过使用TransactionTemplate手动管理事务

  

  

开发中推荐使用(代码侵入最少)

  

春季的声明式事务是通过AOP实现的

  

主要掌握声明式的事务管理。

  

  

总结一下导致事务不回滚的两个原因,一是服务类内部方法调用,二是try…catch异常。

  

  

大概就是服务中有一个方法,会内部调用方法B方法一个没有事务管理,方法B采用了声明式事务,通过在方法上声明事务性的注解来做事务管理。示例代码如下:

        @ service   公共类RabbitServiceImpl实现RabbitService {      @ autowired   私人RabbitDao RabbitDao;   @ autowired   私人TortoiseDao TortoiseDao;      @Override   公共兔子methodA(字符串名称){   返回methodB(名称);   }      @ transactional(传播=Propagation.REQUIRED)   公共布尔methodB(字符串名称){   rabbitDao.insertRabbit(名称);   tortoiseDao.insertTortoise(名称);   返回true;   }      }      

单元测试代码如下:

        公开课RabbitServiceImplTest {      @ autowired   私人RabbitService RabbitService;//事务未开启   @Test   公共空间外种皮(){   rabbitService.methodA(“兔子”);   }//事务开启   @Test   公共空间的(){   rabbitService.methodB(“兔子”);   }   }      

从上一节中可以看的到,声明式事务是通通过AOP动态代理实现的,这样会产生一个代理类来做事务管理,而目标类(服务)本身是不能感知代理类的存在的。

  

对于加了@ transactional注解的方法来说,在调用代理类的方法时,会先通过拦截器TransactionInterceptor开启事务,然后在调用目标类的方法,最后在调用结束后,TransactionInterceptor会提交或回滚事务,大致流程如下图:

  

完美解决春声明式事务不回滚的问题

  

总结,在方法的中调用方法B,实际上是通过“这”的引用,也就是直接调用了目标类的方法,而非通过弹簧上下文获得的代理类,所以事务是不会开启的。

  

  

在一段业务逻辑中对数据库异常进行了处理,使用了try…catch子句捕获异常并抛出了一个自定义异常,这种情况导致了事务未回滚,示例代码如下:

        @ transactional(传播=Propagation.REQUIRED)   公共布尔methodB(字符串名称)抛出BizException {   尝试{   rabbitDao.insertRabbit(名称);   tortoiseDao.insertTortoise(名称);   }捕捉(异常e) {   把新的BizException (ReturnCode.EXCEPTION。代码,ReturnCode.EXCEPTION.msg);   }   返回true;   }      

BizException的定义如下:
  

        公开课BizException延伸异常{//自定义异常   }      

上面代码中的声明式事务在出现异常的时候,事务是不会回滚的。在代码中我虽然捕获了异常,但是同时我也抛出了异常,为什么事务未回滚呢?猜测是异常类型不对,于是开始查询原因,翻看了春天的官方文档,找到了答案。下面是翻译自春官网。

  

<强> 17.5.3声明式事务的回滚

  

上一节中介绍了如何设置开启春天事务,一般在你的应用的服务层代码中设置,这一节将介绍在简单流行的声明式事务中如何控制事务回滚。

  

在Spring框架的事务框架中推荐的事务回滚方法,是在当前执行的事务上下文中抛出一个异常。如果异常未被处理,当抛出异常调用堆栈的时候,Spring框架的事务框架代码将捕获任何未处理的异常,然后并决定是否将此事务标记为回滚。

  

在默认配置中,Spring框架的事务框架代码只会将出现运行时,不异常的事务标记为回滚,也就是说事务中抛出的异常时RuntimeException或者是其子类,这样事务才会回滚(默认情况下误差也会导致事务回滚)。在默认配置的情况下,所有的检查异常都不会引起事务回滚。

  

完美解决春声明式事务不回滚的问题