public class ActorRole : Entity {
public virtual string Actor { get; set; }
public virtual string Role { get; set; }
}
2.映射配置:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NStudy.core"
namespace="NStudy.core">
<class name="ActorRole">
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="Actor" not-null="true" />
<property name="Role" not-null="true" />
</class>
</hibernate-mapping>
3.修改product,添加一个actorrole的集合
public class Movie : Product {
public virtual string Director { get; set; }
public virtual IList<ActorRole> Actors { get; set; }
}
4.修改product的映射配置,添加 <list name="Actors" cascade="all-delete-orphan">
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NStudy.core" namespace="NStudy.core">
<subclass name="Movie" extends="Product">
<property name="Director" />
<list name="Actors" cascade="all-delete-orphan">
<key column="MovieId" />
<index column="ActorIndex" />
<one-to-many class="ActorRole"/>
</list>
</subclass>
</hibernate-mapping>
5.数据库对应的数据:
注意MovieId和ActorIndex是从movie的映射表中生成的。
6.注意在movie中我们的定义
public virtual IList<ActorRole> Actors { get; set; }
是一个list的接口(a good programming practice in general, is the liberal use of interfaces)这个定义允许NH用自己的实现列表去支持延迟加载,呆会再说这个
解释一下下面这个配置文件
<list name="Actors" cascade="all-delete-orphan">
<key column="MovieId" />
<index column="ActorIndex" />
<one-to-many class="ActorRole"/>
</list>
表示一个listmovie中有一个list的actors列表,在数据库中存在一个movieid用来标识这个所有属于这个movie的actors,index用来做列表中的索引,最后一个one-to-many表示一个一对多的关系,而且对应的类是actorole
all-delete-orphan表示删除规则和存储规则,存movie的时候把里面的actor一起存,删的时候也一起删除。
7.延迟加载
关于NH加载数据movie的步骤
1:加载基本信息,不加载actor的数据
select
movie0_.Id as Id1_,
movie0_.Name as Name1_,
movie0_.Description as Descript4_1_,
movie0_.UnitPrice as UnitPrice1_,
movie0_.Director as Director1_
from Product movie0_
where
movie0_.ProductType='Eg.Core.Movie' and
movie0_.Id = 'a2c42861-9ff0-4546-85c1-9db700d6175e'
2:创建一个movie的实例对象。
3:根据取出的数据来设置这个实例对象的属性。
4:创建一个特殊的延迟加载对象实现IList<ActorRole>,将它赋给movie的Actors属性
5:NH返回movie实例
现在来看下面的代码:注意现在还没有加载actors数据。
foreach (var actor in movie.Actors)
Console.WriteLine(actor.Actor);
现在数据需要使用了,这个时候延迟加载对象初始化,它加载了相关联的对象从数据库
SELECT
actors0_.MovieId as MovieId1_,
actors0_.Id as Id1_,
actors0_.ActorIndex as ActorIndex1_,
actors0_.Id as Id0_0_,
actors0_.Actor as Actor0_0_,
actors0_.Role as Role0_0_
FROM ActorRole actors0_
WHERE
actors0_.MovieId= 'a2c42861-9ff0-4546-85c1-9db700d6175e'
当然我们也能够禁止使用延迟加载:
<list name="Actors" cascade="all-delete-orphan" lazy="false">
8.延迟加载代理:
有时候,NH也可以用代理对象来实现延迟加载,看下面的情况,比如现在我们要在actor中添加一个引用回movie对象
public class ActorRole : Entity
{
public virtual string Actor { get; set; }
public virtual string Role { get; set; }
public virtual Movie Movie { get; set; }
}
这里我们加载actorrole对象的时候,只返回数据库里面的movieid,它不会取出所有的数据来初始化movie,而是创建了一个代理对象,用来做延迟加载
一旦我们需要movie中的数据,这个代理对象就会创建一个真实的movie对象,这个过程是透明的,就像从来没有这个代理对象一样。
此代理对象是movie对象的子类,为了让这个代理对象正常运行,需要满足如下的规定:
1>movie不能是sealed类型
2>movie必须要有一个不带参数的默认构造函数,protect或者public都行
3>所有的movie的公公成员必须是virtual的,可以被子类重写的。
NH有几种方式来创建一个代理的对象,默认的是DynamicProxy,一个Castle stack of projects。另外NHibernate包含了LinFu和Spring.NET的支持,并且允许您自己订制。
一般情况下不要lazy="false"这样。对程序有性能的影响。
9.NH支持的集合类型
所有的集合可以用ICollection或者是继承NHibernate.UserType.IUserCollectionType的自定义的集合,只有bag和set可用于双向的关系。
Bags:
允许重复的值,而且排序不太重要的情况
<bag name="Actors">
<key column="MovieId"/>
<one-to-many class="ActorRole"/>
</bag>
那么这个bag也许值是这样的:
actor role 1, actor role 2, actor role 3, actor role 1, actor role 4, 以及 actor role 1
在movie指定的Actors属性类型是:
IList 或者ICollection, 或者IEnumerable.
因为允许重复键的关系,直接删除一个 delete from Actors where ActorRoleId='1' 值,会同时删除几条数据。这样我们如果只是想删除其中一条记录的话,NH不得不在删除后又补全之前“误删”的记录,如果这是一个很大的bag,那么性能问题将很严重
为了应对这个问题NH提供了一个使用代理键的方式:
<idBag name="Actors">
<collection-id column="ActorRoleBagId" type="Int64">
<generator class="hilo" />
</collection-id>
<key column="MovieId"/>
<one-to-many class="ActorRole"/>
</idBag>
这样的话,删除就好多了 delete from Actors where ActorRoleBagId='2'
List类型:
允许重复,需要指定排序,其中的数据类似
role 1 at index 0, actor role 2 at index 1, actor role 3 at index 2, actor role 1 at
index 3, actor role 4 at index 4, and actor role 1 at index5
对应的配置:
<list name="Actors">
<key column="MovieId" />
<list-index column="ActorRoleIndex" />
<one-to-many class="ActorRole"/>
</list>
actors属性必须是IList,其次,当我们插入或删除数据的话,这个index会被重新生成。
set类型:
不允许重复,没有排序。
<set name="Actors">
<key column="MovieId" />
<one-to-many class="ActorRole"/>
</set>
actors的属性类型为 Iesi.Collections.dll中的ISet,NHibernate现在还不直接支持ISet,一旦插入一个数据到set中,将导致这个set从数据库中加载数据,这个时候要确保对象的唯一性,所以要重写Equals和GetHashCode方法
map类型:
相当一个字典类型,键唯一,值可以不唯一
<map name="Actors" >
<key column="MovieId" />
<map-key column="Role" type="string" />
<element column="Actor" type="string"/>
</map>
这个对应的actors属性的类型就必须是IDictionary<string, string>类型。
注意不一定要用基础类型作为键值,也可以用自定义的类型:
<map name="SomeProperty">
<key column="Id" />
<index-many-to-many class="KeyEntity"/>
<many-to-many class="ValueEntity" />
</map>