Hibernate映射异常:集合外键映射列数错误求助
解决Hibernate复合主键映射异常问题
咱们先拆解一下你遇到的org.hibernate.MappingException错误,这个问题主要出在两个关键配置上,我一步步给你说怎么改:
1. 修正@OneToMany的mappedBy属性
你在Subject类里定义的students集合,mappedBy写的是"student",但实际上StudentSubject中关联Subject的字段是private Subject subject;,所以mappedBy应该指向这个字段名,而不是student。
修改后的Subject类部分代码:
@Entity @IdClass(SubjectId.class) @Data public class Subject { @Id private String name; @ManyToOne @JsonBackReference @Id @JoinColumn(name = "teacher_id") // 显式指定外键列名,方便后续主键类匹配 private Teacher teacher; // 这里把mappedBy改成"subject" @OneToMany(mappedBy = "subject") private List<StudentSubject> students; }
2. 修正复合主键类SubjectId的定义
Hibernate的复合主键类(实现Serializable的类)里的属性,不能直接用实体类类型(比如你这里的private Teacher teacher;),必须用对应实体的主键类型(比如如果Teacher的主键是Long类型的id,就用Long;如果是String类型的teacherNo,就用String)。
假设你的Teacher类主键是Long id,修改后的SubjectId类:
import java.util.Objects; public class SubjectId implements Serializable{ // 和Subject类中的@Id属性对应:name对应name,teacher的主键对应teacherId private String name; private Long teacherId; // 必须要有全参构造 public SubjectId(String name, Long teacherId) { this.name = name; this.teacherId = teacherId; } // 必须要有无参构造 SubjectId(){} // 别忘了重写equals和hashCode!复合主键类必须实现这两个方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SubjectId subjectId = (SubjectId) o; return Objects.equals(name, subjectId.name) && Objects.equals(teacherId, subjectId.teacherId); } @Override public int hashCode() { return Objects.hash(name, teacherId); } }
3. 修正StudentSubject和StudentSubjectId的配置
因为StudentSubject用了复合主键,它的主键类StudentSubjectId需要包含所有@Id字段的主键类型,也就是对应Student的主键和Subject的复合主键(name+teacherId)。
假设Student的主键是String studentId,修改后的StudentSubject和StudentSubjectId:
StudentSubject类
@Entity @IdClass(StudentSubjectId.class) @Data public class StudentSubject { // 这里不需要单独定义studentId,而是通过@ManyToOne关联Student作为主键 @ManyToOne @Id @JoinColumn(name = "student_id") private Student student; @ManyToOne @Id @JoinColumns({ @JoinColumn(name = "subject_name", referencedColumnName = "name"), @JoinColumn(name = "subject_teacher_id", referencedColumnName = "teacher_id") }) private Subject subject; }
StudentSubjectId类
import java.util.Objects; public class StudentSubjectId implements Serializable { // 对应Student的主键类型 private String studentId; // 对应Subject的复合主键的两个属性 private String name; private Long teacherId; // 全参构造 public StudentSubjectId(String studentId, String name, Long teacherId) { this.studentId = studentId; this.name = name; this.teacherId = teacherId; } // 无参构造 StudentSubjectId(){} // 必须重写equals和hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; StudentSubjectId that = (StudentSubjectId) o; return Objects.equals(studentId, that.studentId) && Objects.equals(name, that.name) && Objects.equals(teacherId, that.teacherId); } @Override public int hashCode() { return Objects.hash(studentId, name, teacherId); } }
关键注意点
- 所有复合主键类必须实现
Serializable,并且重写equals()和hashCode()方法,Hibernate需要用这些方法来识别主键的唯一性。 - @IdClass中的属性名必须和实体类中@Id字段的关联主键属性名完全匹配(比如Subject类中@Id Teacher teacher对应的是teacher的id,所以SubjectId里用teacherId)。
- 多对多关联中间表(这里StudentSubject其实是Subject和Student的关联表)用@IdClass时,要显式指定@JoinColumns来关联复合主键的各个字段。
内容的提问来源于stack exchange,提问作者Registered User
相关产品推荐
相关产品推荐

