• Spring事务传播机制


    目录

    一、事务在Spring中是如何运作的

    1.1 开启事务(DataSourceTransactionManager.doBegin)

    二、Spring的事务传播机制

    2.1 子事务的传播机制为REQUIRED

    2.2 子事务的传播机制为REQUIRES_NEW

    2.3 子事务的传播机制为NESTED


    当我们在使用Spring所提供的事务功能时,如果是仅仅处理单个的事务,是比较容易把握事务的提交与回滚,不过一旦引入嵌套事务后,多个事务的回滚和提交就会变得复杂起来,各个事务之间是如何相互影响的,是一个值得讨论的点。

    一、事务在Spring中是如何运作的

    在了解嵌套事务之前,可以先看下单个事务在Spring中的处理流程,以便后面可以更清晰地认识嵌套事务的逻辑。

    Spring事务使用AOP的机制实现,会在@Transactional注解修饰的方法前后分别织入开启事务的逻辑,以及提交或回滚的逻辑。@Transactional可以修饰在方法或者类上,区别就在于修饰于类上的,会对该类下符合条件的方法(例如private修饰的方法就不符合条件)前后都织入事务的逻辑。

    具体的处理逻辑如下(具体的方法路径为TransactionInterceptor.invoke -> TransactionAspectSupport.invokeWithinTransaction):

    1.1 开启事务(DataSourceTransactionManager.doBegin)

    这里主要做了获取连接,并关闭自动提交,将@Transactional注解中的一些参数初始化到txObject对象中。

    1.2 异常回滚(TransactionAspectSupport.completeTransactionAfterThrowing)

    这里是事务异常回滚的地方,这里有个注意点是回滚会先用rollbackOn这个方法判断,默认情况下只有RunTimeException以及Error是会进行回滚的,除非在@Transactional显式声明了rollbackFor。

    二、Spring的事务传播机制

    当出现多个事务嵌套的场景发生时,Spring事务的处理会变得复杂一些,需要考虑嵌套事务下的提交顺序,以及回滚顺序。对此,Spring提供了多种的传播机制,每种传播机制的效果都不尽相同,以便应对各种复杂的业务场景。

    正常处理的嵌套事务流程如下:

    传播机制以及它们的效果如下:

    REQUIRED:默认值,支持当前事务,如果没有事务会创建一个新的事务

    SUPPORTS:支持当前事务,如果没有事务的话以非事务方式执行

    MANDATORY:支持当前事务,如果没有事务抛出异常

    REQUIRES_NEW:创建一个新的事务并挂起当前事务

    NOT_SUPPORTED:以非事务方式执行,如果当前存在事务则将当前事务挂起

    NEVER:以非事务方式进行,如果存在事务则抛出异常

    NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作

    以下的说明均建立在父事务的传播机制为REQUIRED的前提下,来探讨各种传播机制的回滚策略。

    2.1 子事务的传播机制为REQUIRED

    子事务

    主事务

    结果

    异常

    正常,并try-catch异常

    均回滚

    正常

    异常

    均回滚

    正常

    异常,并try-catch异常

    不回滚

    这里详细说下第一种场景,子事务异常情况下,主事务捕获了子事务的异常却仍发生了回滚。从代码来看:

    回滚的原因在于,子事务失败的时候在回滚代码中设置了全局回滚的标识(AbstractPlatformTransactionManager.processRollback)。

    之后主事务在进行事务提交时,会判断全局回滚标识是否存在。若存在就会进行回滚动作。(AbstractPlatformTransactionManager.commit)

    2.2 子事务的传播机制为REQUIRES_NEW

    子事务

    主事务

    结果

    异常

    正常,并try-catch异常

    子回滚,主不回滚

    正常

    异常

    子不回滚,主回滚

    异常

    正常

    均回滚

    这里主要说下第三种场景,因为从REQUIRES_NEW的描述中容易造成误解:创建一个新的事务并挂起当前事务,很容易理解为子事务独立于主事务,子事务回滚后不会影响主事务的执行。

    我认为问题点主要在于对挂起的认识,可以看下Spring时如何执行的(AbstractPlatformTransactionManager.handleExistingTransaction):

    在这个挂起动作中主要做了两件事,一是将全局ThreadLocal中的配置初始化,二是将原事务的信息保存在SuspendedResourcesHolder对象中。

    当子事务异常回滚后,就会通过AbstractPlatformTransactionManager.resume方法恢复主事务。

    恢复后,子会再向外抛出一个异常,因此主事务会接收到该异常,因此主事务也发生回滚。

    总的来说,挂起的动作并不代表子完全独立于主。

    2.3 子事务的传播机制为NESTED

    子事务

    主事务

    结果

    异常

    正常,并try-catch异常

    子回滚,主不回滚

    正常

    异常

    均回滚

    异常

    正常

    均回滚

    NESTED是通过rollback的savepoint机制,实现异常回滚。相比于子事务为REQUIRED下,子事务异常,主事务try-catch了异常,REQUIRED情况下主子均会回滚,但NESTED模式主不会回滚。因此可以看下场景一,NESTED是如何处理的。

    子事务如果是NESTED模式,则会保存savepoint,以便后面回滚到该savepoint。

    回滚时,直接回滚到savepoint,且不会设置全局回滚标识。因此即相当于就是回滚了子事务。主事务由于try-catch了异常,因此执行方法的时候也没有抛出异常,正常走提交流程,且没有全局回滚标识,故不会回滚,正常提交。

    PS: mysql的savepoint机制

    BEGIN;

    INSERT INTO test_entity values (1, ‘aaa’, 10); ①

    SAVEPOINT savepoint1;

    INSERT INTO test_entity values (2, ‘bbb’, 11); ②

    ROLLBACK TO savepoint1;

    RELEASE SAVEPOINT savepoint1;

    COMMIT;

    回滚到savepoint1,①会入库,②被回滚。

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    java-php-python-ssm员工培训管理系统计算机毕业设计
    Windows系统/Linux系统修改远程连接端口号
    SpringBoot原理篇(2)—自定义starter
    优彩云采集器下载-免费优彩云采集器下载地址
    一起用Go做一个小游戏(中)
    LockSupport与线程中断
    【UV打印机】理光喷头组合说明(8H)
    Fedora CoreOS 安装部署详解
    调用API post请求
    linux笔记(8):东山哪吒D1H移植lvgl(HDMI输出)
  • 原文地址:https://blog.csdn.net/m0_67393413/article/details/126116789