一個雇員可參與多個工程,一個工程可包含多個雇員。中間表 pro_emp 維護關係。
適用: 純關係,無額外屬性。
// Employe.java
public class Employe {
private Integer empId;
private String empName;
private Set projects = new HashSet<>(); // 集合類型為對方實體
// getter/setter
}
// Project.java
public class Project {
private Integer proId;
private String proName;
private Set employes = new HashSet<>();
// getter/setter
}
<!-- employe.hbm.xml -->
<class name="Employe" table="employe">
<id name="empId"><generator class="sequence"><param name="sequence">SQU_NUM</param></generator></id>
<property name="empName"/>
<set name="projects" table="pro_emp" inverse="true"> <!-- 非維護端 -->
<key column="rpro_id"/> <!-- 中間表外鍵指向本表 -->
<many-to-many class="Project" column="remp_id"/> <!-- 中間表外鍵指向對方表 -->
</set>
</class>
<!-- project.hbm.xml -->
<class name="Project" table="project">
<id name="proId"><generator class="sequence"><param name="sequence">SQU_NUM</param></generator></id>
<property name="proName"/>
<set name="employes" table="pro_emp" cascade="save-update"> <!-- 維護端 -->
<key column="remp_id"/>
<many-to-many class="Employe" column="rpro_id"/>
</set>
</class>
table="pro_emp":指定中間表名inverse="true":只能配置在一方,聲明「我不維護關係」,避免雙方各插一次中間表記錄cascade="save-update":維護端保存時級聯保存關聯對象及關係<key column="..."/>:中間表中指向「本表」的外鍵列<many-to-many column="..."/>:中間表中指向「對方表」的外鍵列Session session = HibernateUtil.currentSession();
Transaction tx = session.beginTransaction();
Employe emp = new Employe(); emp.setEmpName("田超");
Employe emp1 = new Employe(); emp1.setEmpName("施強");
Project pro = new Project(); pro.setProName("開發工程");
pro.getEmployes().add(emp); // 只操作維護端集合
pro.getEmployes().add(emp1);
session.save(pro); // 級聯保存 emp, emp1 及關係
tx.commit();
HibernateUtil.closeSession();
適用: 中間表有業務屬性,或需獨立查詢關係記錄。
// Employe.java private SetproEmps = new HashSet<>(); // 集合類型為中間實體 // Project.java private Set proEmps = new HashSet<>(); // ProEmp.java (中間實體) private Integer id; private Employe emp; private Project pro; // getter/setter
<!-- employe.hbm.xml --> <class name="Employe" table="employe"> <id name="empId">...</id> <property name="empName"/> <!-- 無集合映射,純屬性 --> </class> <!-- project.hbm.xml --> <class name="Project" table="project"> <id name="proId">...</id> <property name="proName"/> </class> <!-- pro_emp.hbm.xml 關鍵:將多對多拆為兩個多對一 --> <class name="ProEmp" table="pro_emp_new"> <id name="id">...</id> <many-to-one name="emp" class="Employe" column="emp_id"/> <many-to-one name="pro" class="Project" column="pro_id"/> </class>
Employe emp = new Employe(); emp.setEmpName("田超");
Project pro = new Project(); pro.setProName("開發工程");
ProEmp rel = new ProEmp(); rel.setEmp(emp); rel.setPro(pro);
session.save(emp); session.save(pro); session.save(rel); // 三方均需顯式保存
| 維度 | 策略一:直接集合 | 策略二:中間實體 |
|---|---|---|
| 中間表業務字段 | ❌ 不支持 | ✅ 自然支持 |
| 代碼行數 | 少 (無額外實體) | 多 (需 ProEmp 類) |
| 保存操作 | 單側 session.save(pro) | 三側 save(emp)/save(pro)/save(rel) |
| 關係獨立查詢 | ❌ 需 HQL/SQL | ✅ from ProEmp |
| 級聯刪除控制 | 受 cascade 限制 | 精確控制每條關係 |
inverse="true":否則雙向各插一次中間表,主鍵衝突或數據重複。Set 而非 List:避免重複,Hibernate 對 Set 去重性能優於 List (List 需額外索引列)。table、key column、many-to-many column 全寫死,防止命名策略變更導致映射失效。Hibernate.initialize(set) 或用 DTO 投影。hbm2ddl.auto=create 會刪表重建,生產環境嚴禁,請用 update 或專業遷移工具。Hibernate 多對多雙向關聯的核心抉擇在於 中間表是否承載業務語義。純關係用策略一 (<many-to-many> + inverse),帶屬性用策略二 (中間實體 + 兩個 <many-to-one>)。配置關鍵在 inverse、cascade、集合類型與顯式列名的精準組合,配合 Set 集合,構建健壯、可維護的關係映射。