升级Hibernate 6.6后@OneToOne+@MapsId触发ObjectOptimisticLockingFailureException
升级Hibernate 5.4至6.6后,双向@OneToOne+@MapsId关联实体保存异常
背景说明
我把项目从Hibernate 5.4升级到6.6版本后,遇到了之前从未出现过的异常。系统中有两个共享同一ID的实体:ProductEntity和ProductProfitabilityEntity,它们的ID由其他微服务分配,实体上未添加@GeneratedValue注解。
这两个实体为双向@OneToOne关联,ProductProfitabilityEntity通过@MapsId注解保证自身ID与ProductEntity一致:
ProductEntity包含指向ProductProfitabilityEntity的profitability字段ProductProfitabilityEntity包含指向ProductEntity的product字段
实体代码
ProductEntity
package br.com.triersistemas.cloud.platform.desktop.entity.product; import br.com.triersistemas.cloud.database.entity.AuditingExternalEntity; import br.com.triersistemas.cloud.platform.desktop.entity.productprofitability.ProductProfitabilityEntity; import jakarta.persistence.*; import jakarta.validation.Valid; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; import java.util.UUID; @AllArgsConstructor @NoArgsConstructor @SuperBuilder @Entity @Table(name = "product") public class ProductEntity extends AuditingExternalEntity { @Getter @Setter @Id @Column(name = "id") private UUID id; @Getter @Setter @Column(name = "product_code") private Long productCode; // 关联ProductProfitabilityEntity @Valid @OneToOne(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) private ProductProfitabilityEntity profitability; }
ProductProfitabilityEntity
package br.com.triersistemas.cloud.platform.desktop.entity.productprofitability; import br.com.triersistemas.cloud.database.entity.AuditingEntity; import br.com.triersistemas.cloud.platform.desktop.entity.product.ProductEntity; import jakarta.persistence.*; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; import java.util.UUID; @AllArgsConstructor @NoArgsConstructor @SuperBuilder @Entity @Table(name = "product_profitability") public class ProductProfitabilityEntity extends AuditingEntity { @NotNull(message = "Unique identifier is required.") @Getter @Setter @Id @Column(name = "id") private UUID id; // 关联ProductEntity,通过@MapsId使当前实体ID与ProductEntity一致 @Valid @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "id") @MapsId private ProductEntity product; }
问题现象
保存ProductEntity时触发以下异常:
org.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [br.com.triersistemas.cloud.platform.desktop.entity.productprofitabilityentity.ProductProfitabilityEntity#fb01b3e0-f12e-4380-b82a-2f2882fadaf1]
我的分析
我认为Hibernate检测到ProductProfitabilityEntity已有ID值,会去数据库查询该实体是否存在,以此判断执行更新还是插入操作。但实际上这个实体尚未存在,因此抛出异常。我原本预期,当实体没有@GeneratedValue注解且数据库中无对应记录时,Hibernate会直接插入指定ID的记录。
手动将ProductProfitabilityEntity的id设为null可以正常保存,但由于API接收的是其他微服务复制过来的完整数据,这种方案并不适用。
求助问题
- Hibernate 6.6中的哪些变更导致了这个行为差异?
- 有没有比手动设置id为null更优的解决方案?
内容的提问来源于stack exchange,提问作者Nikolas Trapp
相关产品推荐
相关产品推荐

