@Transactional + @Rollback 是 Spring Boot 集成测试默认首选,因测试上下文自动启用事务管理器,执行后自动回滚,无需手动清理;@Rollback 非必需,默认即回滚,仅用于显式覆盖为 false。

@Transactional 是 Spring Boot 集成测试中实现数据回滚最轻量、最可靠的方式,但必须用对——错配 propagation 或漏掉 @Rollback 会导致测试间数据污染,且错误表现极其隐蔽(比如第3个测试突然查到第1个测试写入的脏数据)。
为什么 @Transactional + @Rollback 是默认首选
Spring Boot 的测试上下文在 @SpringBootTest 下默认启用事务管理器,只要测试类或方法被 @Transactional 标记,整个测试执行就会包裹在一个事务中;测试结束时,框架自动触发 rollback,无需手动清理 SQL 或重置数据库。
常见误区:
- 误以为必须显式写
@Rollback(true)—— 实际上@Transactional默认就是回滚,加@Rollback只是为了覆盖为 false(如某些需验证提交行为的特殊用例) - 在非
@SpringBootTest或@DataJpaTest环境下盲目加@Transactional—— 若事务管理器未加载(如纯@ExtendWith(MockitoExtension.class)),该注解完全无效 - 混用
@Transactional(propagation = Propagation.REQUIRES_NEW)—— 这会开启新事务,导致外层事务无法控制其回滚,是回滚失效的头号原因
@DataJpaTest 自带事务回滚,但仅限于 JPA 层
@DataJpaTest 会自动配置内存数据库(如 H2)、加载 JpaRepository 相关 Bean,并**隐式启用事务边界**。它等价于同时使用 @ImportAutoConfiguration(JpaTestTools.class) 和 @Transactional。
适用场景:
- 只测 Repository 方法(
save()、findById()、自定义 JPQL 查询等) - 不需要 Controller、Service 完整链路,追求启动快、隔离强
- 想避免手写
@Transactional但又需要数据干净
注意点:
- 它不加载
@Service或@Controller,所以不能用于验证 service 逻辑中的事务传播行为 - 若测试中调用了非 JPA 的外部组件(如 RedisTemplate),这些操作不会被回滚
- 默认使用 H2,如需换 PostgreSQL/MySQL 测试,得配合
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)并配好真实连接
@SpringBootTest + @AutoConfigureTestDatabase 的组合陷阱
当集成测试涉及 Web 层、Service 层和数据库三者联动时,通常用 @SpringBootTest。此时若想确保数据回滚,必须确认两点:
- 测试类或方法上有
@Transactional - 数据库配置没绕过事务管理器 —— 尤其警惕
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)配合真实数据库时,若未启用事务,数据将永久落库
典型错误配置:
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional // ✅ 必须有
class UserServiceIntegrationTest { ... }
如果去掉 @Transactional,哪怕只执行一条 userRepository.save(...),数据就真进生产级数据库了(哪怕只是本地 PostgreSQL 实例)。
更安全的做法是:除非明确要测“提交后效果”,否则始终搭配 @Transactional,并避免 Replace.NONE 在 CI 环境中使用。
回滚失败的三个高发信号
一旦出现以下现象,基本可判定事务回滚机制已失效:
- 连续运行两次相同测试,第二次断言失败(比如
count()结果比预期多 1) - 测试类中两个
@Test方法共享了数据库状态(A 方法插入、B 方法能查到) - 日志里出现
Transaction rolled back because it has been marked as rollback-only—— 往往是嵌套事务或异常吞没导致
排查顺序建议:先检查是否漏了 @Transactional;再确认有没有 Propagation.REQUIRES_NEW 或 @Async 导致事务脱离上下文;最后看是否在测试中手动调用了 EntityManager.flush() 或 JdbcTemplate.update() 绕过了事务代理。

















