Spring 有哪几种事务传播行为?它们有什么区别?

你好,我是猿java

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

1. 事务传播行为概述

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

  1. REQUIRED
  2. REQUIRES_NEW
  3. SUPPORTS
  4. NOT_SUPPORTED
  5. MANDATORY
  6. NEVER
  7. NESTED

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

2. 原理分析

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

  • 事务的存在检查:当一个方法(被调用者)执行时,它会检查调用它的方法(调用者)是否存在活动事务。

  • 决定是否挂起、重用或创建新事务:根据传播行为的不同,调用者可能会被挂起,或者调用者的方法可能会加入到现有事务中,或者开启一个全新的事务。

  • 事务的边界控制:传播行为还控制了事务的提交和回滚边界,确保在复杂调用链中事务的一致性与完整性。

3. 各种传播行为的示例分析

3.1 REQUIRED

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

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

示例

1
2
3
4
5
6
7
8
9
10
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}

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

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

3.2 REQUIRES_NEW

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

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

示例

1
2
3
4
5
6
7
8
9
10
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}

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

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

3.3 SUPPORTS

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

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

示例

1
2
3
4
5
6
7
8
9
10
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}

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

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

3.4 NOT_SUPPORTED

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

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

示例

1
2
3
4
5
6
7
8
9
10
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}

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

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

3.5 MANDATORY

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

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

示例

1
2
3
4
5
6
7
8
9
10
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}

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

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

3.6 NEVER

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

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

示例

1
2
3
4
5
6
7
8
9
10
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
}

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

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

3.7 NESTED

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

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

示例

1
2
3
4
5
6
7
8
9
10
11
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 执行数据库操作
methodB();
// 继续执行
}

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

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

4. 传播行为对比

传播行为 是否必须存在事务 是否开启新事务 是否挂起现有事务 适用场景
REQUIRED 默认行为,绝大多数场景使用,如服务层方法。
REQUIRES_NEW 需要独立事务,如日志记录,不受外部事务影响。
SUPPORTS 可选事务性,读操作等,既能在事务中也能在非事务中运行。
NOT_SUPPORTED 非事务操作,如与事务无关的外部系统交互。
MANDATORY 强制要求存在事务的操作,确保方法调用在事务环境中。
NEVER 确保方法在非事务环境中执行,避免事务上下文。
NESTED 基于底层支持 需要在事务内进行部分回滚的场景,如复杂业务操作。

4.1 关键差异点

  • 是否开启新事务REQUIRES_NEWNESTED 可以开启新事务,其他大部分属于参与现有事务或非事务。

  • 是否必须存在事务MANDATORYNEVER 针对事务的存在有严格要求。

  • 是否挂起现有事务REQUIRES_NEWNOT_SUPPORTED 会挂起当前事务。

  • 嵌套事务支持NESTED 依赖于底层数据库的 savepoint 支持,允许在同一事务中进行局部回滚。

5. 实际应用中的选择

  • 大多数场景:使用 REQUIRED 是最合适的选择,因为它简化了事务管理,并且大多数情况下方法需要参与到调用者的事务中。

  • 独立事务需求:如日志记录、发送通知等,需要与主事务独立的操作,可以使用 REQUIRES_NEW

  • 可选事务:对于既能在事务中运行也能在非事务中运行的操作,可以使用 SUPPORTS

  • 确保非事务执行:若某操作必须在非事务环境下执行,如一些特定的外部系统调用,可以使用 NOT_SUPPORTEDNEVER

  • 强制要求事务:在某些关键业务逻辑中,确保方法只能在事务中调用,可以使用 MANDATORY

  • 局部回滚需求:在复杂业务场景下,需要对某部分操作进行局部回滚,可以考虑使用 NESTED,但需确保底层数据库支持。

6. 注意事项

  • 数据库支持:部分传播行为(如 NESTED)依赖于底层数据库的支持,使用前需要确认数据库和事务管理器的兼容性。

  • 事务管理器配置:正确配置 Spring 事务管理器(如 PlatformTransactionManager)对于事务传播行为的正常工作至关重要。

  • 事务边界设计:合理设计事务边界,避免过长的事务导致资源占用和性能问题。

7. 总结

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

8. 学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

drawing