在實際開發中,主從表關係隨處可見:訂單與明細、班級與學生、部門與員工等。這類「一筆主表記錄對應多筆從表記錄」的一對多關係,是 NHibernate 映射配置的核心場景之一。本文透過班級與學生的經典範例,詳細說明雙向一對多的映射配置與操作技巧。
兩張核心表:
外鍵關係:Class.ClassId ↔ Student.ClassId
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="NHibernateSample.Model">
<class name="NHibernateSample.Model.Class, Model" table="`Class`">
<id name="Id" type="Int32" unsaved-value="0">
<column name="ClassId" length="4" sql-type="int" not-null="true" unique="true" index="PK_Class"/>
<generator class="native" />
</id>
<property name="ClassCode" type="String">
<column name="ClassCode" length="50" sql-type="varchar" not-null="false"/>
</property>
<property name="PeopleCount" type="Int32">
<column name="PeopleCount" length="4" sql-type="int" not-null="false"/>
</property>
<!-- 一對多關聯核心配置 -->
<bag name="Students" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="ClassId"/>
<one-to-many class="NHibernateSample.Model.Student, Model"/>
</bag>
</class>
</hibernate-mapping>
| 屬性 | 值 | 說明 |
|---|---|---|
| name | Students | 實體類別中的集合屬性名稱 |
| inverse | true | 關鍵:由「多」的一方(Student)維護關聯關係,避免重複更新外鍵 |
| lazy | true | 啟用延遲載入,存取集合時才查詢資料庫 |
| cascade | all-delete-orphan | 級聯操作:儲存/更新/刪除主表時自動處理從表記錄;孤兒刪除確保無關聯的從表資料被清除 |
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="NHibernateSample.Model">
<class name="NHibernateSample.Model.Student, Model" table="Student">
<id name="Id" type="Int32" unsaved-value="0">
<column name="StudentID" length="4" sql-type="int" not-null="true" unique="true" index="PK_Student"/>
<generator class="native" />
</id>
<property name="Name" type="String">
<column name="Name" length="50" sql-type="varchar" not-null="false"/>
</property>
<property name="Tel" type="String">
<column name="Tel" length="50" sql-type="varchar" not-null="false"/>
</property>
<!-- 多對一關聯配置 -->
<many-to-one name="Class" class="NHibernateSample.Model.Class, Model">
<column name="ClassId" length="4" sql-type="int" not-null="false"/>
</many-to-one>
</class>
</hibernate-mapping>
name="Class":Student 實體中參考 Class 的屬性名稱class:關聯實體的完整類型名稱column="ClassId":本表中作為外鍵的欄位名稱// Class 實體(主表)
public class Class
{
public virtual int Id { get; set; }
public virtual string ClassCode { get; set; }
public virtual int PeopleCount { get; set; }
// 集合屬性對應 bag name="Students"
public virtual IList Students { get; set; } = new ArrayList();
}
// Student 實體(從表)
public class Student
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Tel { get; set; }
// 參考屬性對應 many-to-one name="Class"
public virtual Class Class { get; set; }
}
// 建立主從關聯物件
Class c = new Class();
Student stu = new Student();
stu.Name = "tao";
stu.Tel = "123123123";
stu.Class = c; // 關鍵:從表設定主表參考
c.PeopleCount = 30;
c.ClassCode = "03510236";
c.Students.Add(stu); // 關鍵:主表集合加入從表物件
// 持久化:只需儲存主表,透過 cascade 自動處理從表
ClassBLL cBLL = new ClassBLL();
cBLL.AddClass(c);
操作核心邏輯:
stu.Class = c)c.Students.Add(stu))cascade 自動級聯儲存從表若主表設定 inverse="false"(預設),NHibernate 會由主表維護關聯,導致執行兩次 UPDATE:一次 INSERT 學生,一次 UPDATE 學生的 ClassId。設為 true 後,由 Student 端維護,僅執行一次 INSERT 即含外鍵值。
all-delete-orphan:適合「從表生命週期完全依附主表」的場景(如訂單明細)save-update:僅級聯新增/更新,刪除主表不刪除從表none:完全手動管理,適用於從表可獨立存在的場景程式碼中務必同時維護兩端:
// 正確做法
student.Class = classObj;
classObj.Students.Add(student);
// 錯誤做法:只設定單向
student.Class = classObj; // 缺少 classObj.Students.Add(student)
NHibernate 一對多映射的關鍵在於:
<bag> + <one-to-many>,從表用 <many-to-one>inverse="true" 交給從表維護,效能更佳cascade 選項掌握這些核心概念,主從表操作就能得心應手。希望本文對您的 NHibernate 學習之路有所助益!