Spring事务传播机制:7种姿势教你玩转"事务接力赛"

简介: 事务传播机制是Spring框架中用于管理事务行为的重要概念,它决定了在方法调用时事务如何传递与执行。通过7种传播行为,开发者可以灵活控制事务边界,适应不同业务场景。例如:REQUIRED默认加入或新建事务,REQUIRES_NEW独立开启新事务,NESTED支持嵌套回滚等。合理使用传播机制不仅能保障数据一致性,还能提升系统性能与健壮性。掌握这“七种人格”,才能在复杂业务中游刃有余。

什么是事务传播机制?事务界的"驴友准则"

想象这样一个场景,你想和你的小伙伴一起去旅游,你们准备怎么去:

  • 如果你的小伙伴已经有车了(当前存在事务)→ 你是开自己的车还是直接蹭车?
  • 如果你的小伙伴没有车(当前不存在事务)→ 你是要买车自己开还是直接放弃?

这就是事务传播机制要解决的问题!它定义了当一个事务方法被另一个事务方法调用时,事务该如何传播

Spring定义了7种传播行为,表示传播行为的常量定义在TransactionDefinition

package org.springframework.transaction;  

import org.springframework.lang.Nullable;  

public interface TransactionDefinition {
     
    int PROPAGATION_REQUIRED = 0;  
    int PROPAGATION_SUPPORTS = 1;  
    int PROPAGATION_MANDATORY = 2;  
    int PROPAGATION_REQUIRES_NEW = 3;  
    int PROPAGATION_NOT_SUPPORTED = 4;  
    int PROPAGATION_NEVER = 5;  
    int PROPAGATION_NESTED = 6;  
    ....... 
}

同时为了方便引用,Spring 相应地定义了一个枚举类:Propagation

package org.springframework.transaction.annotation;  

public enum Propagation {
     
    REQUIRED(0),  
    SUPPORTS(1),  
    MANDATORY(2),  
    REQUIRES_NEW(3),  
    NOT_SUPPORTED(4),  
    NEVER(5),  
    NESTED(6);  

    private final int value;  

    private Propagation(int value) {
     
        this.value = value;  
    }  

    public int value() {
     
        return this.value;  
    }  
}

7种传播机制:事务的"七种人格"

1️⃣ REQUIRED:事务界的"随遇而安者"(默认值)

REQUIRED:默认值,有事务则加入,无事务则新建

类比场景

"哎哟,已经有人开车了?那我直接蹭车!"
"咦,没有人有车?那我买车开!"

代码示例

@Transactional
public void createOrder(Order order) {
   
    // 当前没有事务 → 新建事务
    inventoryService.reduce(order); 
}

@Service
public class InventoryService {
   

    // REQUIRED是默认值,可不写
    @Transactional
    public void reduce(Order order) {
   
        // 当前已有事务 → 加入已有事务
    }
}

适用场景绝大多数业务方法,保证数据一致性
注意陷阱

  • 一旦加入已有事务,回滚会一起回滚
  • 不适合需要独立提交的场景

2️⃣ SUPPORTS:事务界的"佛系青年"

SUPPORTS:有事务则加入,无事务则非事务执行

类比场景

"有人有车?那我蹭一下"
"没人有车?那大家各自想法解决吧"

代码示例

@Transactional
public void createOrder(Order order) {
   
    auditService.logAction(); // 加入当前事务
}

@Service
public class AuditService {
   

    @Transactional(propagation = Propagation.SUPPORTS)
    public void logAction() {
   
        // 如果有事务 → 加入
        // 如果没有事务 → 非事务执行
    }
}

适用场景只读操作,如记录日志、审计跟踪

注意陷阱

  • 不能用于写操作,可能导致数据不一致
  • 外部无事务时,内部操作无回滚保障

3️⃣ MANDATORY:事务界的"死硬派"

MANDATORY:必须有事务,否则抛异常

类比场景

"必须要有车才去!没有?那大家都别去了!"

代码示例

// 外部调用
public void processWithoutTransaction() {
   
    // 直接调用会报错!
    internalService.mustBeInTransaction(); 
}

@Service
public class InternalService {
   

    @Transactional(propagation = Propagation.MANDATORY)
    public void mustBeInTransaction() {
   
        // 必须被事务方法调用
    }
}

适用场景核心业务方法,确保只能在事务环境中执行

注意陷阱

  • 单独调用会直接抛出IllegalTransactionStateException
  • 适合内部服务,不适合对外接口

4️⃣ REQUIRES_NEW:事务界的"独行侠"

REQUIRES_NEW:挂起当前事务,新建事务

类比场景

"已经有车了?那你们先别开,等我开自己的车到了你们再继续开!"

代码示例

@Transactional
public void createOrder(Order order) {
   
    try {
   
        // 挂起当前事务,开启新事务
        paymentService.processPayment(order); 
    } catch (Exception e) {
   
        // 即使支付失败,订单仍可提交
    }
}

@Service
public class PaymentService {
   

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(Order order) {
   
        // 独立事务,失败不影响外部事务
    }
}

适用场景

  • 需要独立提交的操作,如支付记录、日志记录
  • 失败不影响主流程的关键操作

注意陷阱

  • 频繁使用会导致性能下降(多次开启/提交事务)
  • 嵌套调用可能导致死锁

5️⃣ NOT_SUPPORTED:事务界的"反社会者"

NOT_SUPPORTED:挂起当前事务,非事务执行

类比场景

"有车?那你们等等我,我不开车想别的办法过去,我到了你们再开!"

代码示例

@Transactional
public void createOrder(Order order) {
   
    // 挂起当前事务,非事务执行
    externalSystemService.notify(order); 
    // 继续当前事务
}

@Service
public class ExternalSystemService {
   

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void notify(Order order) {
   
        // 非事务执行,即使失败也不影响主事务
    }
}

适用场景

  • 调用外部系统(如短信、邮件服务)
  • 长时间运行的操作,避免占用事务连接

注意陷阱

  • 非事务操作无法回滚
  • 可能导致数据最终一致性问题

6️⃣ NEVER:事务界的"洁癖患者"

NEVER:非事务执行,有事务则抛异常

类比场景

"咱们都是穷人,谁有车就是背叛了组织,就谁也别去了!"

代码示例

// 外部调用
@Transactional
public void createOrder(Order order) {
   
    // 直接报错!
    reportService.generateReport(); 
}

@Service
public class ReportService {
   

    @Transactional(propagation = Propagation.NEVER)
    public void generateReport() {
   
        // 必须在非事务环境下执行
    }
}

适用场景

  • 只读报表生成
  • 某些特殊数据库操作(如DDL语句)

注意陷阱

  • 被事务方法调用会直接抛出异常
  • 使用场景非常有限

7️⃣ NESTED:事务界的"救火队员"

NESTED:嵌套事务,保存点机制

类比场景

"已经有人开车了?那我设个路标,走错了路我可以自己回到路标处!",如果大家都没车,那我就开自己的车了。

代码示例

@Transactional
public void createOrder(Order order) {
   
    try {
   
        // 创建保存点,可单独回滚
        userService.updateUserPoints(order); 
    } catch (Exception e) {
   
        // 用户积分更新失败,但不影响订单创建
    }
}

@Service
public class UserService {
   

    @Transactional(propagation = Propagation.NESTED)
    public void updateUserPoints(Order order) {
   
        // 嵌套事务,可单独回滚
    }
}

适用场景

  • 部分操作需要独立回滚
  • 复杂业务流程中的子步骤

注意陷阱

  • 依赖数据库的保存点功能(MySQL 5.6+支持)
  • 与REQUIRES_NEW的区别:NESTED是同一物理事务

传播机制的"连环套":真实业务场景解析

订单系统的事务链

@Transactional
public void createOrder(Order order) {
   
    // REQUIRED (默认)
    orderDao.create(order); 

    try {
   
        // REQUIRES_NEW - 独立事务,支付记录要保存
        paymentService.process(order); 

        // NESTED - 可单独回滚,不影响主流程
        userService.updatePoints(order); 

        // NOT_SUPPORTED - 调用外部系统,不占用事务
        smsService.sendConfirmation(order); 
    } catch (Exception e) {
   
        // 处理异常
    }

    // REQUIRED - 加入当前事务
    inventoryService.reduce(order); 
}

💡 传播机制选择指南

业务需求 推荐传播行为
保证整体一致性 REQUIRED (默认)
操作必须在事务中 MANDATORY
需要独立提交/回滚 REQUIRES_NEW
调用外部系统 NOT_SUPPORTED
只读操作 SUPPORTS
部分操作可回滚 NESTED
绝对不能在事务中 NEVER

🔧 事务传播最佳实践

1. 80/20法则:合理控制事务边界

  • **80%的方法使用默认的REQUIRED
  • **20%的关键方法显式指定传播行为

2. 事务"瘦身"原则

  • 避免在事务中执行耗时操作(如远程调用、复杂计算)
  • 使用NOT_SUPPORTED处理非关键外部调用

3. 异常与传播的黄金搭配

@Transactional(propagation = Propagation.REQUIRES_NEW, 
               rollbackFor = Exception.class)
public void criticalOperation() throws Exception {
   
    // 关键操作,任何异常都要回滚
}

4. 事务方法命名规范

  • Transactional结尾:createOrderTransactional
  • 或使用注释明确标注:/* @Transactional */

5. 单元测试验证传播行为

@Test
@Transactional
void testNestedTransaction() {
   
    // 验证NESTED行为
    assertThrows(Exception.class, () -> orderService.processWithNested());
    // 验证内部操作是否回滚
    assertFalse(orderDao.exists(orderId));
}

结语:事务传播的"道"与"术"

事务传播机制就像是需要精准把握的手术刀。用得好,系统健壮如磐石;用不好,数据崩坏如山倒。

下次当你敲下@Transactional(propagation = ...)时,请先问问自己:

  1. 这个操作是否需要独立事务
  2. 失败时希望如何回滚
  3. 外部系统交互时如何保证一致性?
  4. 性能影响是否可接受?

最后送大家一张事务传播分类图,收藏起来随时查阅:

事务传播.png事务传播.png

相关文章
|
2天前
|
人工智能 运维 安全
|
4天前
|
SpringCloudAlibaba 负载均衡 Dubbo
微服务架构下Feign和Dubbo的性能大比拼,到底鹿死谁手?
本文对比分析了SpringCloudAlibaba框架下Feign与Dubbo的服务调用性能及差异。Feign基于HTTP协议,使用简单,适合轻量级微服务架构;Dubbo采用RPC通信,性能更优,支持丰富的服务治理功能。通过实际测试,Dubbo在调用性能、负载均衡和服务发现方面表现更出色。两者各有适用场景,可根据项目需求灵活选择。
386 124
微服务架构下Feign和Dubbo的性能大比拼,到底鹿死谁手?
|
7天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
695 107
|
2天前
|
算法 Python
【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)
【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)
223 152
|
4天前
|
Java 数据库 数据安全/隐私保护
Spring 微服务和多租户:处理多个客户端
本文介绍了如何在 Spring Boot 微服务架构中实现多租户。多租户允许单个应用实例为多个客户提供独立服务,尤其适用于 SaaS 应用。文章探讨了多租户的类型、优势与挑战,并详细说明了如何通过 Spring Boot 的灵活配置实现租户隔离、动态租户管理及数据源路由,同时确保数据安全与系统可扩展性。结合微服务的优势,开发者可以构建高效、可维护的多租户系统。
202 127
|
3天前
|
Web App开发 前端开发 API
在折叠屏应用中,如何处理不同屏幕尺寸和设备类型的样式兼容性?
在折叠屏应用中,如何处理不同屏幕尺寸和设备类型的样式兼容性?
230 124
|
2天前
|
编解码 算法 自动驾驶
【雷达通信】用于集成传感和通信的OFDM雷达传感算法(Matlab代码实现)
【雷达通信】用于集成传感和通信的OFDM雷达传感算法(Matlab代码实现)
172 125
|
1天前
|
JavaScript 关系型数据库 MySQL
基于python的网上外卖订餐系统
本系统基于Python与Flask框架,结合MySQL数据库及Vue前端技术,实现了一个功能完善的网上订餐平台。系统涵盖餐品、订单、用户及评价管理模块,并深入研究订餐系统的商业模式、用户行为与服务质量。技术上采用HTML、PyCharm开发工具,支持移动端访问,助力餐饮业数字化转型。