首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

NHibernate 多對多關聯映射實戰:從資料庫設計到程式碼實作

     編輯:關於.NET

前言

在關聯式資料庫設計中,多對多關係是非常常見的模式。例如學生選課系統中,一個學生可以選修多門課程,而一門課程也可以被多位學生選修。這種關係在資料庫中通常透過一張中介表來實現。本文將詳細說明如何在 NHibernate 中配置多對多映射,並展示實際操作範例。

開發環境與版本

  • 開發工具:Visual Studio 2008 SP1
  • NHibernate 版本:NHibernate-2.1.2.GA-bin

資料庫結構分析

以學生選課系統為例,涉及四張表,核心關係集中在三張表:

  • Students 表:學生基本資料
  • Course 表:課程資訊
  • SelectCourse 表:中介表,記錄學生與課程的對應關係

外鍵關係如下:

  • Students.ID ↔ SelectCourse.Students (FK_SelectCourse_Students)
  • Course.ID ↔ SelectCourse.CourseID (FK_SelectCourse_Course)

實體類別設計

Student 實體類別

using System;
using System.Collections;

namespace NHibernateSample.Model
{
    public class Student
    {
        protected int _id;
        protected string _name;
        protected string _phone;
        protected Class _class;
        protected IList _courses;

        public Student() { }
        public Student(string name, string phone, Class classes)
        {
            this._name = name;
            this._phone = phone;
            this._class = classes;
        }

        public virtual int Id { get; set; }
        
        public virtual string Name 
        { 
            get { return _name; }
            set 
            {
                if (value != null && value.Length > 50)
                    throw new ArgumentOutOfRangeException("Invalid value for Name", value, value.ToString());
                _name = value;
            }
        }
        
        public virtual string Phone 
        { 
            get { return _phone; }
            set 
            {
                if (value != null && value.Length > 15)
                    throw new ArgumentOutOfRangeException("Invalid value for Phone", value, value.ToString());
                _phone = value;
            }
        }
        
        public virtual Class Class { get; set; }
        
        public virtual IList Courses
        {
            get
            {
                if (_courses == null) _courses = new ArrayList();
                return _courses;
            }
            set { _courses = value; }
        }
    }
}

Course 實體類別

using System;
using System.Collections;

namespace NHibernateSample.Model
{
    public class Course
    {
        protected int _id;
        protected string _courseName;
        protected string _teacher;
        protected DateTime _time;
        protected string _address;
        protected IList _students;

        public Course() { }
        public Course(string courseName, string teacher, DateTime time, string address)
        {
            this._courseName = courseName;
            this._teacher = teacher;
            this._time = time;
            this._address = address;
        }

        public virtual int Id { get; set; }
        
        public virtual string CourseName 
        { 
            get { return _courseName; }
            set 
            {
                if (value != null && value.Length > 50)
                    throw new ArgumentOutOfRangeException("Invalid value for CourseName", value, value.ToString());
                _courseName = value;
            }
        }
        
        public virtual string Teacher 
        { 
            get { return _teacher; }
            set 
            {
                if (value != null && value.Length > 50)
                    throw new ArgumentOutOfRangeException("Invalid value for Teacher", value, value.ToString());
                _teacher = value;
            }
        }
        
        public virtual DateTime Time { get; set; }
        
        public virtual string Address 
        { 
            get { return _address; }
            set 
            {
                if (value != null && value.Length > 50)
                    throw new ArgumentOutOfRangeException("Invalid value for Address", value, value.ToString());
                _address = value;
            }
        }
        
        public virtual IList Students
        {
            get
            {
                if (_students == null) _students = new ArrayList();
                return _students;
            }
            set { _students = value; }
        }
    }
}

映射檔案配置詳解

Student.hbm.xml

<?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="Students">
    <id name="Id" type="Int32" unsaved-value="0">
      <column name="ID" length="4" sql-type="int" not-null="true" unique="true" index="PK_Students"/>
      <generator class="native" />
    </id>
    <property name="Name" type="String">
      <column name="Name" length="50" sql-type="varchar" not-null="false"/>
    </property>
    <property name="Phone" type="String">
      <column name="Phone" length="15" 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>
    
    <!-- 多對多關聯配置重點 -->
    <bag name="Courses" generic="true" table="SelectCourse">
      <key column="StudentID" foreign-key="FK_SelectCourse_Students"/>
      <many-to-many column="CourseID" foreign-key="FK_SelectCourse_Course" 
                    class="NHibernateSample.Model.Course, Model" />
    </bag>
  </class>
</hibernate-mapping>

Course.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="NHibernateSample.Model">
  <class name="NHibernateSample.Model.Course, Model" table="Course">
    <id name="Id" type="Int32" unsaved-value="0">
      <column name="ID" length="4" sql-type="int" not-null="true" unique="true" index="PK_Course"/>
      <generator class="native" />
    </id>
    <property name="CourseName" type="String">
      <column name="CourseName" length="50" sql-type="varchar" not-null="false"/>
    </property>
    <property name="Teacher" type="String">
      <column name="Teacher" length="50" sql-type="varchar" not-null="false"/>
    </property>
    <property name="Time" type="DateTime">
      <column name="`Time`" length="8" sql-type="datetime" not-null="false"/>
    </property>
    <property name="Address" type="String">
      <column name="Address" length="50" sql-type="varchar" not-null="false"/>
    </property>
    
    <!-- 反向多對多關聯配置 -->
    <bag name="Students" generic="true" table="SelectCourse">
      <key column="CourseID" foreign-key="FK_SelectCourse_Course"/>
      <many-to-many column="StudentID" class="NHibernateSample.Model.Student, Model"/>
    </bag>
  </class>
</hibernate-mapping>

關鍵配置解析

<bag> 元素

用於映射集合屬性,對應 .NET 中的 IList 或 IList<T>。屬性說明:

  • name:實體類別中的集合屬性名稱
  • table:中介表名稱
  • generic:是否使用泛型集合

<key> 元素

定義中介表中對應「本端」實體的外鍵欄位:

  • 在 Student 映射中:column="StudentID" 對應 SelectCourse 表的 StudentID 欄位
  • 在 Course 映射中:column="CourseID" 對應 SelectCourse 表的 CourseID 欄位

<many-to-many> 元素

定義指向「另一端」實體的關聯:

  • column:中介表中對應另一端實體的外鍵欄位
  • class:另一端實體的完整類型名稱
  • foreign-key:指定外鍵約束名稱(可選)

核心概念總結:

  • key 節點:針對「當前映射檔案對應的實體類別」說明,定義中介表中指向自己的外鍵
  • many-to-many 節點:針對「多對多關係中的另一方」說明,定義中介表中指向對方的外鍵及對方實體類別

實際操作範例:新增學生與課程關聯

// 建立物件
Class cls = new Class();
Student stu = new Student();
Course c = new Course();

// 設定班級資料
cls.ClassCode = "03510236";
cls.StudentsAmount = 36;

// 設定學生資料
stu.Class = cls;
stu.Name = "teau";
stu.Phone = "0897658";

// 設定課程資料
c.Address = "Beijing";
c.CourseName = "C++";
c.Teacher = "Tao";
c.Time = DateTime.Now;

// 建立雙向關聯
stu.Courses.Add(c);
c.Students.Add(stu);

// 持久化操作
ClassesBLL cBLL = new ClassesBLL();
CourseBLL courseBLL = new CourseBLL();

try
{
    if (cBLL != null && cls != null)
    {
        cBLL.AddStudent(cls);    // 透過班級級聯儲存學生
        courseBLL.AddCourse(c);  // 儲存課程及關聯
    }
}
catch (Exception ex)
{
    // 建議記錄日誌後重新拋出
    throw;
}

替代方案:將多對多拆解為兩個一對多

多對多關係本質上可以視為兩個一對多關係的組合:

  • Student ↔ SelectCourse:一對多
  • SelectCourse ↔ Course:一對多

如果不想使用 <many-to-many>,可以建立 SelectCourse 實體類別,分別配置兩個一對多關係。這樣做的優點是可以在中介表中儲存額外資訊(如選課時間、成績等),缺點是程式碼較為繁瑣。

總結

NHibernate 中的多對多映射雖然概念上較為繞,但只要掌握 <key> 和 <many-to-many> 的角色定位,理解「中介表如何連接兩端實體」,配置就不再困難。實務上建議:

  1. 優先考慮純多對多映射(無中介實體)
  2. 若中介表需攜帶業務屬性,改用兩個一對多映射
  3. 注意雙向關聯的一致性維護(兩端集合都要 Add)
  4. 適當設定 cascade 與 inverse 避免重複儲存或資料不一致

希望本文能幫助大家更清晰地理解 NHibernate 多對多配置!若有錯誤或建議,歡迎指正交流。

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