Java Spring:子类ManyToOne关联EmbeddedId父类加附加Id保存报null错误
复合主键关联子实体保存空字段问题修复方案
核心原因
你的子实体使用了双@Id声明派生复合主键,但未显式定义主键结构,@MapsId未明确指定映射的主键片段,导致JPA级联保存时仅识别父实体映射的主键部分,直接忽略了已赋值的toyInventory字段,最终抛出字段非空约束报错。
你收到的报错中文翻译为:
SQL完整性约束违反异常:
toy_inventory字段不能为空
修复步骤
- 新建子实体对应的嵌入式复合主键类,明确主键组成结构,必须实现
equals和hashCode方法
@Embeddable public class AnnualServiceHistoryNonMachinePK implements Serializable { // 对应父实体的复合主键 private AnnualServiceHistoryPK annualServiceHistoryPK; // 对应ToyInventory的主键字段 private Long toyInventoryId; // 必须重写equals和hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AnnualServiceHistoryNonMachinePK that = (AnnualServiceHistoryNonMachinePK) o; return Objects.equals(annualServiceHistoryPK, that.annualServiceHistoryPK) && Objects.equals(toyInventoryId, that.toyInventoryId); } @Override public int hashCode() { return Objects.hash(annualServiceHistoryPK, toyInventoryId); } // getter、setter省略 }
- 修改子实体的映射配置,明确
@MapsId对应的主键片段
@Entity public class AnnualServiceHistoryNonMachine extends Auditable<String> implements Serializable { // 显式声明嵌入式复合主键 @EmbeddedId private AnnualServiceHistoryNonMachinePK id = new AnnualServiceHistoryNonMachinePK(); // 映射父实体关联,对应主键中的annualServiceHistoryPK部分 @MapsId("annualServiceHistoryPK") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "year", referencedColumnName = "year") @JoinColumn(name = "month", referencedColumnName = "month") @JoinColumn(name = "route", referencedColumnName = "route") private AnnualServiceHistory annualServiceHistory; // 映射ToyInventory关联,对应主键中的toyInventoryId部分 @MapsId("toyInventoryId") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "toy_inventory", referencedColumnName = "id") private ToyInventory toyInventory; // 其余属性、getter、setter省略 }
- 保存父实体前补全双向关联
调用save方法前,遍历父实体的子实体列表,给每个子实体手动设置annualServiceHistory属性指向当前父实体,确保关联关系双向绑定,避免JPA无法识别关联。
验证
修改完成后重新执行保存操作,JPA会自动从两个关联字段中获取值填充复合主键,不会再出现toy_inventory字段为空的报错。
内容的提问来源于stack exchange,提问作者Brian Leierzapf
相关产品推荐
相关产品推荐

