NHibernate 中支持的三种继承策略

来源:互联网 发布:js判断对象为空 编辑:程序博客网 时间:2024/04/29 20:35

下面对这三种映射策略的优缺点逐一加以说明:

1. Table per concrete class

这种方式要求我们把每一个子类都对应一张表,把这个子类的所有属性(包括父类的所有属性)都映射到一张表上。

这种方式的最大的问题是对多态不能很好的支持。在数据库中关联关系一般是通过外键来体现的。如果所有的子类都单独映射到不同的表,那么子类对于父类的多态关系就不能通过一个简单的外键搞定了。这在我们的领域模型中就会产生问题。BillDetails肯定还会与其他类关联,比如User类,那么这两张表都需要外键引用到User表。

多态查询(查询并且返回所有的那些匹配查询类接口类)也会产生问题。对于父类的查询需要执行多条select语句,每一个子类一条。会严重影响性能。

还有一个严重的问题是,如果父类修改了,会导致所有子类相关列和映射文件的修改。

这种映射策略,在NHibernate中没有专门的标签,仅需要为每一个具体类建立一个新的<class>声明,然后指定一个table属性。这种映射策略,仅当真的可以放弃继承带来的好处的时候才可以使用,否则是绝对不推荐的。

2. Table per class hierarchy

这种映射策略要求将整类的继承体系完全映射到一张表中。将整个继承体系中的所有类的所有属性都一一映射到这个表的各个列上。每个子类代表表中由识别器列(discriminator column)标识的一行。

这种映射策略在三种策略中最简单,性能也最好。但是这种方式也有一个主要问题,子类定义的那些属性,对于到数据库中的列必须要被定义成可以为空。这将失去数据库对Not Null的约束,从数据完整性的角度看,问题很严重:)

在NHibernate中,我们用<subclass>元素定义table-per-class hierarchy映射。映射文件如下所示:

<hibernate-mapping>
    <class table="BILLING_DETAILS" discriminator-value="BD">
        <id column="BILLING_DETAILS_ID" type="long">
            <generator />
        </id>
        <discriminator column="BILLING_DETAILS_TYPE" type="string"/>
        <property column="OWNER" type="string"/>
        ...
        <subclass discriminator-value="CC">
            <property column="CREDIT_CARD_TYPE"/>
            ...
        </subclass>
        ...
    </class>
</hibernate-mapping>

其中discriminator并不是持久类的属性,它仅是用来区分继承体系中的各个持久类的,它仅供NHibernate内部自己使用 。这一列的名字是BILLING_DETAILS_TYPE,在这个例子中这一列的值会被置为CC和BD,NHibernate会自动地得到或者设置这一列的值。也就是说表中的每一行代表继承体系中的一个类,用BILLING_DETAILS_TYPE的不同的值来区分,CC就表示这一行对应CreditCard这个类。每一个子类都有自己的subclass元素。子类的属性被映射到BILLING_DETAILS表的列中。值得注意的是,各个子类的属性必须设置not-null属性为true,因为,CreditCard子类实例不可能全部拥有其他子类实例的属性。<subclass>可以嵌套其他的<subclass>,直到整个的继承体系全部映射到表中。

3.  Table per subclass

第三种就是用外键关系来表示继承关系。在这种策略中,每一个子类,父类都会单独映射到一张表中。和第一种策略不同的是,这种策略的表中仅包含类中自己定义的那些属性(不包含继承下来的属性)。

如果CreditCard子类的实例被持久化,那么BillingDetails父类中定义的那些属性也要被持久化到BILLING_DETAILS表中新的一行中,CreditCard子类中自己定义的那几个属性(不包括继承的属性)会被持久化到CREDIT_CARD表新的一行中。这两个新行通过共享的主键相关联。之后子类的实例从数据库中被取出的时候,需要join子类和父类分别对应得那几张表。

这种策略最大的好处就是关系模型完全标准化,关系模型和领域模型完全一致。在NHibernate中用<joined-subclass>来表示这种策略,映射文件如下:

<hibernate-mapping>
    <class table="BILLING_DETAILS">
        <id column="BILLING_DETAILS_ID" type="long">
            <generator />
        </id>
        <property column="OWNER" type="string"/>
        ...
        <joined-subclass table="CREDIT_CARD">
            <key column="CREDIT_CARD_ID" />
            <property column="TYPE"/>
            ...
        </joined-subclass>
        ...
    </class>
</hibernate-mapping>

在这种策略中是不需要识别器列(discriminator)的。<joined-subclass>元素用来映射子类。在CREDIT_CARD表中,主键是必须的,它需要外键关联到BILLING_DETAILS表的主键。<joined-subclass>也可以嵌套其他的<joined-subclass>元素,但不能包含<subclass>元素。

这种策略明显比其他策略要复杂,性能也要低得多,因为需要大量的join语句。尤其继承关系比较复杂的情况下,问题更加严重。