如何测试Spring JPA审计注解?两个测试无法同时通过该如何解决
Spring JPA审计测试不同注解互斥问题解答
失败原因分析
1. @DataJpaTest 下第二个测试失败的原因
- @DataJpaTest 是Spring Boot提供的JPA切片测试注解,默认会为每个测试方法开启独立的事务,测试结束后自动回滚,不会真的提交事务。
- JPA的
@LastModifiedDate审计字段的更新触发时机是**实体执行update操作同步到数据库(即flush)**时,第二个测试中两次save操作都在同一个测试事务内,没有触发flush操作,所以审计字段不会更新,测试失败。 - 额外补充:@DataJpaTest 默认只加载JPA相关的上下文切片,如果你主类的
@EnableJpaAuditing没有被扫描到,也可能导致审计逻辑异常,第一个测试能过是因为@CreatedDate是在实体持久化(persist)阶段触发,只要实体存入持久化上下文就会赋值。
2. @SpringBootTest 下第一个测试失败的原因
- 你定义的是双向一对多关联,
mappedBy = "parent"说明关联关系的维护方是子实体的parent字段,父实体的children只是关联的反向映射,不负责维护外键关系。 - 第一个测试中你只给父实体的
children属性塞了子实例,没有主动给子实例的parent属性赋值,级联保存子实体时,子实例的parent为null,对应数据库的parent_id字段违反非空约束,所以抛出ConstraintViolationException。 - @DataJpaTest 下第一个测试能通过是因为切片测试默认会关闭H2的外键约束校验,所以不会触发非空报错,但这个配置是测试专属的,不符合实际运行逻辑。
解决方案(无需拆分测试类,可同时通过)
不需要拆分到不同测试类,只要做两处调整即可:
调整1:修复双向关联的赋值逻辑
在构造测试数据时,主动维护双向关联,给子实体的parent属性赋值,修改第一个测试的given块:
given: def parent = new OneToManyEntity() def child = new ManyToOneEntity() child.setParent(parent) // 主动设置子实体的父关联 parent.setChildren([child])
你也可以在OneToManyEntity实体中新增工具方法统一维护关联,避免每次手动赋值:
public void addChild(ManyToOneEntity child) { children.add(child); child.setParent(this); }
调整2:统一使用@SpringBootTest注解
直接保留@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)注解即可,修复关联赋值后,第一个测试的非空报错会消失,第二个测试本身就能正常通过。
如果想改用@DataJpaTest的话,额外做两个调整即可:
- 在测试类上新增
@EnableJpaAuditing或者导入主类的配置,保证审计逻辑生效; - 第二个测试中把
repo.save(persisted1)改成repo.saveAndFlush(persisted1),或者注入EntityManager手动调用flush()方法触发更新审计。
内容的提问来源于stack exchange,提问作者jaco0646
相关产品推荐
相关产品推荐

