Spring Boot 3.4.0升级后JPA/Hibernate集成测试失败求助
将Spring Boot从3.3.6升级至3.4.0后,涉及Hibernate/JPA交互(基于spring-data-jpa)的@SpringBootTest集成测试突然失败。
测试代码片段
@Test void retrieveAllFahrzeugbewegungenByMuster() throws Exception { // Given var musterEntity = MusterMetadatenGenerator.createDefaultMuster(); var musterEntitySaved = musterMetadatenRepository.saveAndFlush(musterEntity); // 此处报错 ... }
实体创建核心逻辑
static <E extends BaseEntity> E createDefaultValues( @NotNull final E source, @NotNull final LocalDate datumAb, @NotNull final LocalDate datumBis ) { final Instant now = Instant.now(); source.setId(UUID.randomUUID()); source.setDatumAb(datumAb); source.setDatumBis(datumBis); source.setBearbeitetAm(now); source.setErstelltAm(now); return source; }
父类实体定义
@Data @NoArgsConstructor @AllArgsConstructor @MappedSuperclass public class BaseEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(updatable = false, nullable = false) protected UUID id; ... }
抛出的异常信息
org.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [de.muster.jpa.entity.MusterMetadaten#40add996-a1f9-43f7-a157-cf5692e5ea65]
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [de.muster.jpa.entity.MusterMetadaten#40add996-a1f9-43f7-a157-cf5692e5ea65]
Spring Boot 3.4.0对应Hibernate 6.6.x,之前版本为Hibernate 6.5.x,询问是否存在破坏性变更导致该问题。
这是Hibernate 6.6.x的破坏性变更导致的,核心问题出在UUID主键配合GenerationType.AUTO的状态校验逻辑上:
在Hibernate 6.5及更早版本中,手动设置UUID主键值后,Hibernate会默认执行INSERT操作(因为数据库中无对应ID记录);但Hibernate 6.6.x收紧了规则:当手动设置了@GeneratedValue标注的主键值时,Hibernate会判定该实体为** detached 状态**(认为它已持久化但当前不在Session中),执行saveAndFlush时会尝试UPDATE操作,而数据库中没有对应记录,最终触发StaleObjectStateException。
两种修复方案
- 修改主键生成策略:将
GenerationType.AUTO改为GenerationType.UUID,明确指定UUID生成逻辑,让Hibernate正确识别手动设置的ID:
@Id @GeneratedValue(strategy = GenerationType.UUID) @Column(updatable = false, nullable = false) protected UUID id;
- 移除手动设置ID的逻辑:让Hibernate自动生成UUID主键,
saveAndFlush会正常执行INSERT操作:
// 移除测试代码中手动设置ID的行 // source.setId(UUID.randomUUID());
补充说明
Hibernate 6.6.x的这次变更属于对JPA规范的严格遵循,之前版本对"手动设置自动生成主键"的场景存在逻辑宽松,新版本收紧规则后,原有不符合规范的代码触发了异常。
内容的提问来源于stack exchange,提问作者kladderradatsch

