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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 08:21:34