Java事务传播级别REQUIRES_NEW,REQUIRED_NEW区别

文章目录

先纠正名称:Spring 里叫 REQUIRES_NEW ,没有 REQUIRED_NEW

用一句话记忆:

REQUIRED:有事务就加入,没有就创建。

REQUIRES_NEW:不管有没有,都另开一个新事务。

打比方理解:餐厅结账

把"事务"想象成餐厅的一张账单。

REQUIRED:有账单就记在同一张上

外层方法已经开了一张账单,内层方法选择 REQUIRED

"不用开新单,把我的消费也记在外层那张账单上。"

因此外层和内层实际上共用同一个数据库事务:

text 复制代码
外层事务
├── 创建订单
├── 扣减库存(内层 REQUIRED)
└── 创建支付记录

任何一步失败,整张账单作废:

text 复制代码
订单回滚
库存回滚
支付记录回滚

如果当前没有事务,REQUIRED 就自己创建一个。

REQUIRES_NEW:暂停旧账单,另开一张新账单

外层已经有事务,调用 REQUIRES_NEW

"先把外层账单放一边,我单独开张账单,单独结账。"

text 复制代码
外层事务A:创建订单
    ↓ 暂停
内层事务B:保存操作日志
    ↓ 独立提交
恢复外层事务A
    ↓
外层事务A失败并回滚

最终结果:

text 复制代码
订单:回滚
日志:已经独立提交,仍然存在

Spring 官方定义也是如此:REQUIRED 加入现有事务;REQUIRES_NEW 暂停现有事务并创建独立物理事务。Spring 事务传播文档

核心区别

情况 REQUIRED REQUIRES_NEW
外层没有事务 创建新事务 创建新事务
外层已有事务 加入外层事务 暂停外层,另开事务
与外层是否同一事务
内层提交 等外层一起提交 内层立即独立提交
外层后来回滚 内层一起回滚 已提交的内层不回滚
数据库连接 通常共用外层连接 通常需要额外连接
常见用途 普通业务流程 日志、状态记录等独立操作

代码示例

REQUIRED:订单和库存必须同生共死

java 复制代码
@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        orderMapper.insertOrder();

        // InventoryService中的方法也是REQUIRED
        inventoryService.deductStock();

        throw new RuntimeException("创建支付记录失败");
    }
}
java 复制代码
@Service
public class InventoryService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock() {
        inventoryMapper.deductStock();
    }
}

因为 deductStock() 加入外层事务,所以最终:

text 复制代码
订单插入:回滚
库存扣减:回滚

实际上 @Transactional 默认传播行为就是 REQUIRED,通常不写也可以:

java 复制代码
@Transactional

等价于:

java 复制代码
@Transactional(propagation = Propagation.REQUIRED)

REQUIRES_NEW:日志必须独立保存

java 复制代码
@Service
public class AuditLogService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String message) {
        auditLogMapper.insert(message);
    }
}
java 复制代码
@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        orderMapper.insertOrder();

        auditLogService.saveLog("开始创建订单");

        throw new RuntimeException("订单创建失败");
    }
}

结果是:

text 复制代码
订单:回滚
日志:保留

因为日志事务已经独立提交。

一个非常容易踩的坑

假设内层使用 REQUIRED,发生异常后,外层把异常吃掉了:

java 复制代码
@Transactional
public void outer() {
    try {
        inventoryService.deductStock();
    } catch (Exception e) {
        // 吃掉异常
    }

    orderMapper.insertOrder();
}

你可能以为外层还能提交,但内层和外层共用事务,内层可能已经把整个事务标记为:

text 复制代码
rollback-only:只能回滚

外层最后提交时,Spring 可能抛出:

text 复制代码
UnexpectedRollbackException

就像账单已经被盖上"作废"章,即使经理说"我处理了",结账系统仍不允许提交。

REQUIRES_NEW 也不是内层失败后外层一定成功

如果内层异常继续向外抛:

java 复制代码
@Transactional
public void outer() {
    auditLogService.saveLog(); // REQUIRES_NEW,内部抛异常
    orderMapper.insertOrder();
}

虽然两个事务相互独立,但异常传到外层后,外层也可能因为这个异常回滚。

如果业务允许内层失败、外层继续,外层必须明确处理异常:

java 复制代码
@Transactional
public void outer() {
    try {
        auditLogService.saveLog();
    } catch (Exception e) {
        log.error("保存日志失败", e);
    }

    orderMapper.insertOrder();
}

区别是:REQUIRES_NEW 的内层回滚不会直接把外层标记为 rollback-only;但传播出去的异常仍可能触发外层自己的回滚规则。

什么时候使用哪个?

绝大多数业务使用 REQUIRED

  • 创建订单和订单明细
  • 扣库存和记录库存流水
  • 转账的扣款与入账
  • 删除主表和关联数据

需要所有步骤"同生共死"。

少数真正独立的操作使用 REQUIRES_NEW

  • 无论主业务成功失败,都要记录审计日志
  • 独立记录任务执行状态
  • 保存失败原因或重试记录
  • 某个子任务允许单独提交

REQUIRES_NEW 的三个风险

  1. 占用额外数据库连接

外层事务的连接不会释放,内层还要再申请一个连接。高并发时可能耗尽连接池,甚至造成等待或死锁。Spring 官方特别提醒

  1. 不要更新外层已经锁住的数据

外层事务锁住一行后,内层新事务又修改同一行,内层可能一直等待外层释放锁;但外层又在等待内层结束,最终超时。

  1. 同类内部调用可能不生效

这样调用:

java 复制代码
this.saveLog();

可能绕过 Spring 代理,导致 REQUIRES_NEW 没有真正创建新事务。通常应拆到另一个 Spring Bean:

java 复制代码
orderService
    → auditLogService.saveLog()

最终记忆口诀:

REQUIRED:加入团队,共同成败。

REQUIRES_NEW:另立项目,独立结算。

日常默认选 REQUIRED,只有业务明确要求独立提交时才选 REQUIRES_NEW

相关推荐
jun_bai1 小时前
使用java安全的移动文件
java
zdl6861 小时前
EasyMarkets:“芯片波动考验科技估值”
数据库·人工智能·科技
caishenzhibiao1 小时前
市场同步系统 同花顺期货通指标
java·c语言·c#
Devin~Y1 小时前
从本地生活电商到 AI RAG:互联网大厂 Java 面试场景完整实战
java·spring boot·redis·elasticsearch·spring cloud·kafka·rag
520拼好饭被践踏1 小时前
JAVA+Agent学习day25
java·开发语言·学习
大黄说说1 小时前
Java 并发大坑:volatile、synchronized、Lock 三者如何选择?
java·开发语言
翼龙云_cloud2 小时前
阿里云国际站代理商:ECS弹性伸缩 自动应对流量高峰
运维·网络·数据库·阿里云·架构
吴声子夜歌2 小时前
MongoDB 4.2——安装 MongoDB
数据库·mongodb
吴声子夜歌2 小时前
MongoDB 8.0——MongoDB的安装
数据库·mongodb
IMPYLH2 小时前
HTML 的 <em> 元素
java·前端·html