执行@BeforeEach#repository.save()时测试中UUID终值变更问题
问题:保存JPA实体后预分配UUID被覆盖,断言失败
问题详情
编写OrderRepositoryTest测试类时,期望通过预定义的UUID获取有效订单:
- 定义了静态final的UUID
id2,并以此构建Order对象 - 执行
@BeforeEach中的orderRepository.save(order2)后,断言id2与order2.getUuid()出现Expected [correct_uuid] but was [wrong_uuid]错误 - 注释
save和delete操作后测试可通过 Order实体的UUID字段标注了@Id和@GeneratedValue(strategy = GenerationType.UUID),尝试直接初始化id2为UUID.randomUUID()同样失败- 希望使用最初分配的UUID完成断言,而非保存后生成的新UUID
相关代码
OrderRepositoryTest.java
@DataJpaTest @ExtendWith(SpringExtension.class) @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2) public class OrderRepositoryTest { private static final Logger logger = LoggerFactory.getLogger(OrderRepositoryTest.class); @Autowired OrderRepository orderRepository; private static final UUID id2; Order order2 = setOrder2(); static { id2 = UUID.randomUUID(); } @BeforeEach public void setUp() { orderRepository.save(order2); } @AfterEach public void tearDown() { orderRepository.deleteAll(); } @Test public void OrderRepositoryTest_getValidOrder() { Assertions.assertThat(id2).isEqualTo(order2.getUuid()); // TODO unit test get valid order using orderRepository.findOrderByUuid() } public Order setOrder2() { return Order .builder() .uuid(id2) .build(); } }
Order.java
@Getter @Setter @Builder @ToString @Jacksonized @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "orders") public class Order { @Valid @Id @GeneratedValue(strategy = GenerationType.UUID) UUID uuid; // TODO other columns }
OrderRepository.java
public interface OrderRepository extends JpaRepository<Order, UUID> { Optional<Order> findOrderByUuid(UUID uuid); }
原因分析
@GeneratedValue(strategy = GenerationType.UUID)注解会让JPA(如Hibernate)在保存实体时强制生成新的UUID并覆盖手动设置的值,无论你是否提前为UUID字段赋值,JPA都会触发生成策略替换原有ID,这就是断言失败的根本原因。
解决方案
方案1:移除自动生成策略(推荐,若业务需手动控制UUID)
如果不需要JPA自动生成UUID,直接移除@GeneratedValue注解,这样JPA会使用你手动设置的UUID作为主键:
@Id UUID uuid;
方案2:配置生成策略仅在ID为null时生效
保留自动生成能力,但让JPA仅在UUID为null时才生成新值。以Hibernate为例,修改实体的UUID字段注解:
@Id @GeneratedValue(generator = "uuid2") @GenericGenerator(name = "uuid2", strategy = "uuid2") UUID uuid;
此时,若你手动设置了UUID,JPA会直接使用该值;若未设置,则自动生成UUID。
方案3:调整测试逻辑(兼容自动生成策略)
如果必须保留@GeneratedValue(strategy = GenerationType.UUID),可以将save方法的返回值重新赋值给order2,因为save返回的是持久化后的实体(包含最终生成的UUID):
@BeforeEach public void setUp() { order2 = orderRepository.save(order2); }
注意:此方案会使用JPA生成的UUID进行断言,而非你最初分配的id2,仅适用于不需要严格匹配预分配UUID的场景。
内容的提问来源于stack exchange,提问作者Mr. Kenneth
相关产品推荐
相关产品推荐

