Spring Boot3.2+JUnit5+JPA测试中数据保存后无法查询问题
问题排查与解决方案
1. 测试类默认事务回滚行为
Spring Boot JUnit测试默认会给测试方法自动添加@Transactional注解,且测试结束后强制回滚事务——哪怕你在内部方法执行了saveAndFlush、甚至手动提交事务,测试上下文的顶层事务最终仍会回滚,导致数据库中无持久化数据。
- 解决方案:
- 在测试类/测试方法上添加
@Commit注解,让测试结束后提交事务; - 或添加
@Rollback(false),禁用默认回滚逻辑; - 若不需要测试方法的事务管理,直接移除测试类上的
@Transactional注解。
- 在测试类/测试方法上添加
2. 同类内部调用导致REQUIRES_NEW失效
Spring事务基于动态代理实现,若readyDocument和doLog是同一类内部的方法调用,doLog上的@Transactional(TxType.REQUIRES_NEW)不会触发新事务——内部调用不经过代理对象,事务切面无法拦截。
- 解决方案:
- 将
doLog抽离到独立的Spring管理Bean中,通过依赖注入调用,确保走代理逻辑; - 或通过
AopContext.currentProxy()获取当前类的代理对象调用doLog(需在启动类添加@EnableAspectJAutoProxy(exposeProxy = true)):
- 将
// 同一类内调用示例 ((YourService) AopContext.currentProxy()).doLog();
3. JPA一级缓存与事务同步问题
若doLog和readyDocument处于同一事务(REQUIRES_NEW未生效时),findAll可能从EntityManager一级缓存读取数据,若缓存未同步实体状态,会出现查不到的情况;若事务已提交,更可能是测试回滚导致的。
- 解决方案:
saveAndFlush后调用entityManager.clear()清空一级缓存(需在当前事务中查询最新数据时);- 确保
REQUIRES_NEW生效,让doLog在独立新事务执行,此时会重新获取EntityManager,能读取到已提交的数据。
4. 实体持久化有效性验证
检查saveAndFlush的返回实体,确认主键是否生成、必填字段是否完整——若实体不符合持久化规则(比如非空字段为空),JPA可能静默处理(取决于配置),导致数据未真正插入数据库。
- 解决方案:
- 打印
saveAndFlush返回的实体对象,校验主键与字段值; - 开启JPA SQL日志,查看是否有对应INSERT语句执行(
application-test.yml配置):
- 打印
spring: jpa: show-sql: true properties: hibernate: format_sql: true
5. 数据库事务隔离级别排查
若数据库使用默认的REPEATABLE READ隔离级别(如MySQL),同一事务内多次查询会读取相同数据快照,即使其他事务已提交;但REQUIRES_NEW的新事务应能读到提交数据,此场景概率较低但可排查。
- 解决方案:
- 临时将数据库隔离级别改为
READ COMMITTED测试; - 查看日志确认
doLog的事务ID是否与readyDocument不同,验证新事务是否开启。
- 临时将数据库隔离级别改为
内容的提问来源于stack exchange,提问作者dankim
相关产品推荐
相关产品推荐

