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

Hibernate多对多关联问题:Prezenta表关联字段不符合预期

解决Hibernate中Prezenta表关联UserCurs时使用uc_id作为外键的问题

看起来你遇到的核心问题是:UserCurs实体的主键配置冲突,导致Hibernate在生成Prezenta表外键时,优先使用了UserCurs的嵌入式复合ID(User+Curs),而不是你期望的自增主键uc_id。下面是一步步的修复方案:

问题根源分析

你当前的UserCurs实体同时定义了@Id(自增uc_id)和@EmbeddedId(复合IDpk),这违反了JPA的主键规范——一个实体只能有一个主键策略。Hibernate在这种情况下会默认把嵌入式复合ID作为实体的主键标识,因此当Prezenta关联UserCurs时,会拆解复合ID的两个字段生成外键,也就是你看到的userCurs_id_curs和userCurs_id_user。

修复步骤

1. 重构UserCurs实体,让uc_id成为唯一主键

我们需要去掉嵌入式复合ID的主键地位,把User和Curs的关联改成普通的@ManyToOne,同时添加唯一约束保证用户和课程的组合不重复(避免同一用户重复选同一门课):

@Entity
@Table(name="user_curs", 
       uniqueConstraints = @UniqueConstraint(columnNames = {"id_user", "id_curs"})) // 保证用户+课程组合唯一
public class UserCurs implements java.io.Serializable {
    private static final long serialVersionUID = 4050660680047579957L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer uc_id;

    @ManyToOne
    @JoinColumn(name="id_user", nullable = false)
    private User user;

    @ManyToOne
    @JoinColumn(name="id_curs", nullable = false)
    private Curs cs;

    // Getters & Setters
    public Integer getUc_id() { return uc_id; }
    public void setUc_id(Integer uc_id) { this.uc_id = uc_id; }
    public User getUser() { return user; }
    public void setUser(User user) { this.user = user; }
    public Curs getCs() { return cs; }
    public void setCs(Curs cs) { this.cs = cs; }
}

2. 修改PrezentaID,确保正确实现序列化与相等性判断

嵌入式复合ID必须重写equals()和hashCode()方法,否则Hibernate无法正确处理实体的相等性:

@Embeddable
public class PrezentaID implements java.io.Serializable{
    private static final long serialVersionUID = -9120607274421816301L;

    private UserCurs usercurs;
    private OrarCurs orar;

    @ManyToOne
    public UserCurs getUserCurs() { return usercurs; }
    public void setUserCurs(UserCurs usercurs) { this.usercurs = usercurs; }

    @ManyToOne
    public OrarCurs getOrar() { return orar; }
    public void setOrar(OrarCurs orar) { this.orar = orar; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PrezentaID that = (PrezentaID) o;
        return Objects.equals(usercurs, that.usercurs) && Objects.equals(orar, that.orar);
    }

    @Override
    public int hashCode() {
        return Objects.hash(usercurs, orar);
    }
}

3. 调整Prezenta实体的关联配置,指向uc_id

修改@AssociationOverride的referencedColumnName,明确关联UserCurs的主键uc_id:

@Entity
@Table(name="prezenta")
@AssociationOverrides({
    @AssociationOverride(name="pk1.usercurs", joinColumns = @JoinColumn(name="id_usercurs", referencedColumnName="uc_id")),
    @AssociationOverride(name="pk1.orar", joinColumns = @JoinColumn(name="id_orar", referencedColumnName="oc_id"))
})
public class Prezenta implements java.io.Serializable{
    private static final long serialVersionUID = 4050660680047579957L;

    private PrezentaID pk1 = new PrezentaID();
    private Boolean present;

    @EmbeddedId
    public PrezentaID getPk1() { return pk1; }
    public void setPk1(PrezentaID pk1) { this.pk1 = pk1; }

    @Column(name="present")
    public Boolean getPresent() { return present; }
    public void setPresent(Boolean present) { this.present = present; }
}

验证结果

修改完成后重新运行程序,生成的prezenta表会包含以下字段:

  • id_usercurs:关联user_curs表的uc_id字段
  • id_orar:关联OrarCurs表的oc_id字段
  • present:存储出勤状态的字段

完全符合你期望的关联结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 07:43:13