NHibernate.3.0.Cookbook第一章第三节Creating class hierarchy mappings的翻译

最近在看Packtpub.NHibernate.3.0.Cookbook.Oct.2010一书,这次把第一章第三节Creating class hierarchy mappings顺手也翻译了一下,由于自己英语水平欠缺,再加上中文也没学好,所以翻译质量较低,还请大家谅解。

由于NHibernate的中文资料匮乏,导致NHibernate在.net中的使用受到了一定的影响,或者说没有其在java领域这么流行。

 

以下便是译文:

 

创建继承关系的类型映射

父子类的继承层次结构在我们的面向对象语言中非常的常见。在这个例子中,我将给大家展示通过NHibernate去映射继承类型的一种方法,称为所有类型使用一张表的结构设计(table-per-class)

准备工作

在开始这一节之前,请完成之前的示例代码(通过一个xml映射文件去映射一个类型),接下来的工作都是在此基础上进行的。

 

如何去做呢.....

1.创建一个新类,并命名为Book,代码如下:

 

namespace Eg.Core
{
    
public class Book : Product
    {
        
public virtual string ISBN { getset; }
        
public virtual string Author { getset; }
    }
}

 

2.再创建一个新类,并命名为Movie,代码如下:

 

namespace Eg.Core
{
    
public class Movie : Product
    {
        
public virtual string Director { getset; }
    }
}

 

3.修改我们之前建好的Product.hbm.xml文件,使之和如下代码一致:

 

代码
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly
="Eg.Core"
namespace
="Eg.Core">
<class name="Product">
<id name="Id">
<generator class="guid.comb" />
</id>
<discriminator column="ProductType" />
<natural-id mutable="true">
<property name="Name" not-null="true" />
</natural-id>
<property name="Description" />
<property name="UnitPrice" not-null="true" />
</class>
</hibernate-mapping>

 

4.创建一个新的Book.hbm.xml嵌入式资源文件,代码如下:

 

Book.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly
="Eg.Core"
namespace
="Eg.Core">
<subclass name="Book" extends="Product">
<property name="Author"/>
<property name="ISBN"/>
</subclass>
</hibernate-mapping>

 

5.同样地,为Movie也创建一个映射文件Movie.hbm.xml,生成方式同样要选择嵌入的资源文件,代码如下:

 

Movie.hbm.xml
<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
assembly
="Eg.Core"
<subclass name="Movie" extends="Product"> 
<property name="Director" /> 
</subclass> 
</hibernate-mapping>

 

 

它们是怎样工作的呢….

 

 首先我们来讲第一种方法,在这个例子中,我们将上面的三个类(Product, Movie, Book)都映射到一张数据库表中,即所有类使用一张表的设计方式,数据库表的设计见下图:

NHibernate使用了一个用于鉴别各个子类的字段,这里是字段ProductType,它用于区别记录是products, books还是movies类型。默认情况下,这个用于鉴别的字段包含的值是类的名称,在这个例子中,它们的值可能是Eg.Core.Product, Eg.Core.Book, 或者 Eg.Core.Movie,如果你不喜欢NHibernate提供的默认值,你也可以自己定义其它的值来覆盖NHibernate提供的默认值,这时候你需要在父类和子类元素节点中设置discriminator-value属性。例如:

<class name="Product" table="Product" discriminator-value="MyProduct">
<subclass name="Book" extends="Product" discriminator-value="MyBook">

 

 

在我们的Book.hbm.xml映射文件中,我们已经定义了Book类继承Product类,并且实现了自己的属性Author ISBN。在我们的Movie.hbm.xml映射文件中,同样我们也已经定义了Movie类继承Product类,并且实现了Movie类自己的属性Director。
使用所有类型同一张表的设计方式的话,我们不能够定义子类的任何属性不为空,即设置not-null="true",因为这将会使得数据库中的这些字段会有一个非空(not-null)约束:比如说,我们设置了Movie类的Director属性为非空,那么Movie类实例可以持久化到数据库中,但是持久化Product
或者Book实例的时候由于他们没有Director属性,从而导致无法插入到数据库中去。如果说子类的特有属性必须定义为可空对你来说不能接受的话,那么下面的任何一种继承关系的映射策略你都可以使用。

 

更多的继承关系的映射方法

java语言中,extends关键字用来申明继承关系,所以它不难认得extends属性(映射文件中的extends属性)NHibernate诞生之时就是从java中的Hibernate ORM移植过来的。继承方法中,我们首先推荐的是把所有类型都映射到一个表中的方法,即上面已经讲到的方法,但是,NHibernate也给了我们其它的选择,如果不是非常地必须,我们强烈建议你不要在同样的类继承结构中混合使用这些方法。

 

在第二种方法,每个类分别使用一张表的映射策略中,基类以及各个子类分别对应一张表,基类Product的属性值存放在一张共享的表中,MovieBook类从基类Product继承来的属性值都存放在同一张表中,而MovieBook类自己的属性值分别存放在它们自己的表中。

每一个类一张表的方法在映射文件中使用joined-subclass元素节点,这里需要一个key元素节点来指明主键列,顾名思义,NHibernate使用join查询来获取数据,注意到在我们的Product表中已经没有了之前的ProductType列,仅仅在所有类型使用一个表的方法中才需要使用这个鉴别列。使用每个类型单独一张表的方法,我们的Movie映射文件看上去如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly
="Eg.Core"
namespace
="Eg.Core">
<joined-subclass name="Movie" extends="Product">
<key column="Id" />
<property name="Director" />
</joined-subclass>
</hibernate-mapping>

 

 

第三种方法,每一个实际的类对应一张表,这种方法中,每个子类的表中的字段既包含了子类自己的属性,同时也包含了基类的属性,表的设计如下:

如果要取得一个Book实例只需要从Book表中读取数据就可以了,不需要和Product表进行联合查询,如果要多态查询Product数据,那么NHibernate会使用unions查询,同时取得这三个表中的数据。使用每个具体类一张表的方法,那么我们的Movie类的映射文件看起来如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly
="Eg.Core"
namespace
="Eg.Core">
<union-subclass name="Movie" extends="Product">
<property name="Director" />
</union-subclass>
</hibernate-mapping>

 

 

对译文的补充:

1.这一节关于继承层次结构的映射方法讲的比较简单,应该说只能是一个示例而已。

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

 

2.上面提到的三种方法是NHibernate处理继承关系的典型方法,如果你是从一个新的项目开始的话那应该来说问题不大,但是如果使用的是一个遗留系统的话而且又不能对数据库架构进行更改,那也许处理起来比较棘手。

 

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

 

4.文中提到了不建议在同一继承体系中混合使用这些方法,而且文中也没有给出相应的例子,由于三种方式都有优缺点,所以我们在一个继承层次较多的继承体系中混合使用这些方式也不可避免,但是混合使用的方式比较复杂,这里我也不去举例说明了

 

posted @ 2010-12-17 11:25  喆_喆  阅读(2569)  评论(5编辑  收藏  举报