不同的事务,在 Spring 中是如何传播的?

开发
本文我们详细分析了 Spring 事务传播行为,掌握它们对于构建健壮的事务管理策略至关重要。

Spring 事务传播行为(Propagation Behavior)定义了一个事务方法被另一个事务方法调用时事务的边界和行为。这篇文章,我们将深度分析它们的原理以及对比它们之间的差异。

一、事务传播行为概述

在 Spring 中,Propagation 枚举类型定义了七种主要的事务传播行为:

  • REQUIRED
  • REQUIRES_NEW
  • SUPPORTS
  • NOT_SUPPORTED
  • MANDATORY
  • NEVER
  • NESTED

此外,NESTED 传播行为在某些数据源(如支持嵌套事务的数据库)下可用。

二、原理分析

事务传播行为主要决定了在一个事务方法被调用时,当前存在的事务(如果有)应该如何被处理。以下是其原理分析:

  • 事务的存在检查:当一个方法(被调用者)执行时,它会检查调用它的方法(调用者)是否存在活动事务。
  • 决定是否挂起、重用或创建新事务:根据传播行为的不同,调用者可能会被挂起,或者调用者的方法可能会加入到现有事务中,或者开启一个全新的事务。
  • 事务的边界控制:传播行为还控制了事务的提交和回滚边界,确保在复杂调用链中事务的一致性与完整性。

三、各种传播行为的示例分析

1. REQUIRED

定义:支持当前事务。如果没有事务,就新建一个事务。

原理:调用者存在事务,方法加入到这个事务中;否则,开启新事务。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // 执行数据库操作
}

分析:methodA 开启事务,methodB 加入到同一个事务中。如果 methodA 回滚,methodB 也会回滚。

2. REQUIRES_NEW

定义:新建事务,如果存在事务,就将当前事务挂起。

原理:无论调用者是否存在事务,方法都会开启一个独立的新事务。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 执行数据库操作
}

分析:methodA 和 methodB 各自有独立的事务。如果 methodB 回滚,不会影响 methodA 的事务。

3. SUPPORTS

定义:支持当前事务,如果存在事务,就加入;否则,以非事务方式执行。

原理:依赖调用者是否有事务,调用者有则参与事务,无则不使用事务。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
}

@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // 执行数据库操作
}

分析:如果 methodA 调用 methodB,methodB 会参与 methodA 的事务。如果 methodB 被独立调用,则以非事务方式执行。

4. NOT_SUPPORTED

定义:以非事务方式执行操作,如果存在事务,就将其挂起。

原理:方法不支持事务,即使调用者有事务,也不会参与。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
    // 执行数据库操作
}

分析:methodA 的事务会在执行 methodB 时被挂起,methodB 以非事务方式执行,执行完毕后恢复 methodA 的事务。

5. MANDATORY

定义:支持当前事务,必须存在一个事务,否则抛出异常。

原理:如果调用者有事务,方法参与;否则,抛出 IllegalTransactionStateException。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
}

@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // 执行数据库操作
}

分析:当 methodA 调用 methodB 时,由于 methodA 有事务,methodB 可以正常参与事务。如果 methodB 被独立调用,无事务,会抛出异常。

6. NEVER

定义:以非事务方式执行,如果存在事务,则抛出异常。

原理:方法绝不支持事务,确保其执行不在事务上下文中。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
}

@Transactional(propagation = Propagation.NEVER)
public void methodB() {
    // 执行数据库操作
}

分析:当 methodA 调用 methodB 时,由于 methodA 有事务,调用 methodB 会抛出异常。如果 methodB 被独立调用,无事务,则正常执行。

7. NESTED

定义:如果存在事务,则在嵌套事务内执行;否则,类似于 REQUIRED。

原理:基于底层数据库的保存点(savepoint),允许在嵌套事务中回滚到保存点,而不影响外部事务。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 执行数据库操作
    methodB();
    // 继续执行
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 执行数据库操作
}

分析:如果 methodB 出现异常且事务被回滚,只会回滚到 methodB 的开始,而 methodA 的事务仍然可以继续或回滚。

四、传播行为对比

传播行为

是否必须存在事务

是否开启新事务

是否挂起现有事务

适用场景

REQUIRED




默认行为,绝大多数场景使用,如服务层方法。

REQUIRES_NEW




需要独立事务,如日志记录,不受外部事务影响。

SUPPORTS




可选事务性,读操作等,既能在事务中也能在非事务中运行。

NOT_SUPPORTED




非事务操作,如与事务无关的外部系统交互。

MANDATORY




强制要求存在事务的操作,确保方法调用在事务环境中。

NEVER




确保方法在非事务环境中执行,避免事务上下文。

NESTED


基于底层支持


需要在事务内进行部分回滚的场景,如复杂业务操作。

关键差异点:

  • 是否开启新事务:REQUIRES_NEW 和 NESTED 可以开启新事务,其他大部分属于参与现有事务或非事务。
  • 是否必须存在事务:MANDATORY 和 NEVER 针对事务的存在有严格要求。
  • 是否挂起现有事务:REQUIRES_NEW 和 NOT_SUPPORTED 会挂起当前事务。
  • 嵌套事务支持:NESTED 依赖于底层数据库的 savepoint 支持,允许在同一事务中进行局部回滚。

五、实际应用中的选择

  • 大多数场景:使用 REQUIRED 是最合适的选择,因为它简化了事务管理,并且大多数情况下方法需要参与到调用者的事务中。
  • 独立事务需求:如日志记录、发送通知等,需要与主事务独立的操作,可以使用 REQUIRES_NEW。
  • 可选事务:对于既能在事务中运行也能在非事务中运行的操作,可以使用 SUPPORTS。
  • 确保非事务执行:若某操作必须在非事务环境下执行,如一些特定的外部系统调用,可以使用 NOT_SUPPORTED 或 NEVER。
  • 强制要求事务:在某些关键业务逻辑中,确保方法只能在事务中调用,可以使用 MANDATORY。
  • 局部回滚需求:在复杂业务场景下,需要对某部分操作进行局部回滚,可以考虑使用 NESTED,但需确保底层数据库支持。

六、注意事项

  • 数据库支持:部分传播行为(如 NESTED)依赖于底层数据库的支持,使用前需要确认数据库和事务管理器的兼容性。
  • 事务管理器配置:正确配置 Spring 事务管理器(如 PlatformTransactionManager)对于事务传播行为的正常工作至关重要。
  • 事务边界设计:合理设计事务边界,避免过长的事务导致资源占用和性能问题。

七、总结

本文,我们详细分析了 Spring 事务传播行为,掌握它们对于构建健壮的事务管理策略至关重要。通过合理选择合适的传播行为,可以确保在复杂的业务逻辑和调用链中,数据的一致性和系统的稳定性。开发人员应根据具体的业务需求和应用场景,灵活运用不同的传播行为,以达到最佳的事务管理效果。

责任编辑:赵宁宁 来源: 猿java
相关推荐

2023-10-30 07:36:19

Spring事务传播机制

2022-08-27 14:14:06

Spring事务开发

2020-07-17 08:07:54

Spring数据库事务

2021-06-26 14:59:13

SpringTransaction执行

2024-01-04 12:48:00

Spring

2010-07-13 15:14:15

2021-07-12 10:37:42

Spring面试事务

2019-08-15 16:30:49

TomcatSpringBootJava

2022-09-27 21:14:54

Spring事务传播机制

2020-08-19 09:45:29

Spring数据库代码

2012-06-07 09:18:16

ibmdw

2011-03-25 10:00:23

Spring3.0事务的配置

2023-05-06 07:29:49

Spring事务传播

2023-04-27 07:34:36

前端工程师前端已死

2021-09-07 10:33:42

MySQL事务隔离性

2021-07-20 15:42:05

编程语言PythonJava

2022-06-28 14:57:09

FormatterSpring

2024-01-08 08:45:07

Spring容器Bean

2024-12-30 13:58:14

2022-04-10 23:42:33

MySQLSQL数据库
点赞
收藏

51CTO技术栈公众号