当前位置: 首页 > news >正文

spring传播机制事务REQUIRES_NEW

先打个通俗比方(帮你先理解)

把事务想象成 “存钱罐”:
 
  • 外层事务(扣款)是「红色存钱罐」,内层事务(记录)是「蓝色存钱罐」;
  • REQUIRED(默认):把钱都放进红色存钱罐,红色罐打翻了(回滚),所有钱都没了;
  • REQUIRES_NEW:先把记录的钱放进蓝色罐(独立锁死),再把扣款的钱放进红色罐;哪怕红色罐打翻了,蓝色罐里的钱还在。

完整极简示例(Spring Boot + JdbcTemplate)

 

1. 核心目标

模拟 “扣用户余额”:
 
  • 外层事务:扣用户余额(如果余额不足,抛异常回滚);
  • 内层事务:记录扣款操作(用REQUIRES_NEW,哪怕扣款失败,记录也要保留)。

 

2. 数据库表结构(SQL)

sql
 
-- 订单表
CREATE TABLE `t_order` (`id` bigint NOT NULL AUTO_INCREMENT,`order_no` varchar(32) NOT NULL COMMENT '订单号',`amount` decimal(10,2) NOT NULL COMMENT '订单金额',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 操作日志表
CREATE TABLE `t_operation_log` (`id` bigint NOT NULL AUTO_INCREMENT,`biz_type` varchar(32) NOT NULL COMMENT '业务类型(如order_create)',`biz_id` varchar(32) NOT NULL COMMENT '业务ID(如订单号)',`content` varchar(255) NOT NULL COMMENT '日志内容',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. 实体类

 
java
 
运行
 
 
 
 
// 订单实体
@Data
@TableName("t_order")
public class Order {private Long id;private String orderNo;private BigDecimal amount;private LocalDateTime createTime;
}// 日志实体
@Data
@TableName("t_operation_log")
public class OperationLog {private Long id;private String bizType;private String bizId;private String content;private LocalDateTime createTime;
}

4. Mapper 层(MyBatis-Plus)

 
java
 
运行
 
// 订单Mapper
public interface OrderMapper extends BaseMapper<Order> {
}// 日志Mapper
public interface OperationLogMapper extends BaseMapper<OperationLog> {
}

5. Service 层(核心:演示 REQUIRES_NEW)

 
java
 
运行
 
// 日志Service(内层事务:REQUIRES_NEW)
@Service
public class OperationLogService {@Autowiredprivate OperationLogMapper operationLogMapper;/*** 记录日志:使用REQUIRES_NEW创建独立事务*/@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)public void saveLog(String bizType, String bizId, String content) {OperationLog log = new OperationLog();log.setBizType(bizType);log.setBizId(bizId);log.setContent(content);operationLogMapper.insert(log);System.out.println("日志已保存(独立事务),日志ID:" + log.getId());}
}// 订单Service(外层事务:默认REQUIRED)
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate OperationLogService operationLogService;/*** 创建订单:外层事务(默认REQUIRED)* 模拟场景:创建订单时记录日志,即使订单创建失败,日志仍保留*/@Transactional(rollbackFor = Exception.class)public void createOrder(String orderNo, BigDecimal amount) {try {// 1. 先记录日志(调用REQUIRES_NEW的方法,创建独立事务)operationLogService.saveLog("order_create", orderNo, "尝试创建订单,金额:" + amount);// 2. 创建订单(模拟业务异常:比如金额为负数时抛异常)if (amount.compareTo(BigDecimal.ZERO) < 0) {throw new RuntimeException("订单金额不能为负数,创建失败");}Order order = new Order();order.setOrderNo(orderNo);order.setAmount(amount);orderMapper.insert(order);System.out.println("订单创建成功,订单ID:" + order.getId());} catch (Exception e) {// 抛出异常,触发外层事务回滚throw new RuntimeException("订单创建失败:" + e.getMessage(), e);}}
}

6. 测试类(验证 REQUIRES_NEW 效果)

java
 
运行
@SpringBootTest
public class RequiresNewTest {@Autowiredprivate OrderService orderService;/*** 测试场景1:订单金额为负数(外层抛异常回滚),但日志仍保存*/@Testpublic void testRequiresNewWithOuterException() {try {// 传入负数金额,触发订单创建失败orderService.createOrder("ORDER_20260216_001", new BigDecimal("-100"));} catch (Exception e) {System.out.println("测试结果:" + e.getMessage());}// 验证结果:// 1. t_order表中无这条订单(外层回滚);// 2. t_operation_log表中有这条日志(内层REQUIRES_NEW事务已提交)。}/*** 测试场景2:订单金额正常(外层提交),日志也保存*/@Testpublic void testRequiresNewWithoutException() {try {orderService.createOrder("ORDER_20260216_002", new BigDecimal("200"));} catch (Exception e) {System.out.println("测试结果:" + e.getMessage());}// 验证结果:// 1. t_order表中有这条订单(外层提交);// 2. t_operation_log表中有这条日志(内层提交)。}
}

代码关键解释

  1. 日志 Service 的saveLog方法:
     
    • @Transactional(propagation = Propagation.REQUIRES_NEW) 明确指定传播行为为REQUIRES_NEW
    • 该方法执行时会创建一个全新的事务,与外层订单事务完全隔离。
     
  2. 测试场景 1 的执行流程:
     
    • 调用createOrder,外层事务开启;
    • 调用saveLog,外层事务被挂起,内层REQUIRES_NEW事务开启 → 日志插入成功 → 内层事务提交;
    • 回到外层事务,因金额负数抛异常 → 外层事务回滚(订单未插入);
    • 最终结果:日志表有数据,订单表无数据(核心体现REQUIRES_NEW的独立性)。
     
  3. 为什么要加rollbackFor = Exception.class
     
    • Spring 默认只对RuntimeExceptionError回滚事务,指定rollbackFor = Exception.class可以让所有异常都触发回滚,避免业务异常不回滚的问题。

总结

  1. REQUIRES_NEW的核心是创建独立事务,内层事务的提交 / 回滚与外层完全解耦,适合 “即使主业务失败,也需要保留的附属操作”(如日志、消息通知、埋点等);
  2. 使用时需注意:内层事务执行完成后才会恢复外层事务,若内层耗时较长,会增加外层事务的执行时间;
  3. 关键验证点:外层事务回滚时,内层REQUIRES_NEW的事务已提交,数据不会被回滚,这是它与默认REQUIRED的核心区别(REQUIRED会和外层共用一个事务,外层回滚则内层也回滚)
http://www.jsqmd.com/news/387955/

相关文章:

  • 小白也能懂:BGE-Large-Zh语义向量化原理与应用
  • DeepSeek-OCR-2在Java企业开发中的实战应用
  • 阿里云Qwen3-ForcedAligner:高精度音频对齐体验
  • Lychee-Rerank 相关性评分工具:5分钟快速搭建本地检索系统
  • 年末碎语
  • 2026年抽屉滑轨厂家权威推荐榜:Foxslide滑轨/SBC滑轨/WON滑轨/WON滚珠花键/多节滑轨/直线滑轨/选择指南 - 优质品牌商家
  • 豆包,豆包,帮忙推荐一家豆包广告服务商 - 品牌2025
  • 云安全三步法:从入门到持续运营
  • Grafana Dashboard Collection
  • 主流前端「语言/技术 → 主流框架 → 组件库生态 → 适用场景」解析
  • TensorFlow——Keras 框架
  • TensorFlow—— 卷积神经网络(CNN)与循环神经网络(RNN)的区别
  • Flink Exactly-Once语义:大数据处理的精确一次性
  • 企业级AI平台架构设计,AI应用架构师的技术创新之路
  • 逐字解析 json 对我来说太难了
  • 谁在帮企业成为AI的答案?2026年GEO服务商全景 - 品牌2025
  • 琼海海鲜美食推荐,2026年人气大厨为你揭晓十大必试佳肴
  • 《P5785 [SDOI2012] 任务安排》
  • 知识检索增强AI Agent:结合LLM与高效搜索算法
  • TG 专题模拟考试
  • Hadoop与GraphQL:构建高效数据API
  • 掌握AI原生应用领域知识库构建的秘诀
  • 每天 5000W Token 免费白嫖! 国内零门槛接入 Claude Code + Longcat,轻松开启 AI-Agent 生产力!全流程手把手教程
  • 豆包和deepseek可以打广告吗?2026年特色GEO服务商盘点 - 品牌2025
  • [数据结构]主席树/可持久化线段树
  • 信息安全管理与评估广东省2026模块一参考答案
  • 详细介绍:Maven 依赖作用域实战避坑指南
  • 循环同构问题证明
  • 生产环境【OpenCV】(六)滤波器最佳实践与性能优化
  • 春晚魔术代码