JPA @JoinColumn多对多关联报错:添加weekday列引发映射异常
问题原因与解决方案
错误根源
你遇到的AnnotationException是因为多对多关联的joinColumns只能引用关联实体的主键字段,但你把Plans实体的普通字段weekday加到了关联外键配置里。
Plans的主键是嵌入式的PlansPKId,仅包含planDate和planId两个字段;weekday是实体的普通列,不属于主键范畴,不能作为多对多关联的外键组成部分。Hibernate无法将非主键字段识别为关联的映射属性,因此抛出错误。
两种解决思路
思路1:移除非主键字段的关联配置(如果weekday不需要作为关联条件)
如果weekday只是Plans的普通属性,不需要作为关联Meds的条件,直接从@JoinTable的joinColumns中删除weekday对应的配置即可,回到之前正常的代码:
@ManyToMany @JoinTable(name = "Plan_Meds", joinColumns = { @JoinColumn(name = "planDate", referencedColumnName = "planDate"), @JoinColumn(name = "planId", referencedColumnName = "planId") }, inverseJoinColumns = @JoinColumn(name = "id")) private Set<Meds> assignedMeds = new HashSet<>();
思路2:拆分中间实体(如果weekday必须作为关联条件)
如果weekday需要作为关联Plans和Meds的一部分,说明这不是单纯的多对多关联,而是带有额外字段的关联关系。此时需要拆分出中间实体,用@OneToMany + @ManyToOne替代@ManyToMany:
1. 创建中间实体的复合主键类
@Data @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode @Embeddable public class PlanMedPK implements Serializable { private Date planDate; private long planId; private String weekday; private long medId; }
2. 创建中间实体PlanMed
@Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "Plan_Meds") public class PlanMed { @EmbeddedId private PlanMedPK id; @ManyToOne @MapsId("planDate") @MapsId("planId") @JoinColumns({ @JoinColumn(name = "planDate", referencedColumnName = "planDate"), @JoinColumn(name = "planId", referencedColumnName = "planId") }) private Plans plan; @ManyToOne @MapsId("medId") @JoinColumn(name = "id", referencedColumnName = "id") private Meds med; }
3. 修改Plans实体
@Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "plans") @EqualsAndHashCode(exclude = "planMeds") public class Plans { @EmbeddedId private PlansPKId plansPKId; @Column private String planName; @Column private String weekday; @OneToMany(mappedBy = "plan", cascade = CascadeType.ALL, orphanRemoval = true) private Set<PlanMed> planMeds = new HashSet<>(); // 便捷方法:关联药品 public void addMed(Meds med) { PlanMedPK pk = new PlanMedPK(); pk.setPlanDate(plansPKId.getPlanDate()); pk.setPlanId(plansPKId.getPlanId()); pk.setWeekday(this.weekday); pk.setMedId(med.getId()); PlanMed planMed = new PlanMed(); planMed.setId(pk); planMed.setPlan(this); planMed.setMed(med); planMeds.add(planMed); med.getPlanMeds().add(planMed); } }
4. 修改Meds实体
@Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "meds") public class Meds { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column private String name; @Column private BigDecimal price; @Column private String category; @Column private int pillNumber; @Column private Date date; @JsonIgnore @OneToMany(mappedBy = "med", cascade = CascadeType.ALL, orphanRemoval = true) private Set<PlanMed> planMeds = new HashSet<>(); }
内容的提问来源于stack exchange,提问作者Bernardo Alves
相关产品推荐
相关产品推荐

