You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级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接收的是其他微服务复制过来的完整数据,这种方案并不适用。

求助问题

  1. Hibernate 6.6中的哪些变更导致了这个行为差异?
  2. 有没有比手动设置id为null更优的解决方案?

内容的提问来源于stack exchange,提问作者Nikolas Trapp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:25:54