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

