关于事务的传播方式

传播行为定义了当一个事务方法被另一个事务方法调用时,事务应该如何进行。它决定了新方法是加入已有的“事务组”,还是自己单开一个“新项目组”,或者干脆不参与“项目组”(事务)。

核心传播行为详解与示例:

假设我们有两个服务方法:ServiceA.methodA()ServiceB.methodB()methodA 内部调用了 methodB

1.REQUIRED (默认) - “一起干”或“开新组”

含义: 如果当前存在事务则加入该事务;如果当前没有事务,则新建一个事务。

场景: 最常用的设置。适用于大多数业务逻辑,保证操作在同一个事务边界内。你在项目组A (methodA)。现在需要完成一个子任务 (methodB)。如果项目组A已经存在(有事务),你就直接在组A内完成子任务(加入事务)。如果还没有项目组(无事务),你就自己成立一个新项目组(新建事务)来完成这个子任务(以及后续可能由methodA做的其他事)。

        @Service
        public class ServiceA {
            @Autowired
            private ServiceB serviceB;

            @Transactional(propagation = Propagation.REQUIRED) // 默认就是REQUIRED
            public void methodA() {
                // 操作A
                serviceB.methodB(); // 调用另一个事务方法
                // 操作C(如果methodB失败回滚,操作A和操作C也会回滚)
            }
        }

        @Service
        public class ServiceB {
            @Transactional(propagation = Propagation.REQUIRED) // 加入ServiceA开启的事务
            public void methodB() {
                // 操作B
            }
        }
结果: `操作A`, `操作B`, `操作C` 要么一起成功,要么一起失败回滚。它们在一个物理数据库事务中执行。

2. REQUIRES_NEW - “必须单干”

含义: 总是新建一个事务。如果当前存在事务,则挂起(暂停) 当前事务,先执行新事务。新事务执行完毕(提交或回滚)后,恢复被挂起的事务。

场景: 需要独立于调用者的事务逻辑,无论调用者成功与否,该方法的操作都必须独立提交或回滚。常用于日志记录(即使主业务失败,日志也要记录)、需要强独立性的核心操作。

你是项目组A (methodA) 的成员。现在有一个非常重要的特殊任务 (methodB),要求必须单独成立一个项目组来完成,不受主项目组A成功或失败的影响。你暂停了组A的工作(挂起事务),成立新组B(新建事务)去完成任务。完成后(无论B组成功失败),再回到组A继续工作(恢复事务)。

        @Service
        public class ServiceA {
            @Autowired
            private ServiceB serviceB;
            @Autowired
            private AuditLogService auditLogService; // 假设这是一个记录审计日志的服务

            @Transactional(propagation = Propagation.REQUIRED)
            public void methodA() {
                // 操作A (在事务T1中)
                try {
                    serviceB.criticalOperation(); // REQUIRES_NEW, 开启新事务T2
                } catch (Exception e) {
                    // 即使criticalOperation失败,日志也要记录
                }
                auditLogService.log("methodA executed"); // REQUIRES_NEW, 开启新事务T3
                // 操作C (在事务T1中)
            }
        }

        @Service
        public class ServiceB {
            @Transactional(propagation = Propagation.REQUIRES_NEW)
            public void criticalOperation() {
                // 操作B (在独立的事务T2中)
            }
        }

        @Service
        public class AuditLogService {
            @Transactional(propagation = Propagation.REQUIRES_NEW) // 确保日志记录独立提交
            public void log(String message) {
                // 记录日志到数据库 (在独立的事务T3中)
            }
        }
结果:`操作A` 和 `操作C` 在事务 T1 中。 

`操作B` 在事务 T2 中。 * 审计日志记录在事务 T3 中。

* 即使 `操作B` (`criticalOperation`) 失败导致 T2 回滚,T1(包含操作A、操作C)和 T3(日志)不受影响(除非 `criticalOperation` 抛出的异常被 `methodA` 捕获后导致 T1 也失败)。

* 即使 T1(操作A或操作C失败)回滚,T2(操作B)和 T3(日志)**已经提交成功**(只要它们自己执行期间没出错)。

3. SUPPORTS - “随大流”

* 含义: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

* 场景: 方法可以适应调用者的事务状态。主要用于查询方法,这些方法在有事务时保证读取一致性,在没有事务时也能执行(但可能读到中间状态)。慎用于修改数据的方法

        @Transactional(propagation = Propagation.REQUIRED)
        public void updateData() {
            // 更新操作1 (在事务中)
            String currentValue = readCurrentValue(); // SUPPORTS, 加入事务,读到更新操作1后的值?
            // 更新操作2 (在事务中,依赖currentValue)
        }

        @Transactional(propagation = Propagation.SUPPORTS)
        public String readCurrentValue() {
            // 读取数据
        }

* 如果 `updateData` 在一个事务中调用 `readCurrentValue`,那么 `readCurrentValue` 就在同一个事务中执行,可能读到 `updateData` 中已经修改但未提交的数据(取决于隔离级别)。如果直接调用 `readCurrentValue`(无事务环境),它就以普通查询执行。

4. NOT_SUPPORTED - “绝不参与” *

含义: 总是以非事务方式执行。如果当前存在事务,则挂起该事务。

* 场景: 方法本身不适合在事务中运行(例如,执行长时间计算、调用不支持事务的外部系统)。它会强制让调用它的方法暂时“脱离”事务环境执行它。

        @Transactional(propagation = Propagation.REQUIRED)
        public void processOrder() {
            // 事务性操作...
            externalSystemCall(); // NOT_SUPPORTED, 暂停当前事务,无事务执行
            // 恢复事务,继续其他事务操作...
        }

        @Transactional(propagation = Propagation.NOT_SUPPORTED)
        public void externalSystemCall() {
            // 调用一个不支持事务或执行非常慢的外部系统接口
        }


 `externalSystemCall` 的执行不会在数据库事务中进行,即使它失败了,也不会导致 `processOrder` 的事务回滚(除非它抛出的异常没有被处理)。

`processOrder` 的事务在调用 `externalSystemCall` 时被挂起,调用结束后恢复。

5. MANDATORY - “必须一起干”

 含义: 方法必须在一个已存在的事务中运行。如果当前没有事务,则抛出异(IllegalTransactionStateException)。

*场景: 严格要求方法不能独立运行,必须作为更大事务的一部分。用于强制事务上下文存在。 * 


        public void businessFlow() {
            // 没有开启事务
            try {
                serviceB.mandatoryOperation(); // 调用MANDATORY方法
            } catch (IllegalTransactionStateException e) {
                // 因为没有事务,这里会捕获到异常
            }
        }

        @Service
        public class ServiceB {
            @Transactional(propagation = Propagation.MANDATORY)
            public void mandatoryOperation() {
                // 必须在一个已存在的事务中运行
            }
        }

* 如果 `businessFlow` 没有用 `@Transactional` 标注(或者不在其他事务方法内),直接调用 `mandatoryOperation` 会导致异常。

6. NEVER - “绝不一起干”

 含义: 方法绝不能在一个事务中运行。如果当前存在事务,则抛出异常 (IllegalTransactionStateException)。

场景: 强制要求方法在非事务环境下执行,防止意外加入现有事务导致预期外的行为(如延迟提交、锁持有时间过长)。通常用于纯查询且对实时性要求极高(不希望被前面的事务阻塞)、或操作不支持事务的资源。

        @Transactional(propagation = Propagation.REQUIRED)
        public void updateAndCheck() {
            updateData(); // 更新操作
            try {
                realtimeStatsCheck(); // 调用NEVER方法
            } catch (IllegalTransactionStateException e) {
                // 因为updateAndCheck在事务中,这里会捕获到异常
            }
        }

        @Transactional(propagation = Propagation.NEVER)
        public void realtimeStatsCheck() {
            // 需要实时查询最新统计数据(不希望被updateData的事务阻塞)
        }
在事务方法 `updateAndCheck` 中调用 `realtimeStatsCheck` 会抛出异常。
<think>我们正在讨论Spring事务传播行为。根据引用[1][2][3],Spring定义了7种事务传播行为。这些行为决定了在事务方法调用其他事务方法时,事务应该如何传播。 我们需要列出所有7种传播行为,并解释每种行为的使用场景。 根据引用[1]和[3]: 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)。嵌套事务可以独立回滚而不影响外部事务(但外部事务回滚会导致嵌套事务回滚)。适用于需要部分回滚的场景。 使用场景举例: - REQUIRED:最常用,适用于增删改操作。 - REQUIRES_NEW:用于需要独立提交的事务,如审计日志。 - NESTED:用于可部分回滚的复杂业务,比如一个订单处理包含多个子步骤,某个子步骤失败只需回滚该步骤而不影响其他步骤(但外部事务回滚则全部回滚)。 注意:引用[4]中提到了事务失效的场景,但用户当前问题聚焦于传播行为,因此我们主要解释传播行为。 下面按上述思路组织回答。</think>### Spring事务传播行为详解 Spring框架定义了7种事务传播行为,用于控制事务方法在调用其他事务方法时如何传播事务上下文。这些传播行为是Spring事务管理的核心机制,决定了事务的边界和协作方式。以下是每种传播行为的详细说明及使用场景: --- #### 1. **PROPAGATION_REQUIRED(默认)** - **行为**:如果当前存在事务,则加入该事务;如果不存在,则创建一个新事务。 - **使用场景**: 适用于大多数业务操作(如订单创建、支付处理),确保多个操作在同一个事务中执行(例如:订单创建成功后必须更新库存)。 ```java @Transactional(propagation = Propagation.REQUIRED) public void createOrder() { // 订单创建逻辑 updateInventory(); // 加入当前事务 } ``` [^1][^3] --- #### 2. **PROPAGATION_SUPPORTS** - **行为**:如果当前存在事务,则加入该事务;如果不存在,则以非事务方式运行。 - **使用场景**: 适用于查询操作(如商品详情查询),可灵活适应调用方的事务状态(例如:查询操作可独立运行,也可在事务中运行)。 ```java @Transactional(propagation = Propagation.SUPPORTS) public Product getProductDetails(Long id) { // 查询逻辑(无事务时不影响数据一致性) } ``` [^1][^3] --- #### 3. **PROPAGATION_MANDATORY** - **行为**:如果当前存在事务,则加入该事务;如果不存在,则抛出异常。 - **使用场景**: 强制要求必须在事务中调用的方法(如资金扣减操作),避免因无事务导致数据不一致。 ```java @Transactional(propagation = Propagation.MANDATORY) public void deductBalance() { // 必须在事务中执行 } ``` [^1][^3] --- #### 4. **PROPAGATION_REQUIRES_NEW** - **行为**:无论当前是否存在事务,都创建一个新事务。若当前有事务,则挂起原事务。 - **使用场景**: 需要独立提交的操作(如日志记录),即使主事务失败,日志仍需提交(例如:审计日志必须独立保存)。 ```java @Transactional(propagation = Propagation.REQUIRES_NEW) public void logOperation() { // 独立事务,不受主事务回滚影响 } ``` [^1][^3][^4] --- #### 5. **PROPAGATION_NOT_SUPPORTED** - **行为**:以非事务方式运行。若当前存在事务,则挂起该事务。 - **使用场景**: 不需要事务的操作(如批量数据导出),避免事务开销(例如:只读操作无需锁机制)。 ```java @Transactional(propagation = Propagation.NOT_SUPPORTED) public void exportData() { // 非事务执行 } ``` [^1][^3] --- #### 6. **PROPAGATION_NEVER** - **行为**:以非事务方式运行。若当前存在事务,则抛出异常。 - **使用场景**: 禁止在事务中调用的方法(如某些工具类方法),确保操作不会受事务影响(例如:静态数据加载)。 ```java @Transactional(propagation = Propagation.NEVER) public void loadConfig() { // 禁止在事务中调用 } ``` [^1][^3] --- #### 7. **PROPAGATION_NESTED** - **行为**:如果当前存在事务,则在嵌套事务中执行(可部分回滚);如果不存在,则创建一个新事务。 - **使用场景**: 复杂业务需部分回滚的场景(如订单多步骤处理),子步骤失败不影响主事务(例如:订单创建成功后,库存扣减失败仅回滚库存操作)。 ```java @Transactional(propagation = Propagation.NESTED) public void updateInventory() { // 嵌套事务,可独立回滚 } ``` [^1][^3] --- ### 关键总结 | 传播行为 | 特点 | 典型场景 | |----------------------|----------------------------------------------------------------------|------------------------| | **REQUIRED** (默认) | 加入或创建事务 | 增删改操作 | | **REQUIRES_NEW** | 强制创建独立事务 | 日志记录、审计 | | **NESTED** | 嵌套事务(支持部分回滚) | 多步骤业务处理 | | **SUPPORTS** | 灵活适应调用方事务状态 | 查询操作 | | **MANDATORY** | 强制要求调用方必须有事务 | 资金操作 | | **NOT_SUPPORTED** | 非事务执行 | 批量导出 | | **NEVER** | 禁止在事务中调用 | 工具类方法 | > **注意**:事务传播行为需结合隔离级别(如 `READ_COMMITTED`)和回滚规则使用,避免事务失效(例如:非public方法调用导致 `@Transactional` 失效)[^3][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值