Spring 事务传播机制 + 隔离级别

一、核心前置说明

  1. 传播机制:多个 @Transactional 方法内部调用,事务如何传递、创建、隔离
  2. 隔离级别:并发事务之间读写可见性,解决脏读 / 不可重复读 / 幻读
  3. 环境:SpringBoot 2.5 + MySQL InnoDB
  4. 关键前提:必须通过 Spring 代理调用(外部 Service 调用、不要本类 this 调用),事务才会生效

二、事务隔离级别(Isolation)+ 代码示例

1. 五种隔离级别枚举

复制代码
public enum Isolation {
    // 使用数据库默认隔离级别
    DEFAULT,
    // 读未提交:脏读+不可重复读+幻读都存在
    READ_UNCOMMITTED,
    // 读已提交:解决脏读,存在不可重复读、幻读
    READ_COMMITTED,
    // 可重复读:MySQL默认,解决脏读、不可重复读,存在幻读(间隙锁优化)
    REPEATABLE_READ,
    // 串行化:最高隔离,完全锁表,性能差
    SERIALIZABLE
}

2. 业务代码配置隔离级别

复制代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

    /**
     * 默认隔离级别:跟随MySQL → REPEATABLE_READ
     */
    @Transactional
    public void createOrderDefault() {
        // 新增订单、扣库存
    }

    /**
     * 读已提交:避免脏读,适合大多数金融、订单业务
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void createOrderReadCommitted() {
        // 业务操作
    }

    /**
     * 可重复读:同事务多次查询结果一致
     */
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void queryOrderRepeatableRead() {
        // 多次查询订单数据
    }
}

3. 并发问题对照(面试背诵)

  • 脏读:读取到其他事务未提交数据
  • 不可重复读:同一事务内,多次查询被其他事务 update 修改
  • 幻读:同一事务内,多次查询被其他事务 insert/delete 改变行数

三、7 种事务传播机制(Propagation)+ 完整场景代码

1. 传播机制枚举含义

复制代码
public enum Propagation {
    REQUIRED,        // 默认:有事务加入,无则新建
    SUPPORTS,        // 有事务加入,无则非事务运行
    MANDATORY,       // 强制必须在事务内,无事务直接报错
    REQUIRES_NEW,    // 新建独立事务,外部事务挂起,互不影响回滚
    NOT_SUPPORTED,   // 强制非事务运行,挂起外部事务
    NEVER,           // 强制非事务,有事务直接报错
    NESTED           // 嵌套事务:依赖外部事务,通过savepoint保存点
}

2. 分层业务 Service(模拟方法内部调用)

外层主业务
复制代码
@Service
public class BusinessService {

    private final LogService logService;

    // 构造器注入
    public BusinessService(LogService logService) {
        this.logService = logService;
    }

    /**
     * 外层主方法:默认 REQUIRED
     */
    @Transactional(rollbackFor = Exception.class)
    public void mainBusiness() throws Exception {
        // 1. 主业务入库
        System.out.println("外层主业务执行");

        // 2. 调用内部日志方法
        logService.saveOperateLog();

        // 模拟业务异常
        int a = 1 / 0;
    }
}
内层不同传播机制实现类
复制代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class LogService {

    // 1. REQUIRED 默认:共用外层事务,外层异常整体回滚
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveOperateLog() {
        System.out.println("REQUIRED:保存操作日志");
    }

    // 2. REQUIRES_NEW:独立新事务,外层回滚、日志依然保留
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void saveLogIndependent() {
        System.out.println("REQUIRES_NEW:独立日志事务");
    }

    // 3. NESTED 嵌套事务:依赖外层事务,内部可局部回滚
    @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
    public void nestedLog() {
        System.out.println("NESTED:嵌套保存点事务");
    }

    // 4. NOT_SUPPORTED:强制非事务执行
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void noTxOperate() {
        System.out.println("NOT_SUPPORTED:无事务执行");
    }

    // 5. SUPPORTS:有事务共用,无事务裸跑
    @Transactional(propagation = Propagation.SUPPORTS)
    public void supportQuery() {
        System.out.println("SUPPORTS:查询业务");
    }
}

四、高频场景关键差异(代码对应落地)

场景 1:REQUIRED(默认)

  • 外层有事务 → 合并同一个事务
  • 外层抛异常 → 内层、外层全部回滚
  • 适用:90% 增删改业务

场景 2:REQUIRES_NEW【高频面试 + 实战】

修改外层调用代码:

复制代码
@Transactional(rollbackFor = Exception.class)
public void mainBusiness() {
    System.out.println("外层主业务");
    // 独立事务,不受外层异常影响
    logService.saveLogIndependent();
    // 主业务异常
    int i = 1 / 0;
}
  • 结果:主业务回滚,日志数据正常提交
  • 适用:操作日志、监控记录、消息投递,不能因为主业务失败丢失日志

场景 3:NESTED 嵌套事务

复制代码
@Transactional(rollbackFor = Exception.class)
public void nestedDemo() {
    // 主业务
    // 嵌套子事务
    try {
        logService.nestedLog();
        int a = 1 / 0;
    } catch (Exception e) {
        // 子事务局部回滚,主事务可正常提交
        e.printStackTrace();
    }
}
  • 依靠 savepoint 保存点
  • 子异常可单独回滚,外层正常提交;外层回滚,子事务强制回滚

场景 4:NOT_SUPPORTED 非事务

复制代码
@Transactional
public void testNoSupport() {
    // 此段无事务,即时写入数据库
    logService.noTxOperate();
    // 后续异常,上面数据不会回滚
    int a = 1 / 0;
}

五、事务失效经典坑(代码避坑)

  1. 本类 this 调用 事务方法,不走代理,事务失效

    复制代码
    // 错误写法
    public void outer() {
        this.innerTx();
    }
    @Transactional
    public void innerTx(){}
  2. 异常不是 RuntimeException / Error,未配置 rollbackFor

    复制代码
    // 正确写法
    @Transactional(rollbackFor = Exception.class)
  3. 方法权限为 private,无法被 Spring 代理


六、面试极简总结(代码版)

  1. 隔离级别:控制并发事务 数据可见性,MySQL 默认 REPEATABLE_READ
  2. 传播机制:控制方法嵌套调用事务归属
  3. 日常开发:
    • 增删改:@Transactional(rollbackFor = Exception.class)
    • 日志独立:Propagation.REQUIRES_NEW
    • 查询接口:Propagation.SUPPORTS
  4. 核心区别:
    • REQUIRES_NEW:完全独立事务
    • NESTED:依赖外层、保存点局部回滚
相关推荐
Arya_aa1 小时前
数据字典模块–JSR303参数校验
java
明月(Alioo)2 小时前
给 AI Agent 装上“大脑“:Java语言中Code Interpreter 的设计与实现
java·ai·agent
QuZero2 小时前
StampedLock Mechanism
java·算法
Javatutouhouduan2 小时前
Java小白如何快速玩转Redis?
java·数据库·redis·分布式锁·java面试·后端开发·java程序员
xuhaoyu_cpp_java2 小时前
Spring学习(一)
java·经验分享·笔记·学习·spring
陈随易2 小时前
为什么今天还会有新语言?MoonBit 想解决什么问题?
前端·后端·程序员
Cosolar2 小时前
大型语言模型(LLM)微调与量化技术全指南——从预训练到高效部署
人工智能·后端·面试
SamDeepThinking3 小时前
代码能跑就别动?有AI之后其实未必
后端·程序员·ai编程
kybs19913 小时前
springboot视频推荐系统--附源码72953
java·spring boot·python·eclipse·asp.net·php·idea