本节内容
通过前几篇文章的介绍,基本上了解了NHibernate,但是在NHibernate中映射关系是NHibernate中的亮点,也是最难掌握的技术。从这篇开始学习这些东西,我将图文结合来说明这里奥秘的知识。
前几篇,我们的例子只使用了一个简单的Customer对象。但是在客户/订单/产品的经典组合中,他们的关系非常复杂?让我们先回顾在第二篇中建立的数据模型。
在图上,我已经清晰的标注了表之间的关系,首先分析Customer和Order之间的“外键关系”或者称作“父子关系”、“一对多关系”。在分析之前先初步了解NHibernate中的集合。
NHibernate支持/定义的几种类型的集合:
Bag:对象集合,每个元素可以重复。例如{1,2,2,6,0,0},在.Net中相当于IList或者IList<T>实现。
Set:对象集合,每个元素必须唯一。例如{1,2,5,6},在.Net中相当于ISet或者ISet<T>实现,Iesi.Collections.dll程序集提供ISet集合。
List:整数索引对象集合,每个元素可以重复。例如{{1,"YJingLee"},{2,"CnBlogs"},{3,"LiYongJing"}},在.Net中相当于ArraryList或者List<T>实现。
Map:键值对集合。例如{{"YJingLee",5},{"CnBlogs",7},{"LiYongJing",6}},在.Net中相当于HashTable或者IDictionary<Tkey,TValue>实现。
实际上,我们大多数情况下使用Set集合类型,因为这个类型和关系型数据库模型比较接近。
直接看下面一幅图的两张表:
上面两张表关系表达的意思是:Customer有一个或多个Orders,Orders属于一个Customer。一般而言,我们称Customer为“父”,Order称为“子”。Customer和Order之间关系就有几种说法:“外键关系”、“父子关系”、“一对多关系”都可以。
在对象模型中:在Customer类中把Orders作为一个集合,这时可以说Customer对象包含了Orders集合。在.NET中通常这样表述:
public class Customer { //...... public IList<Order> Orders{ get; set; } }
访问对象方式:通过子集合访问:Customer.Orders[...]
在NHibernate中,通常而言使用Iesi.Collections.dll程序集中的ISet集合,现在修改Customer.cs类,首先需要引用这个程序集,Customer.cs类代码如下:
using Iesi.Collections.Generic; namespace DomainModel.Entities { public class Customer { public virtual int CustomerId { get; set; } public virtual string Firstname { get; set; } public virtual string Lastname { get; set; } //一对多关系:Customer有一个或多个Orders public virtual ISet<Order> Orders { get; set; } } }
在对象模型中:在Order类中把Customer作为单一对象,这时可以说Order对象包含了一个Customer。在.NET中通常这样表述:
public class Order
{
//......
public Customer Customer{ get; set; }
}
其访问对象方式:通过父对象成员访问:Order.Customer
我们在项目DomainModel层的Entities文件夹中新建Order.cs类,编写代码如下:
namespace DomainModel.Entities { public class Order { public virtual int OrderId { get; set; } public virtual DateTime OrderDate { get; set; } //多对一关系:Orders属于一个Customer public virtual Customer Customer { get; set; } } }
好了,我们现在完成持久类了,下面看看这两个类如何映射。
在NHibernate中,我们可以通过映射文件来关联对象之间的关系。映射文件定义了:
父实体(Customer)映射定义了:
这些具体的设置是NHibernate中的难点所在,以后慢慢讨论这些不同设置下的奥秘之处。
这一篇初步建立Customer与Order的一对多关系,修改Customer.hbm.xml映射文件如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel"> <class name ="DomainModel.Entities.Customer,DomainModel" table="Customer"> <id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0"> <generator class ="native"></generator> </id> <property name="Firstname" column ="Firstname" type="string" length="50" not-null="false"/> <property name ="Lastname" column="Lastname" type="string" length="50" not-null="false"/> <!--一对多关系:Customer有一个或多个Orders--> <set name="Orders" table="`Order`" generic="true" inverse="true"> <key column="Customer" foreign-key="FK_CustomerOrders"/> <one-to-many class="DomainModel.Entities.Order,DomainModel"/> </set> </class> </hibernate-mapping>
可以看到,在“父”端映射使用Set元素,标明属性名称、表名、子实体负责维护关联关系。
子实体(Order)映射定义的东西就是父实体少了:与父实体关联的(多对一、一对多、多对多) 关系,并用一个指针来导航到父实体。
在“子”端通过many-to-one元素定义与“父”端的关联,从“子”端角度看这种关系模型是多对一关联(实际上是对Customer对象的引用)。下面看看many-to-one元素映射属性:
看看这些映射属性具体有什么意义:
我们来建立“子”端到“父”端的映射,新建Order.hbm.xml文件,编写代码如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel"> <class name="DomainModel.Entities.Order,DomainModel" table="`Order`" > <id name="OrderId" column="OrderId" type="Int32" unsaved-value="0"> <generator class="native" /> </id> <property name="OrderDate" column="OrderDate" type="DateTime" not-null="true" /> <!--多对一关系:Orders属于一个Customer--> <many-to-one name="Customer" column="Customer" not-null="true" class="DomainModel.Entities.Customer,DomainModel" foreign-key="FK_CustomerOrders" /> </class> </hibernate-mapping>
关于如何关联看看上面的属性就一目了然了。