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
相关产品推荐
相关产品推荐

