NHibernate Cookbook 3 Mapping a one-to-many relationship

1.创建一个新的类:
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>


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值