全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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

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

下面总结一下经验教训:

Spring事务的管理操作方法

编程式的事务管理

实际应用中很少使用

通过使用TransactionTemplate 手动管理事务

声明式的事务管理

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

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

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

spring事务不回滚的两个原因

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

1. Service类内部方法调用

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

@Service
public class RabbitServiceImpl implements RabbitService {

  @Autowired
  private RabbitDao rabbitDao;
  @Autowired
  private TortoiseDao tortoiseDao;

  @Override
  public Rabbit methodA(String name){
    return methodB(name);
  }

  @Transactional(propagation = Propagation.REQUIRED)
  public boolean methodB(String name){
    rabbitDao.insertRabbit(name);
    tortoiseDao.insertTortoise(name);
    return true;
  }

}

单元测试代码如下:

public class RabbitServiceImplTest {

  @Autowired
  private RabbitService rabbitService;

  // 事务未开启
  @Test
  public void testA(){
    rabbitService.methodA("rabbit");
  }

  // 事务开启
  @Test
  public void testB(){
    rabbitService.methodB("rabbit");
  }
}

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

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

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

2. try...catch异常

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

@Transactional(propagation = Propagation.REQUIRED)
public boolean methodB(String name) throws BizException {
  try {
    rabbitDao.insertRabbit(name);
    tortoiseDao.insertTortoise(name);
  } catch (Exception e) {
    throw new BizException(ReturnCode.EXCEPTION.code, ReturnCode.EXCEPTION.msg);
  }
  return true;
}

BizException的定义如下:

public class BizException extends Exception {
  // 自定义异常
}

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

17.5.3 声明式事务的回滚

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

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

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

注:Unchecked Exception包括Error与RuntimeException. RuntimeException的所有子类也都属于此类。另一类就是checked Exception。

你可以精确的配置异常类型,指定此异常类事务回滚,包括 checked 异常。下面的xml代码片段展示了如何配置checked异常引起事务回滚,应用自定义异常类型:

<tx:advice id="txAdvice" transaction-manager="txManager">
 <tx:attributes>
 <tx:method name="get*" read-only="true" rollback-for="Exception"/>
 <tx:method name="*"/>
 </tx:attributes>
</tx:advice>

与其有同等作用的注解形式如下:

@Transactional(rollbackForClassName={"Exception"})
或者
@Transactional(rollbackFor={Exception.class})

在你遇到异常不想回滚事务的时候,同样的你也可指定不回滚的规则,下面的一个例子告诉你,即使遇到未处理的 InstrumentNotFoundException 异常时,Spring FrameWork 的事务框架同样会提交事务,而不回滚。

<tx:advice id="txAdvice">
 <tx:attributes>
 <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
 <tx:method name="*"/>
 </tx:attributes>
</tx:advice>

与其有同样作用的注解形式如下:   

@Transactional(noRollbackForClassName={"InstrumentNotFoundException"})
或者
@Transactional(noRollbackFor={InstrumentNotFoundException.class})

还有更灵活的回滚规则配置方法,同时指定什么异常回滚,什么异常不回滚。当Spring FrameWork 的事务框架捕获到一个异常的时候,会去匹配配置的回滚规则来决定是否标记回滚事务,使用匹配度最强的规则结果。因此,下面的配置例子表达的意思是,除了异常 InstrumentNotFoundException 之外的任何异常都会导致事务回滚。

<tx:advice id="txAdvice">
 <tx:attributes>
 <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
 </tx:attributes>
</tx:advice>

你也可以通过编程式的方式回滚一个事务,尽管方法非常简单,但是也有非常强的代码侵入性,使你的业务代码和Spring FrameWork 的事务框架代码紧密的绑定在一起,示例代码如下:

public void resolvePosition() {
 try {
   // some business logic...
 } catch (NoProductInStockException ex) {
   // trigger rollback programmatically
   TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
 }
}

如果可能的话,强烈推荐您使用声明式事务方式回滚事务,对于编程式事务,如果你强烈需要它,也是可以使用的,but its usage flies in the face of achieving a clean POJO-based architecture.(没懂...)

看完官方文档这节内容找到了问题的答案,原来是因为我们自定义的异常不是 RuntimeException。我的解决办法是,在注解@Transactional中添加 rollbackFor={BizException.class}。可能你会问我为什么不将自定义异常修改为继承RuntimeException,因为我需要BizException是一个checked 异常。

以上这篇完美解决Spring声明式事务不回滚的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


# spring声明式事务回滚  # Spring声明式事务和@Aspect的拦截顺序问题的解决  # Spring的编程式事务和声明式事务详解  # SpringBoot 注解事务声明式事务的方式  # SpringMVC+MyBatis声明式事务管理  # Spring实现声明式事务的方法详解  # 不回  # 自定义  # 抛出  # 子类  # 给大家  # 来做  # 进行了  # 是一个  # 情况下  # 找到了  # 我也  # 如果你  # 文档  # 也有  # 是因为  # 子句  # 未处理  # 看了  # 也会  # 你可以 


相关文章: 如何配置WinSCP新建站点的密钥验证步骤?  个人网站制作流程图片大全,个人网站如何注销?  免费视频制作网站,更新又快又好的免费电影网站?  宝塔建站无法访问?如何排查配置与端口问题?  网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?  如何在橙子建站中快速调整背景颜色?  如何快速搭建高效简练网站?  制作假网页,招聘网的薪资待遇,会有靠谱的吗?一面试又各种折扣?  如何自定义建站之星模板颜色并下载新样式?  制作销售网站教学视频,销售网站有哪些?  Thinkphp 中 distinct 的用法解析  如何通过西部建站助手安装IIS服务器?  如何在Golang中实现微服务服务拆分_Golang微服务拆分与接口管理方法  浅析上传头像示例及其注意事项  定制建站方案优化指南:企业官网开发与建站费用解析  宿州网站制作公司兴策,安徽省低保查询网站?  哈尔滨网站建设策划,哈尔滨电工证查询网站?  存储型VPS适合搭建中小型网站吗?  如何实现建站之星域名转发设置?  建站IDE高效指南:快速搭建+SEO优化+自适应模板全解析  如何在阿里云虚拟服务器快速搭建网站?  保定网站制作方案定制,保定招聘的渠道有哪些?找工作的人一般都去哪里看招聘信息?  浅谈Javascript中的Label语句  实现点击下箭头变上箭头来回切换的两种方法【推荐】  早安海报制作网站推荐大全,企业早安海报怎么每天更换?  建站之星代理如何获取技术支持?  北京网站制作网页,网站升级改版需要多久?  已有域名如何快速搭建专属网站?  如何获取PHP WAP自助建站系统源码?  css网站制作参考文献有哪些,易聊怎么注册?  香港服务器租用每月最低只需15元?  建站之星代理如何优化在线客服效率?  建站之星五站合一营销型网站搭建攻略,流量入口全覆盖优化指南  网站插件制作软件免费下载,网页视频怎么下到本地插件?  如何在Ubuntu系统下快速搭建WordPress个人网站?  网站制作与设计教程,如何制作一个企业网站,建设网站的基本步骤有哪些?  建站之星各版本价格是多少?  网站制作培训多少钱一个月,网站优化seo培训课程有哪些?  香港服务器WordPress建站指南:SEO优化与高效部署策略  专业公司网站制作公司,用什么语言做企业网站比较好?  香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化  香港服务器建站指南:免备案优势与SEO优化技巧全解析  小建面朝正北,A点实际方位是否存在偏差?  建站之星下载版如何获取与安装?  建站之星如何快速更换网站模板?  做企业网站制作流程,企业网站制作基本流程有哪些?  大连网站制作公司哪家好一点,大连买房网站哪个好?  高端云建站费用究竟需要多少预算?  网站微信制作软件,如何制作微信链接?  如何解决VPS建站LNMP环境配置常见问题? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。