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

Oracle中JPA保存一对多关联实体报Multiple representations of the same entity错误如何解决?

问题根因分析
  • 核心问题1:RecipeItem 实体的联合主键配置错误
    业务中明确 TB_RECIPE_ITEM 主键由 ID_RECIPE、CD_LOT、CD_PRODUCT 三个字段构成,但现有代码仅将 ID_RECIPE 标注为 @Id,JPA 会将 ID_RECIPE 作为唯一主键判断依据。当同一个配方下有多个子项时,所有子项的 ID_RECIPE 值相同,JPA 会认为你在同一个持久化上下文中存入了多个主键完全一致的实体,直接触发 Multiple representations of the same entity 异常。单子项场景因为不存在重复主键值所以运行正常。
  • 核心问题2:一对多关联映射配置错误
    现有 Recipe 中 @OneToMany 的 mappedBy = "id" 配置指向的是 RecipeItem 中的普通主键字段,没有建立实体层面的外键关联关系,级联操作的逻辑完全走不通,所以你调整级联配置不会有任何效果。
  • 附加问题:Lombok @Data 注解适配问题
    @Data 自动生成的 equals、hashCode 方法会包含所有实体字段,包含关联属性后会干扰JPA对实体相等性的判断,容易引发持久化上下文异常。
修复方案

步骤1:定义联合主键类

首先创建 RecipeItemId 作为联合主键载体,需实现 Serializable 接口,并重写 equals 和 hashCode 方法:

import lombok.Data;
import java.io.Serializable;

@Data
public class RecipeItemId implements Serializable {
    // 字段名要和RecipeItem中@Id标注的字段名完全对应
    private Long id;
    private String lot;
    private Long code;
}

步骤2:修正RecipeItem实体映射

调整 RecipeItem 配置,声明联合主键,并且建立和 Recipe 的关联关系:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@IdClass(RecipeItemId.class) // 绑定联合主键类
@Table(name = "TB_RECIPE_ITEM", schema = "A_SAMPLE")
public class RecipeItem {
    
    @Id
    @Column(name = "ID_RECIPE", insertable = false, updatable = false)
    private Long id;
    
    @Id
    @Column(name = "CD_LOT")
    private String lot;
    
    @Id
    @Column(name = "CD_PRODUCT")
    private Long code;
    
    // 建立和Recipe的关联,使用@MapsId绑定联合主键中的id字段
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ID_RECIPE")
    @MapsId("id")
    private Recipe recipe;
    
    @Column(name = "QT_PURCHASE")
    private Long purchaseQuantity;

    @Column(name = "FL_AVAILABLE")
    private Boolean available;
}

步骤3:修正Recipe实体的关联配置

调整 @OneToMany 的 mappedBy 指向正确的关联属性:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "TB_RECIPE_DATA", schema = "A_SAMPLE")
public class Recipe {

    @Id
    @Column(name = "ID_RECIPE")
    private Long id;

    @Fetch(FetchMode.SUBSELECT)
    // mappedBy指向RecipeItem中关联Recipe的属性名recipe
    @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    private List<RecipeItem> items;
}

步骤4:业务代码适配

保存配方时需给每个 RecipeItem 设置关联的 Recipe 实例,示例逻辑:

Recipe recipe = new Recipe();
recipe.setId(1001L);
// 构造子项
List<RecipeItem> items = new ArrayList<>();
RecipeItem item1 = new RecipeItem();
item1.setLot("L001");
item1.setCode(2001L);
item1.setRecipe(recipe); // 必须设置关联
items.add(item1);

RecipeItem item2 = new RecipeItem();
item2.setLot("L002");
item2.setCode(2002L);
item2.setRecipe(recipe); // 必须设置关联
items.add(item2);

recipe.setItems(items);
// 直接保存Recipe即可级联保存所有子项
recipeRepository.save(recipe);

可选优化

建议移除 @Data 注解,手动重写 equals 和 hashCode 方法,仅基于主键字段生成,避免关联字段带来的相等性判断异常。

内容的提问来源于stack exchange,提问作者Liam Park

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 09:30:03