过节闲着没事,跟着老李学NHibernate.
学到了NHibernate的一对多关系.感觉一对多结构就就像一个C++里边包含指针的类,对应关系就像指针一样从父类指向子类!
程序中使用的一对多关系,是与数据库中的表对应的.一对多是指一个父表中的记录对应子表中的多条记录.
在数据库中,子表通过外键与父表关联.
首先来看看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和Order表建立映射.
编写父实体的映射文件
父子关系的映射需要在映射文件中体现.我们使用了Set集合类型,将子表Orders和父表进行关联,父表中使用one-to-many 这样的方法进行映射只需指明需要映射的子类名和子类所在的程序集即可.具体配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyNHibernate.Domain" namespace="MyNHibernate.Domain.Entities"> <class name="Customer" table="Customer"> <id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0"> <generator class="native"/> </id> <property name="FirstName"/> <property name="LastName"/> <set name="Orders" table="Orders" generic="true" inverse="true"> <key column="CustomerId" /> <one-to-many class="MyNHibernate.Domain.Entities.Orders,MyNHibernate.Domain"/> </set> </class> </hibernate-mapping> 编写子实体映射
子实体与父实体的对应关系是多条Order中的记录对应一个父实体中的记录.所以需要使用many-to-one属性.同时还需要在该属性中指明映射关系所使用的外键,
想想没有外键怎么映射呢?
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyNHibernate.Domain" namespace="MyNHibernate.Domain.Entities"> <class name="Orders" lazy="true" table="Orders"> <id name="OrderId" column="OrderId" unsaved-value="0" type="Int32"> <generator class="native"></generator> </id> <property name="OrderDate" type="DateTime" column="OrderDate"></property> <many-to-one name="Customer" column="CustomerId" not-null="true" class="MyNHibernate.Domain.Entities.Customer,MyNHibernate.Domain" foreign-key="CustomerOrders"/> </class> </hibernate-mapping>
建立父实体和子实体的C#描述
有了映射文件,实体类的编写比较简单
先看看写好的实体类:
Customer
public class Customer { public virtual int CustomerId { get; set; } public virtual int Version { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual ISet<Orders> Orders { get; set; } }
Orders
public class Orders { public virtual int OrderId { get; set; } public virtual DateTime OrderDate { get; set; } //多对一关系:Orders属于一个Customer public virtual Customer Customer { get; set; } }
在Customer类中,Orders是作为Customer的一个集合.在Order类中把Customer作为单一对象.
使用Criteria API进行关联查询
我们使用CreateCriteria()在关联之间导航,很容易地在实体之间指定约束。这里第二个CreateCriteria()返回一个 ICriteria的新实例,并指向Orders实体的元素。
在查询中子对象使用子CreateCriteria语句,这是因为实体之间的关联我们在映射文件中已经定义好了。还有一种方法使用CreateAlias()不会创建ICriteria的新实例。
这个例子返回顾客列表有重复的,不是我们想要的结果。
public IList<Customer> UseCriteriaAPI_GetCustomersWithOrders(int customerId) { return session.CreateCriteria(typeof(Customer)) .CreateCriteria("Orders") //.Add(Restrictions.Eq("Customer",customerId)) .Add(Restrictions.IsNotNull("Customer")) .List<Customer>(); }
这个查询是找到根据CustoerId找出一个包含了Customer和它所对应Orders集合的内容.
结语:
手写NHibernate的一对多关系比较容易出错,我刚开始写的时候在映射文件中写错了一个字母,找错找了一整天!
文章写的比较没水平.各位见谅
写完看着,感觉没说明白. 先发了,有空再改改,或者干脆删掉.................