【NHibernate.3.3.CookBook学习注记】第一章

1、映射文件(*.hbm.xml)的“生成操作”“属性要设置为嵌入的资源,否则会报No Persister for classMappingException类型的异常消息。

2、代理主键和自然主键

    代理主键是一个系统产生的唯一标识,它没有任何业务含义,在数据库表中,它的值是唯一的,用来区分不同的记录,代理主键例如用自动增长或者GUID等。

<id name="Id">
<generator class="guid.comb"/>
</id>

    自然(业务)主键例如以用户名,身份证号码等作为主键。

<natural-id mutable="true">
<property name="Name" not-null="true" />
</natural-id>
(natural-id元素节点带有一个mutable属性,它的默认值是false,意味着它内部包含的属性或者属性集是不可改变的,或者说是常量。)

    NHibernate强烈建议使用代理主键,有两个原因如下:

          第一,使用自然主键将不可避免的产生复合主键的情况。复合主键需要有多个字段来组成,这些业务键的字段可能来自其他对象。

          第二,因为自然键有着真实世界的实际业务逻辑,它们必须随着业务逻辑而允许被更新。我们假定有一个账号的类,并且有一个UserName的属性,就算这个属性是唯一性的,但是把它作为主键也不是一个好的选择,假如UserName的一部分是由姓氏的第一个字母组成的,当有人改变了他们的名字之后,你将不得不在数据库中更新很多外键关联,如果你是用一个不带有任何业务含义的整形类型的字段作为主键的话,你现在仅仅需要更新UserName字段就可以了,不需要修改其他表中的外键关联字段。

3、Id属性的setter访问器被设置成protected

 

public virtual Guid Id { get; protected set; }

      这是一种简单的方法用来限制对Id属性的访问(这里是对写的访问的限制)。在产品类本身的代码外部是无法改变Id属性的值的,然而,NHibernateId属性的赋值是通过高度优化的反射来实现的,会忽略protected的限制,这样做可以保证你的应用程序不会在不经意间改变这个值。Id是该实体的唯一标识,应该交由NHibernate来管理维护,不应该有我们自己的应该程序来赋值的,当然,这只适用于使用代理主键的情况,如果是自然主键(业务主键)则不应该这样做。

4、继承关系的类型映射

    ①Table-per-class:所有类使用一张表的设计方式

 

<subclass name="Movie" extends="Product">
<property name="Director" />
</subclass>


    ②Table per class:每个类分别使用一张表

 

<joined-subclass name="Movie" extends="Product">
<key column="Id" />
<property name="Director" />
</joined-subclass>

    

    ③Table per concrete class:每一个实际的类对应一张表

 

<union-subclass name="Movie" extends="Product">
<property name="Director" />
</union-subclass>

    

      上面讲到的方法中都是类本身有继承关系,而且映射文件中对类的配置也是有继承关系,这主要是考虑到NHibernate进行多态查询的需要。所以除了上面几个方法之外应该还有第四种方法,那就是类本身有继承关系,而映射文件中无继承关系,这样的话对于NHibernate来说其实没有了继承关系,也就没有了多态查询,我们的应用程序中并不是每个地方都需要多态查询的,有些类设计成继承关系很多时候是为了少写或者少拷贝一点代码而已。总结如下:如果程序中不需要多态查询,那么之前的三种方法也许都不适合,你应该使用这里提到的第四种方法。但是这个方法也有缺点,那就是数据库的冗余存储。其实在这节中的Product是继承Entity类的,而在我们的映射文件并没有出现Entity类,Product和Entity继承关系的处理方式就是我所说的第四种方法。

    来说说上面的三种方式的优缺点:(1)所有类型同一张表(第一种方法)的方法会产生数据库的冗余存储,如果基类属性很少,而子类属性很多,那么这个冗余存储就很大了,显然不适合使用这种方法,这个方法还有一个显著缺点就是子类的属性不能设置为非空,这在某些场合下也是不能接受的,但是这种方法的查询效率很高,特别是多态查询无需join或者union其他表。(2)每个类型分别一张表,基类表共享(第二种方法),这种方法比较符合数据库的规范化设计,而且也没有任何冗余数据,不过此方法对于查询的效率较低,需要进行表之间的join,如果说继承关系比较复杂那么相应的表也就很多,对于多态的查询效率会很低。(3)每个具体的子类一张表(第三种方法),这个和第一种方法比较相似,会产生冗余存储,对于单对象的查询效率很高,对于多态查询会使用union方式,效率介于第一种和第二种之间。如果说基类属性较少,子类属性较多,那么比较适合此方法,如果基类属性较多,子类属性较少那更适合第一种方式。综上所述,几种方法中没有最好的方法,都有优劣,需要根据实际情况选择使用。

⑤、延迟加载代理

public class ActorRole : Entity
{
public virtual string Actor { get; set; }
public virtual string Role { get; set; }
public virtual Movie Movie { get; set; }
}

    如果我们从数据库中取得一个ActorRole对象,NHibernate为我们生成ActorRole对象,但是我们可以预见的是,如果NHibernate只从ActorRole表取得数据的话,它只能得到Movie属性对象的Id属性,除非要从两张表里取数据。但是从两张表里取数据往往是不必要的,如果我们实例化一个ActorRole对象只是用它自己本身属性,而不需要去取和它关联的Movie对象的话,从两张表取数据会对性能造成一定的影响。取而代之的是,NHibernate会创建一个代理类的对象赋值给Movie属性,使之能够启用延迟加载。

   当然,我们在没有加载movie的情况下,照样可以访问它的Id的值(该值存放在ActorRole表中,无需关联到Movie表获取),如果我们需要访问其他的属性或者方法,那么NHibernate就会立即到数据库中去加载所有的movie字段数据,加载这些数据对应用程序来说是完全透明的。这个代理对象和一个真正的Movie对象非常相似的。

  这个代理对象其实是Movie对象的一个子类,为了让子类能够继承Movie类,而且为了支持延迟加载,子类需要拦截这些调用,NHibernate要求我们的实体类必须遵循下面的设计要求:

 1.Movie不能够是一个sealed的类,如果类是sealed的,那么就无法让子类继承了。

 2.Movie类必须有一个访问级别是保护或者公共的不带任何参数的构造函数。代理类继承该类,并且会调用基类的无参构造函数。

 3.所有的公共成员必须是带有virtual关键字,包括方法。如果无virtual关键字,则在使用多态的情况下会调用类型本身的方法,而不是具体类的方法。

posted @ 2011-06-20 17:57  Crazy炆、  阅读(1337)  评论(1编辑  收藏  举报