承接一對多單向關聯,本文解析一對多雙向關聯。核心在於「多的一方」新增對「一的一方」的引用,並配置 <many-to-one> 實現反向導航。
public class Classes { // 一的一方
private int id;
private String name;
private Set students; // 集合
// getter/setter
}
public class Student { // 多的一方
private int id;
private String name;
private Classes classes; // 單一引用,關鍵差異
// getter/setter
}
| 表 | 欄位 |
|---|---|
| t_classes | id (PK), name |
| t_student | id (PK), name, classesid (FK) |
外鍵 classesid 在多的一方 (Student) 表中,結構無變化。
<class name="com.zs.hibernate.Classes" table="t_classes">
<id name="id"><generator class="native"/></id>
<property name="name"/>
<set name="students" inverse="true"> <!-- 關鍵:inverse="true" -->
<key column="classesid"/>
<one-to-many class="com.zs.hibernate.Student"/>
</set>
</class>
<class name="com.zs.hibernate.Student" table="t_student">
<id name="id"><generator class="native"/></id>
<property name="name"/>
<many-to-one name="classes" column="classesid"/> <!-- 關鍵:many-to-one -->
</class>
| 配置 | 位置 | 作用 |
|---|---|---|
inverse="true" | 一的一方 <set> | 宣告「關係維護權」在多的一方,避免 Hibernate 重複執行 INSERT/UPDATE 中間表 |
<many-to-one> | 多的一方 | 建立反向導航,映射外鍵欄位,使 Student 能獲取 Classes |
classes.getStudents().add(student) + student.setClasses(classes)inverse="true",Hibernate 會在持久化時雙邊各執行一次 SQL,導致重複更新外鍵、效能損耗甚至死鎖inverse="true" 告訴 Hibernate:「以多的一方 (Student) 為準,一的一方 (Classes) 不要管外鍵更新」// 在 Classes 側提供便利方法,確保雙邊同步
public class Classes {
public void addStudent(Student student) {
students.add(student);
student.setClasses(this); // 關鍵:同步反向引用
}
public void removeStudent(Student student) {
students.remove(student);
student.setClasses(null);
}
}
inverse="true" → 雙重更新外鍵classes.getStudents().add(s) 忘 s.setClasses(c)) → 記憶體不一致、外鍵可能為 nullinverse → 錯誤,inverse 只能用在集合端@Entity @Table(name="t_classes")
public class Classes {
@OneToMany(mappedBy="classes") // mappedBy 指向多的一方欄位名
private Set students = new HashSet<>();
}
@Entity @Table(name="t_student")
public class Student {
@ManyToOne
@JoinColumn(name="classesid")
private Classes classes;
}
註解天然以 mappedBy 宣告非維護端,無需 inverse。
下篇進入多對多映射:單向 → 雙向,中間表機制、雙向同步、mappedBy 實戰。