Spring中事务嵌套这么用一定得注意了!!

这篇具有很好参考价值的文章主要介绍了Spring中事务嵌套这么用一定得注意了!!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

最近项目上有一个使用事务相对复杂的业务场景报错了。在绝大多数情况下,都是风平浪静,没有问题。其实内在暗流涌动,在有些异常情况下就会报错,这种偶然性的问题很有可能就会在暴露到生产上造成事故,那究竟是怎么回事呢?

问题描述

我们用一个简单的例子模拟下,大家也可以看看下面这段代码输出的结果是什么。

  1. 在类SecondTransactionService定义一个简单接口transaction2,插入一个用户,同时必然会抛出错误
@Override
@Transactional(rollbackFor = Exception.class)
public void transaction2() {
    System.out.println("do transaction2.....");
    User user = new User("tx2", "111", 18);
    // 插入一个用户
    userService.insertUser(user);
    // 跑错了
    throw new RuntimeException();
}
  1. 在另外一个类FirstTransactionService定义一个接口transaction1,它调用transaction2方法,同时做了try catch处理
@Override
@Transactional(rollbackFor = Exception.class)
public void transaction1() {
    System.out.println("do transaction1 .......");
    try {
        // 调用另外一个事务,try catch住
        secondTransactionService.transaction2();
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 插入当前用户tx1
    User user = new User("tx1", "111", 18);
    userService.insertUser(user);
}
  1. 定义一个controller,调用transaction1方法
@GetMapping("/testNestedTx")
public String testNestedTx() {
    firstTransactionService.transaction1();
    return "success";
}

大家觉得调用这个http接口,最终数据库插入的是几条数据呢?

问题结果

正确答案是数据库插入了0条数据。

Spring中事务嵌套这么用一定得注意了!!

同时控制台也报错了,报错原因是:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

Spring中事务嵌套这么用一定得注意了!!

是否和你预想的一样呢?你知道是为什么吗?

原因追溯

其实原因很简单,我们都知道,一个事务要么全成功提交事务,要么失败全部回滚。如果出现在一个事务中部分SQL要回滚,部分SQL要提交,这不就主打的一个”前后矛盾,精神分裂“吗?

controller.testNestedTx() 
  || 
  / 
FirstTransactionService.transaction1()   REQUIRED隔离级别
       || 
       || 
       || 捕获异常,提交事务,出错啦
       / || 
FirstTransactionService.transaction2()   REQUIRED隔离级别
       || || 
       || 抛出异常,标记事务为rollback only
       =======================
  1. 事务的隔离级别为REQUIRED,那么发现没有事务开启一个事务操作,有的话,就合并到这个事务中,所以transaction1()transaction2()是在同一个事务中。
  2. transaction2()抛出异常,那么事务会被标记为rollback only, 源码如下所示:

Spring中事务嵌套这么用一定得注意了!!

  1. transaction1()由于try catch 异常,正常运行,想必就要可以提交事务了,在提交事务的时候,会检查rollback标记,如果是true, 这时候就会抛出上面的异常了。源码如下图所示:

Spring中事务嵌套这么用一定得注意了!!这下,是不是很清楚知道报错的原因了,那想想该怎么处理呢?

解决之道

知道了根本原因之后,是不是解决的方案就很明朗了,我们可以通过调整事务的传播方式分拆多个事务管理,或者让一个事务"前后一致",做一个诚信的好事务。

  • try catch放到内层事务中,也就是transaction2()方法中,这样内层事务会跟着外部事务进行提交或者回滚。
@Override
    @Transactional(rollbackFor = Exception.class)
    public void transaction2() {
        try {
            System.out.println("do transaction2.....");
            User user = new User("tx2", "111", 18);
            userService.insertUser2(user);
            throw new RuntimeException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 如果希望内层事务抛出异常时中断程序执行,直接在外层事务的catch代码块中抛出e,这样同一个事务就都会回滚。
  • 如果希望内层事务回滚,但不影响外层事务提交,需要将内层事务的传播方式指定为PROPAGATION_NESTEDPROPAGATION_NESTED基于数据库savepoint实现的嵌套事务,外层事务的提交和回滚能够控制嵌内层事务,而内层事务报错时,可以返回原始savepoint,外层事务可以继续提交。

Spring中事务嵌套这么用一定得注意了!!

事务的传播机制

前面提到了事务的传播机制,我们再看都有哪几种。

  • PROPAGATION_REQUIRED:加入到当前事务中,如果当前没有事务,就新建一个事务。这是最常见的选择,也是Spring中默认采用的方式。
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY :支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:新建一个事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED :以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED :如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

如何理解PROPAGATION_NESTED的传播机制呢,和PROPAGATION_REQUIRES_NEW又有什么区别呢?我们用一个例子说明白。

  • 定义serviceA.methodA()PROPAGATION_REQUIRED修饰;
  • 定义serviceB.methodB()以表格中三种方式修饰;
  • methodA中调用methodB;

Spring中事务嵌套这么用一定得注意了!!

总结

在我的项目中之所以会报“rollback-only”异常的根本原因是代码风格不一致的原因。外层事务对错误的处理方式是返回true或false来告诉上游执行结果,而内层事务是通过抛出异常来告诉上游(这里指外层事务)执行结果,这种差异就导致了“rollback-only”异常。大家也可以去review自己项目中的代码,是不是也偷偷犯下同样的错误了。

欢迎关注个人公众号【JAVA旭阳】交流学习文章来源地址https://www.toymoban.com/news/detail-421156.html

到了这里,关于Spring中事务嵌套这么用一定得注意了!!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Selenium定位不到元素怎么办?一定要这么做

    在使用Selenium进行自动化测试时,碰到无法定位元素该怎么办?这里总结了9种情况下的元素定位方法: 1、frame/iframe表单嵌套 WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌的页面元素无法直接定位。 解决方法: switch_to.frame() 默认可以直接取表单的id或

    2024年03月15日
    浏览(44)
  • 【文心一言】文心一言最近这么火,它到底是什么

    前言 文心一言(英文名:ERNIE Bot)是百度全新一代知识增强大语言模型,文心大模型家族的新成员,能够与人对话互动,回答问题,协助创作,高效便捷地帮助人们获取信息、知识和灵感。文心一言是知识增强的大语言模型,基于飞桨深度学习平台和文心知识增强大模型,持

    2023年04月25日
    浏览(54)
  • 【Kubernetes】K8S到底是什么,最近怎么这么火

    前言 kubernetes,简称K8s,是用8代替名字中间的8个字符“ubernete”而成的缩写 。是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。 📕作者简介

    2024年02月16日
    浏览(36)
  • 最近怎么流量涨这么多?那我开始讲Hive特性了!

    Hive架构原理 a.用户接口:Client CLI(Hive shell)、JDBC/ODBC(java访问hive)、Hive WEBUI(浏览器访问hive)和Thrift服务器 b.驱动器:Driver 解析器(SQL Parser): 将SQL字符串转换成抽象语法树AST ,这一步一般都用第三方工具库完成,比如antlr;对AST进行语法分析,比如表是否存在、字段是

    2024年02月03日
    浏览(33)
  • 清晰、明了的@Transcation事务嵌套使用

      事务(Transaction):指数据库中执行的一系列操作被视为一个逻辑单元,要么全部成功地执行,要么全部失败回滚,保证数据的一致性和完整性。   @Transactional注解是Spring框架提供的用于声明事务的注解,作用于类和方法上。 属性 可选值 作用 propagation REQUIRED REQUIRES_

    2024年02月08日
    浏览(57)
  • 切片有哪些注意事项是一定要知道的呢

    在之前我写了一篇 切片比数组好用在哪 的文章,仔细介绍了切片相比于数组的优点。但切片事实上也隐藏着一些潜在的陷阱和需要注意的细节,了解和掌握切片的使用注意事项,可以避免意外的程序行为。本文将深入探讨Go语言切片常见的注意事项,从而能够更好得使用切片

    2024年02月08日
    浏览(30)
  • 最近想学PMP,有什么要注意和推荐的吗?

    我觉得参加PMP的学习和考试有两点需要把握住,一是心态,二是学习方法; 谈心态的话虽然比较虚,因为这个还是要看个人在生活中对事物发展的应对能力与应对突发情况的处理能力,但是简单的谈谈在备考过程中心态的处理还是很有必要的。所谓对心态的把握不仅仅限于考

    2024年02月04日
    浏览(33)
  • 使用ES Term query查询时一定要注意的地方

    使用 Term query 可以根据精确值查找相关文档数据,不过 Term query 查询与 Match query 查询还是有区别的,有时候你会发现用 Match query 可以查到,换成 Term query 却不行,本文整理一些使用 Term query 容易出错的点供参考。 如果你查看 Term query 的官方文档,你就会发现,官网首先对

    2024年02月11日
    浏览(36)
  • git提交整个文件夹(如果是网上拉下来项目一定要注意)!!!

    前言:首先我讲下我遇到的问题 我也是用git提交整个文件夹包括里面的内容 然后试了正常的提交流程一直行不通 试了好多次 每次提交上去的都是 空文件 !!!  目录 1.正常提交流程 2.如果提交是空文件的!!! 1.先是初始本地仓库  2.可以先用git status 查看这个目录下面有你的项

    2024年02月07日
    浏览(40)
  • 从零开始学Spring Boot系列-前言

    在数字化和信息化的时代,Java作为一种成熟、稳定且广泛应用的编程语言,已经成为构建企业级应用的首选。而在Java生态系统中,Spring框架无疑是其中最为耀眼的一颗明星。它提供了全面的编程和配置模型,用于构建企业级应用。随着Spring Boot的出现,这一框架变得更加易于

    2024年02月22日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包