首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Hibernate 多對多雙向關聯配置詳解:兩種映射策略的 XML 完整實戰

     編輯:Java 綜合教學

場景:雇員與工程多對多

一個雇員可參與多個工程,一個工程可包含多個雇員。中間表 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 Set proEmps = 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 限制精確控制每條關係

避坑指南

  1. 雙向關聯必設 inverse="true":否則雙向各插一次中間表,主鍵衝突或數據重複。
  2. 集合用 Set 而非 List:避免重複,Hibernate 對 Set 去重性能優於 List (List 需額外索引列)。
  3. 顯式指定中間表名與列名:table、key column、many-to-many column 全寫死,防止命名策略變更導致映射失效。
  4. 懶加載邊界:多對多默認 LAZY,Service 層若需遍歷集合,請在事務內 Hibernate.initialize(set) 或用 DTO 投影。
  5. SchemaExport 警惕:hbm2ddl.auto=create 會刪表重建,生產環境嚴禁,請用 update 或專業遷移工具。

總結

Hibernate 多對多雙向關聯的核心抉擇在於 中間表是否承載業務語義。純關係用策略一 (<many-to-many> + inverse),帶屬性用策略二 (中間實體 + 兩個 <many-to-one>)。配置關鍵在 inverse、cascade、集合類型與顯式列名的精準組合,配合 Set 集合,構建健壯、可維護的關係映射。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved