Spring 事务传播机制与 REQUIRES_NEW
Spring 事务传播机制与 REQUIRES_NEW一、核心概念什么是事务传播行为当一个事务方法调用另一个事务方法时Spring 需要决定如何处理事务边界——是加入已有事务、新建独立事务还是以无事务方式执行。这就是事务传播行为Propagation Behavior。方法 A有事务 → 调用方法 B也声明了事务 ↓ B 是加入 A 的事务 还是新建自己的事务 还是挂起 A 的事务为什么需要 REQUIRES_NEW默认传播行为REQUIRED表示有事务就加入没有就新建。但在某些场景下方法 B 的成功/失败不应影响方法 A或者方法 B 需要独立提交即使 A 后续回滚此时需要REQUIRES_NEW。REQUIRED默认 REQUIRES_NEW ┌── 事务 A ──────────┐ ┌── 事务 A ──────────┐ │ 操作1 │ │ 操作1 │ │ ┌── 方法 B ────┐ │ │ ┌── 事务 B ────┐ │ ← 独立事务 │ │ 操作2 │ │ │ │ 操作2 │ │ │ │ 操作3 │ │ │ │ 操作3 │ │ │ └──────────────┘ │ │ └── 提交/回滚 ─┘ │ │ 操作4 │ │ 操作4 │ └── 全部提交或回滚 ───┘ └── 提交/回滚 ────────┘ ↑ 两个事务互相独立注博客https://blog.csdn.net/badao_liumang_qizhi二、Spring 全部传播行为传播行为含义适用场景REQUIRED有事务加入没有新建默认选择绝大多数业务方法REQUIRES_NEW挂起当前事务新建独立事务独立提交、错误日志记录、MQ 消费NESTED在当前事务中创建保存点嵌套事务部分失败可回滚到保存点SUPPORTS有事务加入没有就非事务执行只读查询NOT_SUPPORTED挂起当前事务非事务执行长时间查询不占事务连接MANDATORY必须在已有事务中调用否则抛异常强制要求调用方提供事务NEVER必须在无事务中调用否则抛异常明确不允许事务的操作三、REQUIRES_NEW 的行为详解执行流程1. 调用方存在事务 T1 2. Spring 检测到 REQUIRES_NEW 3. 挂起事务 T1释放 T1 的数据库连接回连接池不保持持有但暂停 4. 新建事务 T2获取新的数据库连接 5. 执行方法体 6. T2 提交或回滚 7. 恢复事务 T1继续执行隔离性TransactionalpublicvoidmethodA(){// 事务 T1orderRepo.save(order);// T1 中写入数据methodB();// T2 独立提交// 此时 T1 仍未提交// T2 已提交的数据对 T1 可见取决于隔离级别// 如果 T1 后续回滚T2 的数据不受影响}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidmethodB(){// 事务 T2独立于 T1logRepo.save(errorLog);// T2 提交后立即持久化// 即使 methodA 后续抛异常回滚 T1这条日志也不会丢}关键特性特性说明独立提交T2 提交不依赖 T1 的最终结果独立回滚T2 回滚不影响 T1除非异常被传播新连接T2 使用独立的数据库连接对 T1 不可见默认T1 未提交的数据在 T2 中不可见READ_COMMITTED 隔离级别下死锁风险T1 锁的行如果 T2 也要锁会死锁T1 等 T2 完成T2 等 T1 释放锁四、典型应用场景场景 1MQ 消费方法独立事务/** * MQ 消费者调用 Service 层的处理方法. * 为什么需要 REQUIRES_NEW * 1. MQ 框架调用消费方法时可能没有事务上下文 * 2. 需要明确的事务边界控制提交/回滚 * 3. 异常时只回滚本次业务操作不影响消费框架 */Transactional(propagationPropagation.REQUIRES_NEW,rollbackForException.class)publicvoidprocessMessage(LonglogId){// 独立事务成功则提交失败则回滚TaskLogtaskLogtaskLogRepo.findById(logId).orElseThrow();businessService.doWork(taskLog);taskLog.setStatus(Y);taskLogRepo.saveAndFlush(taskLog);}场景 2错误日志独立保存Transactional(rollbackForException.class)publicvoidprocessOrder(LongorderId){try{doBusinessLogic(orderId);}catch(Exceptione){// 业务失败当前事务将回滚// 但错误日志必须持久化不能跟着回滚errorLogService.saveErrorLog(orderId,e.getMessage());throwe;}}// 错误日志服务ServicepublicclassErrorLogService{Transactional(propagationPropagation.REQUIRES_NEW)publicvoidsaveErrorLog(LongbizId,StringerrorMsg){// 独立事务即使外层事务回滚错误日志也能保存ErrorLoglognewErrorLog();log.setBizId(bizId);log.setErrorMsg(errorMsg);log.setCreateTime(newDate());errorLogRepo.save(log);}}场景 3部分操作需要立即可见Transactional(rollbackForException.class)publicvoidcreateOrderAndNotify(OrderDtodto){// 主事务创建订单尚未提交OrderordercreateOrder(dto);// 需要立即生成一个编号并让其他服务可查到StringseqNosequenceService.generateAndPersist(order.getId());// 继续主流程...order.setSeqNo(seqNo);orderRepo.save(order);}ServicepublicclassSequenceService{Transactional(propagationPropagation.REQUIRES_NEW)publicStringgenerateAndPersist(LongorderId){// 独立事务立即提交其他事务可以查到这个编号StringseqgenerateNextSeq();SeqRecordrecordnewSeqRecord(orderId,seq);seqRepo.saveAndFlush(record);returnseq;}}场景 4批量处理中的单条隔离ServicepublicclassBatchProcessor{ResourceprivateSingleItemProcessorsingleItemProcessor;/** * 批量处理每条记录独立事务. * 一条失败不影响其他记录。 */publicBatchResultprocessBatch(ListLongitemIds){BatchResultresultnewBatchResult();for(LongitemId:itemIds){try{singleItemProcessor.processOne(itemId);result.addSuccess(itemId);}catch(Exceptione){// 单条失败不中断批量result.addFailed(itemId,e.getMessage());}}returnresult;}}ServicepublicclassSingleItemProcessor{Transactional(propagationPropagation.REQUIRES_NEW,rollbackForException.class)publicvoidprocessOne(LongitemId){// 每条记录一个独立事务// 失败只回滚当前这条ItemitemitemRepo.findById(itemId).orElseThrow();doProcess(item);item.setStatus(DONE);itemRepo.save(item);}}五、注意事项与陷阱5.1 自调用失效问题ServicepublicclassOrderService{// ❌ 错误同类内部调用事务注解不生效TransactionalpublicvoidmethodA(){this.methodB();// 直接调用不走代理REQUIRES_NEW 失效}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidmethodB(){// 实际仍在 methodA 的事务中执行}}解决方案// 方案1注入自身代理ServicepublicclassOrderService{ResourceprivateOrderServiceself;// 注入代理对象TransactionalpublicvoidmethodA(){self.methodB();// 通过代理调用事务注解生效}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidmethodB(){...}}// 方案2拆分到不同 Service推荐ServicepublicclassOrderService{ResourceprivateOrderLogServiceorderLogService;TransactionalpublicvoidmethodA(){orderLogService.methodB();// 不同类走代理}}ServicepublicclassOrderLogService{Transactional(propagationPropagation.REQUIRES_NEW)publicvoidmethodB(){...}}5.2 死锁风险TransactionalpublicvoidmethodA(){// T1 锁定了 order 表 id1 的行OrderorderorderRepo.findByIdForUpdate(1L);order.setStatus(PROCESSING);orderRepo.save(order);// 行锁未释放T1 未提交// 调用 REQUIRES_NEW 方法auditService.audit(1L);// T2 开始}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidaudit(LongorderId){// T2 也要锁 order 表 id1 的行OrderorderorderRepo.findByIdForUpdate(orderId);// 死锁// T2 等待 T1 释放行锁// T1 等待 T2 完成才能继续}规避方式// 方案1REQUIRES_NEW 方法不锁相同行Transactional(propagationPropagation.REQUIRES_NEW)publicvoidaudit(LongorderId){// 只读查询不加锁OrderorderorderRepo.findById(orderId).orElseThrow();// 写入审计表不同的表AuditLogauditnewAuditLog(orderId,APPROVED);auditLogRepo.save(audit);}// 方案2在 REQUIRES_NEW 调用前释放行锁flush 不再修改TransactionalpublicvoidmethodA(){OrderorderorderRepo.findById(1L).orElseThrow();order.setStatus(PROCESSING);orderRepo.saveAndFlush(order);// flush 但不释放锁// 改为事务提交后再调用审计避免死锁// 或者审计方法操作不同的行/表}5.3 连接池耗尽// ❌ 危险嵌套过深的 REQUIRES_NEWTransactionalpublicvoidlevel1(){level2();// 新连接}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidlevel2(){level3();// 又一个新连接}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidlevel3(){level4();// 又一个新连接...}// 每层挂起的事务都持有一个连接// 4 层嵌套 4 个数据库连接被同一个线程占用// 并发量大时连接池迅速耗尽规避方式控制 REQUIRES_NEW 嵌套深度建议不超过 2 层尽量在最内层使用不要级联嵌套5.4 异常传播TransactionalpublicvoidmethodA(){try{newTxService.methodB();// REQUIRES_NEW内部抛异常}catch(Exceptione){// T2 已回滚// 这里 catch 住了T1 不会回滚log.warn(methodB 失败继续执行 methodA,e);}// T1 继续正常提交}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidmethodB(){// T2 内部抛异常 → T2 回滚thrownewRuntimeException(业务异常);}注意如果 methodA 不 catch 异常异常传播到 methodA 的事务边界时T1 也会被标记为 rollback-only。六、REQUIRES_NEW vs NESTED维度REQUIRES_NEWNESTED事务关系完全独立的新事务外层事务的子事务保存点数据库连接使用新连接共用外层连接外层回滚影响不影响内层已提交内层也回滚保存点失效内层回滚影响不影响外层catch 住回滚到保存点外层可继续内层可见外层数据不可见不同连接可见同一连接JPA 支持完全支持取决于实现不是所有 JPA 实现都支持// NESTED内层失败可回滚到保存点外层继续TransactionalpublicvoidmethodA(){saveOrder();// 保存订单try{nestedMethod();// NESTED 事务}catch(Exceptione){// 内层回滚到保存点// 外层 saveOrder() 的数据不受影响}saveLog();// 继续执行}Transactional(propagationPropagation.NESTED)publicvoidnestedMethod(){// 在保存点内执行// 失败只回滚这部分}七、MQ 消费场景深入分析为什么 MQ 消费方法适合 REQUIRES_NEW// MQ 消费者框架代码简化ComponentpublicclassMqConsumer{RabbitListener(queuesmy-queue)publicvoidonMessage(LonglogId){// 1. MQ 框架调用此方法时通常没有外层事务// 2. 即使有框架层事务业务也应该隔离try{// REQUIRES_NEW 保证明确的事务边界businessService.processInNewTx(logId);// 成功 → T2 已提交 → ACK 消息}catch(Exceptione){// 失败 → T2 已回滚 → NACK/重试log.warn(消费失败,e);}}}ServicepublicclassBusinessService{Transactional(propagationPropagation.REQUIRES_NEW,rollbackForException.class)publicvoidprocessInNewTx(LonglogId){// 明确的独立事务// - 成功数据提交状态更新为 Y// - 失败数据回滚状态保持 O/P}}错误日志为什么也要 REQUIRES_NEWTransactional(propagationPropagation.REQUIRES_NEW,rollbackForException.class)publicvoidprocessMessage(LonglogId){try{doBusinessLogic(logId);markSuccess(logId);}catch(Exceptione){// 问题如果在当前事务中写错误日志事务回滚后日志也丢了// 解决错误日志写入方法也是 REQUIRES_NEWerrorLogService.saveError(logId,e);// 独立事务不随本事务回滚throwe;// 重新抛出让本事务回滚}}与事务后置动作的配合Transactional(propagationPropagation.REQUIRES_NEW,rollbackForException.class)publicvoidprocessMessage(LonglogId){// 业务逻辑...doWork();// 事务提交后才发送 MQ保证数据可见性TransactionSynchronizationManager.registerSynchronization(newTransactionSynchronizationAdapter(){OverridepublicvoidafterCommit(){// 此时 REQUIRES_NEW 的事务已提交// 新的消费者能读到本次写入的数据anotherMqSender.send(nextLogId);}});}八、完整示例MQ 消费 独立事务 错误处理/** * MQ 消费者. * 职责消息接收、锁控制、异常处理 * 不含业务逻辑。 */ComponentSlf4jpublicclassTaskMqConsumer{ResourceprivateTaskProcessServicetaskProcessService;ResourceprivateDistributedLockProviderlockProvider;RabbitListener(queues${mq.queue.task-process})publicvoidconsume(LonglogId){log.info(收到消息, logId{},logId);StringlockKeytask:process:logId;DistributedLocklocklockProvider.getLock(lockKey,60,TimeUnit.SECONDS);if(!lock.tryLock(30,TimeUnit.SECONDS)){log.warn(获取锁失败, logId{},logId);return;}try{// 调用独立事务的处理方法taskProcessService.processInNewTransaction(logId);}catch(Exceptione){// 事务已回滚消息可能重试log.warn(任务处理失败, logId{},logId,e);}finally{lock.unlock();}}}/** * 任务处理 Service. * REQUIRES_NEW 保证每次消费都是独立事务。 */ServiceSlf4jpublicclassTaskProcessService{ResourceprivateTaskLogRepositorytaskLogRepository;ResourceprivateErrorLogServiceerrorLogService;ResourceprivateTaskProcessortaskProcessor;ResourceprivateFollowUpMqSenderfollowUpMqSender;Transactional(propagationPropagation.REQUIRES_NEW,rollbackForException.class)publicvoidprocessInNewTransaction(LonglogId){// 1. 查询任务TaskLogtaskLogtaskLogRepository.findById(logId).orElse(null);if(taskLognull){log.warn(任务不存在, logId{},logId);return;}// 2. 幂等判断if(Y.equals(taskLog.getStatus())){return;}try{// 3. 执行业务taskProcessor.execute(taskLog);// 4. 标记成功taskLog.setStatus(Y);taskLog.setErrorMsg(null);taskLogRepository.saveAndFlush(taskLog);// 5. 事务提交后触发后续动作TransactionSynchronizationManager.registerSynchronization(newTransactionSynchronizationAdapter(){OverridepublicvoidafterCommit(){followUpMqSender.send(taskLog.getFollowUpId());}});}catch(Exceptione){log.warn(任务执行失败, logId{},logId,e);// 6. 错误日志独立保存不受本事务回滚影响errorLogService.saveError(logId,e.getMessage());throwe;// 本事务回滚}}}/** * 错误日志服务. * 独立事务保证错误信息不丢失。 */ServicepublicclassErrorLogService{ResourceprivateTaskLogRepositorytaskLogRepository;Transactional(propagationPropagation.REQUIRES_NEW)publicvoidsaveError(LonglogId,StringerrorMsg){TaskLogtaskLogtaskLogRepository.findById(logId).orElse(null);if(taskLognull)return;taskLog.setStatus(P);taskLog.setRetryCount(taskLog.getRetryCount()1);taskLog.setErrorMsg(errorMsg!null?errorMsg.substring(0,Math.min(errorMsg.length(),500)):null);taskLogRepository.saveAndFlush(taskLog);}}九、决策指南什么时候用 REQUIRES_NEW✅ MQ 消费者的核心处理方法✅ 错误/审计日志写入不能随业务事务回滚✅ 序列号/编号生成需要立即提交避免重复✅ 批量操作中每条记录的独立处理✅ 与外部系统交互前的状态锁定提交后才调外部什么时候不该用 REQUIRES_NEW❌ 普通的 Service 层方法调用用默认 REQUIRED❌ 需要与调用方共享事务上下文的操作❌ 深层嵌套调用超过 2 层❌ 可能与外层事务锁相同数据行的操作死锁❌ 纯查询方法用 SUPPORTS 或 readOnlytrue

相关新闻

罗永浩手撕智能电视行业:适老化成营销噱头,复杂背后是商业利益

罗永浩手撕智能电视行业:适老化成营销噱头,复杂背后是商业利益

智能电视“伪适老化”:老人使用难题凸显罗永浩为家中老人更换新款智能电视,即便开启“长辈模式”,老人仍因复杂菜单层级、频繁广告和绕圈操作逻辑而难以使用,最终只能装回老旧IPTV机顶盒,将智能电视用回传统模式。这一…

2026/7/30 20:40:29 阅读更多 →
AutoView高级教程:定制化组件样式与交互逻辑全攻略

AutoView高级教程:定制化组件样式与交互逻辑全攻略

AutoView高级教程:定制化组件样式与交互逻辑全攻略 【免费下载链接】autoview Automatic view component renderer by AI agent 项目地址: https://gitcode.com/gh_mirrors/au/autoview AutoView是一款由AI驱动的自动视图组件渲染工具,能够帮助开…

2026/7/30 20:40:29 阅读更多 →
云应用牺牲速度与隐私,2026 年本地应用缘何成未来趋势?

云应用牺牲速度与隐私,2026 年本地应用缘何成未来趋势?

今日应用菜单及特色服务今日,getapps.cafe 新鲜奉上应用菜单,涵盖 On the House 2、Espresso 11、Latte 9 等多款应用。同时还有特色推荐,包括定价、博客及支持服务等内容。用户可登录账户,享受 7 天免费试用,无需信用…

2026/7/30 20:40:29 阅读更多 →

最新新闻

终极跨平台音频可视化工具CAVA:如何在终端中实现专业级频谱分析

终极跨平台音频可视化工具CAVA:如何在终端中实现专业级频谱分析

终极跨平台音频可视化工具CAVA:如何在终端中实现专业级频谱分析 【免费下载链接】cava Cross-platform Audio Visualizer 项目地址: https://gitcode.com/GitHub_Trending/ca/cava CAVA(Console Audio Visualizer Adapted)是一款功能强…

2026/7/30 20:51:31 阅读更多 →
QQ空间备份神器:一键永久保存你的青春记忆,告别数据丢失焦虑!

QQ空间备份神器:一键永久保存你的青春记忆,告别数据丢失焦虑!

QQ空间备份神器:一键永久保存你的青春记忆,告别数据丢失焦虑! 【免费下载链接】QZoneExport QQ空间导出助手,用于备份QQ空间的说说、日志、私密日记、相册、视频、留言板、QQ好友、收藏夹、分享、最近访客为文件,便于迁…

2026/7/30 20:51:31 阅读更多 →
构建实时漏洞告警系统:从事件驱动架构到多通道通知实践

构建实时漏洞告警系统:从事件驱动架构到多通道通知实践

1. 项目概述:从被动响应到主动防御的转变在安全运营的日常里,我们常常面临一个尴尬的局面:安全扫描器在凌晨三点发现了一个高危漏洞,但告警邮件却静静地躺在某个不常看的收件箱里,直到第二天上午甚至更晚才被处理。这种…

2026/7/30 20:51:31 阅读更多 →
软件测评工程师就业前景如何?拿下高级软件测评工程师证书,抢占2026软件质量薪赛道

软件测评工程师就业前景如何?拿下高级软件测评工程师证书,抢占2026软件质量薪赛道

敏捷开发、DevOps、持续交付"已经成为软件行业的标配,但与之配套的软件质量保障体系却严重滞后。2026年,随着《系统与软件工程性能测试方法》《系统与软件工程系统与软件质量要求和评价》等国家标准的深入贯彻,企事业单位对"持证上…

2026/7/30 20:51:31 阅读更多 →
3分钟快速汉化Android Studio:社区中文语言包让你的开发效率翻倍

3分钟快速汉化Android Studio:社区中文语言包让你的开发效率翻倍

3分钟快速汉化Android Studio:社区中文语言包让你的开发效率翻倍 【免费下载链接】AndroidStudioChineseLanguagePack AndroidStudio中文插件(官方修改版本) 项目地址: https://gitcode.com/gh_mirrors/an/AndroidStudioChineseLanguagePack 你是…

2026/7/30 20:51:31 阅读更多 →
GAN评估指标失效真相,FID/IS/LPIPS到底在骗谁?——基于ICML 2024最新基准测试的权威重测报告

GAN评估指标失效真相,FID/IS/LPIPS到底在骗谁?——基于ICML 2024最新基准测试的权威重测报告

更多请点击: https://kaifayun.com 第一章:GAN评估指标失效真相,FID/IS/LPIPS到底在骗谁?——基于ICML 2024最新基准测试的权威重测报告 三大指标集体失准:当分数漂亮,视觉灾难却在发生 ICML 2024 Spotli…

2026/7/30 20:50:31 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击: https://intelliparadigm.com 第一章:AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具,它并非替代教师,而是通过语义理解、知识图谱与多模态生成能力,将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻