别再搞混了!一文彻底搞懂 Spring 事务传播的 7 种行为

349 阅读8分钟

📡Spring事务传播行为的类型

Spring 提供了 7 种事务传播行为来控制方法在事务中的执行方式,分别是:

1️⃣ PROPAGATION_REQUIRED(默认行为)
  • 特性:如果当前已有事务,方法会加入该事务;如果没有事务,方法会启动一个新事务。
  • 应用场景:大多数业务逻辑,特别是需要多个操作一致性的场景。比如订单操作和库存更新,确保它们在同一个事务中。
2️⃣ PROPAGATION_SUPPORTS
  • 特性:支持当前事务,如果没有事务,则以非事务方式执行。
  • 应用场景:方法可以在事务内执行,也可以在非事务中执行。例如读取数据时,可以不必加事务,但如果有事务则可以加。
3️⃣ PROPAGATION_MANDATORY
  • 特性:要求方法在现有事务中执行,如果没有事务,则抛出异常。
  • 应用场景:需要依赖现有事务的操作。比如,一个方法必须在事务中执行,不能单独执行。
4️⃣ PROPAGATION_REQUIRES_NEW
  • 特性:无论是否存在当前事务,都会创建一个新事务。如果有现有事务,则挂起该事务,待新事务完成后再恢复。
  • 应用场景:独立执行的事务,通常用于日志记录、支付处理等场景,确保即使主事务回滚,辅助事务依然提交。
5️⃣ PROPAGATION_NOT_SUPPORTED
  • 特性:方法不支持事务,如果当前有事务,则会挂起该事务,非事务方式执行。
  • 应用场景:明确不需要事务控制的操作,如查询操作,避免事务的开销。
6️⃣ PROPAGATION_NEVER
  • 特性:要求方法不能在事务中执行。如果当前有事务,则抛出异常。
  • 应用场景:一些操作完全不允许在事务环境中执行,如某些安全敏感的操作,确保不受到事务影响。
7️⃣ PROPAGATION_NESTED
  • 特性:如果当前存在事务,则在该事务内创建嵌套事务(支持保存点)。内层事务回滚不会影响外层事务,但外层事务回滚会回滚内层事务。
  • 应用场景:需要部分回滚或局部事务控制的场景。比如订单中的一些小操作可能失败,但不希望整个订单操作回滚。
传播行为含义是否创建新事务典型场景
PROPAGATION_REQUIRED(默认)有则加入,无则新建❌ / ✅普通增删改查,保证一致性
PROPAGATION_SUPPORTS支持当前事务,无则非事务执行查询类操作,可有可无事务
PROPAGATION_MANDATORY必须运行在事务中,否则抛异常强依赖事务的操作(如更新余额)
PROPAGATION_REQUIRES_NEW总是新建事务,挂起当前事务日志记录、消息发送等独立操作
PROPAGATION_NOT_SUPPORTED不支持事务,挂起当前事务高频查询、第三方接口调用
PROPAGATION_NEVER绝不允许在事务中运行,否则抛异常安全敏感操作、跨系统调用
PROPAGATION_NESTED嵌套事务(保存点),外层可回滚内层⭕️(保存点)局部失败不影响整体的复杂流程

🛠️ Spring 中如何使用事务传播行为

你可以通过 @Transactional 注解的 propagation 属性来设置事务传播行为。例如:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createNewTransaction() {
    // 新建事务逻辑
}

🎯Spring事务传播的核心价值

在复杂业务中,方法之间频繁调用(如 A → B → C),每个方法可能都标注了 @Transactional。如果没有统一的规则来决定“这些方法是共用一个事务?还是各自独立?”——就会导致数据不一致、回滚混乱、性能问题

👉 事务传播行为(Propagation Behavior) 就是 Spring 提供的一套“事务参与规则”,用于解决:

  • 多个事务方法相互调用时,如何共享或隔离事务?
  • 如何控制事务的边界、回滚范围?
  • 如何实现部分失败不影响整体,或确保整体一致性?
1️⃣ 控制事务传播与事务嵌套

事务传播行为使得我们能够精确控制不同方法之间的事务交互。比如,一个方法调用另一个方法时,是否使用当前事务、启动新事务,或者是否挂起当前事务,这些都可以通过事务传播行为来灵活配置。

常见配置示例

  • PROPAGATION_REQUIRED:如果已有事务,它会加入现有事务;如果没有事务,它会创建一个新事务。这是最常用的事务传播方式,适用于大多数场景。
  • PROPAGATION_REQUIRES_NEW:即使当前已有事务,它也会强制创建一个新事务,并挂起现有事务。
2️⃣ 隔离独立操作事务

对于某些操作(如日志记录、发送通知等),我们希望它们能够独立于主事务执行。无论主事务是否成功,这些操作应当能够继续执行并保证成功。使用 PROPAGATION_REQUIRES_NEW 就能实现这一目标,它确保了这些操作具有独立事务的隔离性。

示例场景

  • 在支付系统中,即使支付操作失败,日志记录操作依然应该成功,这时可以让日志记录操作运行在独立的事务中。
3️⃣ 边界和一致性管理

事务传播行为不仅可以决定如何传播事务,还能确保事务的一致性和完整性。在某些场景中,某个方法必须在事务中执行,而另一些方法则需要在没有事务的情况下执行。Spring 的事务传播行为能够很好地应对这些复杂场景。

配置示例

  • PROPAGATION_MANDATORY:确保某个方法在已经存在的事务中执行。如果没有现有事务,会抛出异常。
  • PROPAGATION_NEVER:确保该方法不能在事务中执行,如果当前有事务,则抛出异常。

🔄 事务传播行为的更深理解

1️⃣ 事务嵌套的特点

PROPAGATION_NESTED 提供了事务嵌套的能力,允许内层事务独立回滚,而外层事务不会受到影响。这是事务传播行为中的一个重要特点,尤其适用于复杂业务逻辑中,有些操作可能失败但不希望影响整个事务的场景。

应用实例

  • 订单创建流程可能包括多个步骤,如库存扣减、支付处理等。某些步骤可能会失败,使用嵌套事务可以确保失败的步骤回滚,而不影响整个订单操作。
2️⃣ 事务复用与隔离的平衡

PROPAGATION_REQUIRED(默认行为)会尽量复用现有事务,多个方法在同一事务上下文中运行,确保它们紧密耦合,适用于那些需要保持一致性的业务操作,如订单与库存的处理。

配置意义

  • 在 PROPAGATION_REQUIRED 下,整个操作是原子性的。无论多少个方法,整个事务要么都成功,要么都回滚。

💡 如何根据场景选择合适的事务传播行为?

  1. 多步骤操作(复用事务) :如果多个操作相互依赖,应该选择 PROPAGATION_REQUIRED 来确保它们共享一个事务,任何操作失败都会导致整个事务回滚。
  2. 独立执行的操作:如日志、邮件发送等,可以使用 PROPAGATION_REQUIRES_NEW 来确保它们与主事务分开执行,即使主事务失败,它们也能成功执行。
  3. 不希望参与事务的操作:如果某个操作完全不需要事务控制,可以使用 PROPAGATION_NOT_SUPPORTED,它会挂起当前事务并以非事务方式执行该操作。

🔧 事务传播与性能的平衡

选择适合的事务传播行为不仅能确保业务逻辑的正确性,还能优化系统性能。例如,过度使用 PROPAGATION_REQUIRES_NEW 可能会导致性能开销增加,因为它总是创建新的事务并挂起当前事务。因此,选择合适的事务策略,不仅要考虑事务的一致性,还要考虑到性能影响。

理解事务传播的边界 🛑
  • 外层事务失败:会回滚内层事务,因为外层事务控制了内层事务的状态.
  • 内层事务失败:不会影响外层事务,内层事务的回滚仅限于当前事务方法内.

这种机制帮助我们在不同的事务操作中控制事务的影响范围,从而保证数据的一致性和完整性. 💪

事务传播行为图解 📊

下面是一个简单的事务传播行为示意图,帮助理解事务传播行为的基本工作原理:


           +--------------------------+
           |     External Transaction  |
           +--------------------------+
                     |  (Propagation.REQUIRED)
                     v
           +--------------------------+
           |     Internal Transaction  |
           +--------------------------+
           |    (Propagation.NESTED)   |
           +--------------------------+

总结 🏁

通过合适的事务传播行为配置,可以确保在复杂业务场景中事务的正确传播与管理。无论是外层事务管理内层事务,还是独立的事务执行,Spring 的事务传播机制都能帮助我们灵活应对不同的事务管理需求. 🌟