本節要點:
1、如何配置表之間多對多的關系
2、多表之間如何進行操作
對於關系型數據庫,表之間也多對多的關系也很常見的。在我們實際開發過 程中如何進行正確的映射的配置,以及所關聯的表之間是如何操作的?這是本文 講述的重點。
開發環境:VS2008 SP1 使用的NHibernate版本:NHibernate-2.1.2.GA- bin。
將上節討論的學生表與班級表再進行深層次的引入:如果構建一個學生選課 的數據庫,還需要什麼表?表之間的關系如何對應?還是通過LINQ的截圖說說表 的字段以及表之間的關系【不討論LINQ與Nhibernate之間的關系,免得又有朋友 誤解】。圖如下:
通過圖,可以很清楚的看出四張表見的關系。不過我主要說的是下面三張表 。
對於學生來說,他可以選擇多門課程。對於課程來說,多個學生也可以選擇 同一門課程。對於學生實體類和課程實體類,他們之間就是一種多對多的關系。 順便給出下面三張表之間的外鍵。students表與SelectCourse:引用列【ID】與 被引用列【Students】,外鍵名:FK_SelectCourse_Students。Course表與 SelectCourse表:引用列【ID】與被引用列【CourseID】,外鍵名: FK_SelectCourse_Course。被引用類那麼他們之間的映射關系如何?
通過上節的說明,一對多是在映射文件中通過one-to-many表示的。大家很自 然想到多對多就是通過many-to-many來表示。有了一對多配置的基礎,我就說說 其中重要的。
Stuents表的映射文件:
1 <?xml version="1.0" encoding="utf-8" ?>
2 <hibernate-mapping xmlns="urn:nhibernate-mapping- 2.2" assembly="Model" namespace="NHibernateSample.Model">
3 <class name="NHibernateSample.Model.Student, Model" table="Students">
4 <id name="Id" type="Int32" unsaved- value="0">
5 <column name="ID" length="4" sql-type="int" not-null="true" unique="true" index="PK_Students"/>
6 <generator class="native" />
7 </id>
8 <property name="Name" type="String">
9 <column name="Name" length="50" sql- type="varchar" not-null="false"/>
10 </property>
11 <property name="Phone" type="String">
12 <column name="Phone" length="15" sql- type="varchar" not-null="false"/>
13 </property>
14
15 <many-to-one name="Class" class="NHibernateSample.Model.Class, Model">
16 <column name="ClassID" length="4" sql- type="int" not-null="false"/>
17 </many-to-one>
18 <bag name="Courses" generic="true" table="SelectCourse">
19 <key column="StudentID" foreign- key="FK_SelectCourse_Students">
20 </key>
21 <many-to-many column="CourseID" foreign- key="FK_SelectCourse_Course"
class="NHibernateSample.Model.Course, Model" ></many-to-many>
22 </bag>
23 </class>
24 </hibernate-mapping>
Students對應的實體類:
代碼
using System;
using System.Collections;
namespace NHibernateSample.Model
{
#region Student
/// <summary>
/// Student object for NHibernate mapped table 'Students'.
/// </summary>
public class Student
{
#region Member Variables
protected int _id;
protected string _name;
protected string _phone;
//多個學生屬於一個班級
protected Class _class;
protected IList _Course;
#endregion
#region Constructors
public Student() { }
public Student( string name, string phone, Class classes )
{
this._name = name;
this._phone = phone;
this._class = classes;
}
#endregion
#region Public Properties
public virtual int Id
{
get {return _id;}
set {_id = value;}
}
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 { return _class; }
set { _class = value; }
}
public virtual IList Courses
{
get
{
if (_Course == null)
{
_Course = new ArrayList();
}
return _Course;
}
set { _Course = value; }
}
#endregion
}
#endregion
}
Course表對應的映射文件:
1 <?xml version="1.0" encoding="utf-8" ?>
2 <hibernate-mapping xmlns="urn:nhibernate-mapping- 2.2" assembly="Model"
3 namespace="NHibernateSample.Model">
4 <class name="NHibernateSample.Model.Course, Model" table="Course">
5 <id name="Id" type="Int32" unsaved- value="0">
6 <column name="ID" length="4" sql-type="int" not-null="true" unique="true"
index="PK_Course"/>
7 <generator class="native" />
8 </id>
9 <property name="CourseName" type="String">
10 <column name="CourseName" length="50" sql- type="varchar" not-null="false"/>
11 </property>
12 <property name="Teacher" type="String">
13 <column name="Teacher" length="50" sql- type="varchar" not-null="false"/>
14 </property>
15 <property name="Time" type="DateTime">
16 <column name="`Time`" length="8" sql- type="datetime" not-null="false"/>
17 </property>
18 <property name="Address" type="String">
19 <column name="Address" length="50" sql- type="varchar" not-null="false"/>
20 </property>
21 <bag name="Students" generic="true" table="SelectCourse">
22 <key column="CourseID" foreign- key="FK_SelectCourse_Course"/>
23 <many-to-many column="StudentID" class="NHibernateSample.Model.Student, Model">
24 </many-to-many>
25 </bag>
26 </class>
27 </hibernate-mapping>
28
對應的實體類:
代碼
using System;
using System.Collections;
namespace NHibernateSample.Model
{
#region Course
/// <summary>
/// Course object for NHibernate mapped table 'Course'.
/// </summary>
public class Course
{
#region Member Variables
protected int _id;
protected string _courseName;
protected string _teacher;
protected DateTime _time;
protected string _address;
//protected IList _courseSelectCourses;
protected IList _Students;
#endregion
#region Constructors
public Course() { }
public Course( string courseName, string teacher, DateTime time, string address )
{
this._courseName = courseName;
this._teacher = teacher;
this._time = time;
this._address = address;
}
#endregion
#region Public Properties
public virtual int Id
{
get {return _id;}
set {_id = value;}
}
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 { return _time; }
set { _time = value; }
}
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; }
}
#endregion
}
#endregion
}
對於實體類,就不用說了。主要說說映射文件中的<bag>節點的配置。
bag:對象集合。集合中的元素可以重復。相當於.Net中的IList或者 IList<T>。當然對應的name是相應實體類的屬性了。
我個人最為關鍵的的是KEY、與many-to-mnay兩個階段的配置與理解。首先對 於兩個實體類,我應該建立他們各自的映射文件,這是最基本的。我就以Course 表來說。它與Students表多對多的關系是通過SelectCourse表建立的。要給 Course實體類建立與Students的映射關系,唯一的途徑就是通過SelectCourse表 上的外鍵FK_SelectCourse_Course。而外鍵FK_SelectCourse_Course是引用的 Course表的主鍵。
所以對key節點:他對應的column【對應表的列明,這裡就不再具體多說】當 然是CourseID。
對於many-to-many節點,我是這樣理解的。既然是多對多。第一個many指的當 然是它自己,即Order,另外一個顯然是針對Students。那麼Students與Course 建立對應關系的唯一途徑也只有通過引用在它的主鍵上建立的外鍵 FK_SelectCourse_Students。後面對應的是實體類Students對應的程序集以及指 明Course多對多的實體類Student。
映射文件小結:
key節點:是對於映射文件對應的實體類所說的。
many-to-many:是對多對多中另外“多”的一方說的
映射文件的介紹與說明就到這。下面說說它們之間該如何做操作。我僅僅以 添加為例進行說明。
添加的需求說明:我希望為一個學生添加他所選的課程,並將此學生添加到 一個新的班級中【課程也要求新添加到課程表】。還是直接上代碼較為直接:
1 //申明對象
2 Class cls = new Class();
3 Student stu = new Student();
4 SelectCourse sCourse = new SelectCourse();
5 Course c = new Course();
6
7 cls.ClassCode = "03510236";
8 cls.ClassStudentses.Add(stu);
9 cls.StudentsAmount = 36;
10
11 stu.Class = cls;
12 stu.Name = "teau";
13 stu.Phone = "0897658";
14
15 sCourse.Course = c;
16 sCourse.Student = stu;
17
18 c.Address = "Beijing";
19 c.CourseName = "C++";
20 c.CourseSelectCourses.Add(sCourse);
21 c.Teacher = "Tao";
22 c.Time = DateTime.Now;
23
24
25 ClassesBLL cBLl = new ClassesBLL();
26 CourseBLL courseBLL = new CourseBLL();
27 try
28 {
29 if (cBLl != null && cls !=null)
30 {
31 cBLl.AddStudent(cls);
32 courseBLL.AddCourse(c);
33 }
34 }
35 catch(Exception ex)
36 {
37 throw ex;
38 }
39
上述添加需求的代碼時候就這些,其中關於業務實體類通過ISession會話操 作數據庫的代碼就不再給出。相信只要了解Nhibernate基礎只是的都能知道是如 何操作的。
說了這麼多,多對多的映射到這就說完了。我個人覺得多對多的問題也就是 通過中間結構構成的多對多。假如在本例中,我們要完成上述例子中的添加需求 ,能不能不通過多對多的關系操作呢?我覺得一點問題也沒有。因為對Studet表 與SelectCourse表,它們對應的實體關系之間就是一對多的關系。同樣對 SelectCourse實體與Course實體,它們之間也是一對多的關系。如果我們把 Student與Course的關系分開來看,其實就是兩個一對多的過程。通過一對多的 關系也可以解決。
總結:多對多的關系是Nhibernate映射配置中比較難也比較繞的一部分【我 個人覺得】。但是只要理解其中key和many-to-many各自配置的作用,以及為何 是通過這樣配置的,問題也就迎刃而解了。
就寫到這了,希望對各位有所幫組,若有理解或者表述有誤的也希望大俠們 踴躍拍磚!