news 2026/4/30 19:31:01

Spring Boot 3.x项目里,@Transactional注解的7种传播行为到底怎么用?附代码实测结果

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot 3.x项目里,@Transactional注解的7种传播行为到底怎么用?附代码实测结果

Spring Boot 3.x事务传播行为实战指南:从理论到代码验证

在分布式系统和高并发场景下,事务管理是保证数据一致性的关键。Spring Boot 3.x对事务传播行为的支持更加成熟,但很多开发者对这些抽象概念的理解仍停留在理论层面。本文将带你搭建测试环境,通过JUnit 5单元测试和真实数据库操作,直观展示7种传播行为在不同调用链路下的表现差异。

1. 测试环境搭建与基础配置

首先创建一个Spring Boot 3.x项目,添加必要的依赖:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

创建简单的银行账户实体和Repository:

@Entity public class Account { @Id private String accountNumber; private BigDecimal balance; // getters and setters } public interface AccountRepository extends JpaRepository<Account, String> { }

2. REQUIRED传播行为深度解析

作为默认传播行为,REQUIRED的特点是"有则加入,无则新建"。我们通过以下测试案例验证其特性:

@Test @DisplayName("REQUIRED传播行为-外部无事务时新建事务") void testRequiredWithoutExistingTransaction() { // 初始数据准备 accountRepository.save(new Account("A123", BigDecimal.valueOf(1000))); accountRepository.save(new Account("B456", BigDecimal.ZERO)); // 测试方法调用 transactionService.transferRequired("A123", "B456", BigDecimal.valueOf(500)); // 验证结果 assertThat(accountRepository.findById("A123").get().getBalance()) .isEqualByComparingTo("500"); assertThat(accountRepository.findById("B456").get().getBalance()) .isEqualByComparingTo("500"); } @Test @DisplayName("REQUIRED传播行为-异常导致整体回滚") void testRequiredRollback() { // 初始数据准备 accountRepository.save(new Account("A123", BigDecimal.valueOf(1000))); accountRepository.save(new Account("B456", BigDecimal.ZERO)); // 模拟异常场景 assertThatThrownBy(() -> transactionService.transferRequiredWithException("A123", "B456", BigDecimal.valueOf(500))) .isInstanceOf(RuntimeException.class); // 验证数据未改变 assertThat(accountRepository.findById("A123").get().getBalance()) .isEqualByComparingTo("1000"); assertThat(accountRepository.findById("B456").get().getBalance()) .isEqualByComparingTo("0"); }

关键发现:

  • 当外部方法没有事务时,REQUIRED会新建事务
  • 嵌套的REQUIRED方法会加入外部事务形成单一事务边界
  • 任何位置的异常都会导致整个事务回滚

3. REQUIRES_NEW的隔离特性实测

REQUIRES_NEW常被误解为"简单新建事务",其实它的核心特性是事务隔离:

@Test @DisplayName("REQUIRES_NEW传播行为-独立事务提交") void testRequiresNewCommit() { accountRepository.save(new Account("A123", BigDecimal.valueOf(1000))); accountRepository.save(new Account("B456", BigDecimal.ZERO)); // 外部事务失败不影响内部REQUIRES_NEW事务 assertThatThrownBy(() -> outerService.outerMethodWithRequiresNew()) .isInstanceOf(RuntimeException.class); // 验证内部事务已提交 assertThat(accountRepository.findById("B456").get().getBalance()) .isEqualByComparingTo("500"); } // 日志输出分析 // [main] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.example.OuterService.outerMethodWithRequiresNew]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // [main] o.s.orm.jpa.JpaTransactionManager : Suspending current transaction, creating new transaction with name [com.example.InnerService.innerMethod] // [main] o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit // [main] o.s.orm.jpa.JpaTransactionManager : Resuming suspended transaction after completion of inner transaction // [main] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback

实测发现三个关键点:

  1. 外部事务挂起时,数据库连接会被释放(通过日志可见)
  2. 内部事务提交后,结果立即对其他事务可见
  3. 外部事务回滚不影响已提交的内部事务

4. NESTED与REQUIRES_NEW的微妙差异

NESTED传播行为创建的是真正的嵌套事务,与REQUIRES_NEW有本质区别:

特性NESTEDREQUIRES_NEW
事务独立性依赖外部事务完全独立
回滚影响只回滚自己不影响外部
保存点使用
数据库要求需要JDBC 3.0+支持通用支持

验证代码:

@Test @DisplayName("NESTED传播行为-外部回滚影响嵌套事务") void testNestedWithOuterRollback() { accountRepository.save(new Account("A123", BigDecimal.valueOf(1000))); accountRepository.save(new Account("B456", BigDecimal.ZERO)); assertThatThrownBy(() -> outerService.outerMethodWithNested()) .isInstanceOf(RuntimeException.class); // 外部回滚导致嵌套事务也回滚 assertThat(accountRepository.findById("B456").get().getBalance()) .isEqualByComparingTo("0"); }

5. 非事务型传播行为实战对比

SUPPORTS、NOT_SUPPORTED和NEVER都涉及非事务执行,但各有特点:

  • SUPPORTS:"随遇而安"模式

    @Test void testSupportsBehavior() { // 有事务时加入事务 transactionWithSupports(); // 无事务时非事务执行 nonTransactionalMethodCallingSupports(); }
  • NOT_SUPPORTED:"强制非事务"模式

    @Test @DisplayName("NOT_SUPPORTED挂起现有事务") void testNotSupported() { assertThatNoException() .isThrownBy(() -> transactionalMethodCallingNotSupported()); // 验证操作已执行(无回滚) assertThat(accountRepository.count()).isEqualTo(2); }
  • NEVER:"禁止事务"模式

    @Test @DisplayName("NEVER在有事务时抛出异常") void testNeverWithTransaction() { assertThatThrownBy(() -> transactionalMethodCallingNever()) .isInstanceOf(IllegalTransactionStateException.class); }

6. 传播行为选型决策树

根据业务场景选择传播行为的实用指南:

  1. 需要事务保障时

    • 默认选择:REQUIRED
    • 特殊需求:REQUIRES_NEW(审计日志)、NESTED(部分回滚)
  2. 优化性能场景

    • 只读操作:SUPPORTS
    • 非关键操作:NOT_SUPPORTED
  3. 强制约束场景

    • 必须无事务:NEVER
    • 必须有事务:MANDATORY

实际项目中,REQUIRED满足80%的场景需求,REQUIRES_NEW约占15%,其他传播行为合计不到5%

7. 生产环境中的陷阱与解决方案

陷阱1:REQUIRES_NEW导致死锁

// 错误示例 @Transactional(propagation = Propagation.REQUIRED) public void batchProcess(List<String> accountNumbers) { accountNumbers.forEach(num -> { individualProcess(num); // 内部REQUIRES_NEW }); } // 解决方案:调整事务边界或添加重试机制 @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100)) public void safeBatchProcess(List<String> accountNumbers) { // 实现略 }

陷阱2:NESTED不支持的场景

// 在JPA环境下可能不工作 @Transactional(propagation = Propagation.NESTED) public void nestedOperation() { // ... } // 解决方案:改用REQUIRES_NEW或调整架构 @Transactional(propagation = Propagation.REQUIRES_NEW) public void alternativeNestedOperation() { // ... }

陷阱3:异步方法中的传播行为失效

// 异步方法的事务传播可能不生效 @Async @Transactional(propagation = Propagation.REQUIRES_NEW) public void asyncMethod() { // ... } // 解决方案:使用编程式事务管理 @Async public void properAsyncMethod() { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); transactionTemplate.execute(status -> { // 业务逻辑 return null; }); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 19:30:44

从零开始:如何为Galgame爱好者打造专属的交流家园

从零开始&#xff1a;如何为Galgame爱好者打造专属的交流家园 【免费下载链接】kun-touchgal-next TouchGAL是立足于分享快乐的一站式Galgame文化社区, 为Gal爱好者提供一片净土! 项目地址: https://gitcode.com/gh_mirrors/ku/kun-touchgal-next 想象一下这样的场景&am…

作者头像 李华
网站建设 2026/4/30 19:22:30

SenseVoice-small镜像部署:无需conda环境,Docker一键拉起WebUI

SenseVoice-small镜像部署&#xff1a;无需conda环境&#xff0c;Docker一键拉起WebUI 1. 引言 如果你正在寻找一个开箱即用、部署简单、功能强大的离线语音识别方案&#xff0c;那么你来对地方了。今天要介绍的 SenseVoice-small 镜像&#xff0c;完美解决了传统语音模型部署…

作者头像 李华
网站建设 2026/4/30 19:21:33

GmSSL国密算法库的完整实现与生产部署方案

GmSSL国密算法库的完整实现与生产部署方案 【免费下载链接】GmSSL 支持国密SM2/SM3/SM4/SM9/SSL的密码工具箱 项目地址: https://gitcode.com/gh_mirrors/gm/GmSSL 在当前网络安全合规要求日益严格的环境下&#xff0c;国密算法已成为国内信息系统安全建设的核心需求。G…

作者头像 李华
网站建设 2026/4/30 19:20:59

Blender终极文件操作革命:Super IO插件完全指南

Blender终极文件操作革命&#xff1a;Super IO插件完全指南 【免费下载链接】super_io blender addon for copy paste import / export 项目地址: https://gitcode.com/gh_mirrors/su/super_io 还在为Blender中繁琐的文件导入导出流程而头疼吗&#xff1f;每次需要导入模…

作者头像 李华