NHibernate官方文档——第八章 继承映射(Inheritance Mapping)

本文翻译自NHibernate官方文档NHibernate Reference Documentation 4.1

受限于个人知识水平,有些地方翻译可能不准确,但是我还是希望我的这些微薄的努力能为他人提供帮助。

侵删。

关于继承映射的三个继承映射方案

NHibernate支持三个基本的继承映射隐射方案。

    • 一个继承一张表(table per class hierarchy)

    • 一个子类额外信息一张表(table per subclass)

    • 一个子类全部信息一张表(table per concrete class)

NHibernate还支持第四种,比较特殊的一种多态:

    • 隐式多态(implicit polymorphism)

针对不同的继承结构,我们可以使用不同的映射方案,然后使用隐式多态去获得整个继承关系中的多态属性。然而NHibernate不支持在一个<class>标签下面混合使用<subclass>,<joined-subclass>和<union-subclass>三种映射。然而,通过使用<subclass>和<join>标签,NHibernate支持将一个继承一张表(table per hierarchy )和一个子类额外信息一张表(table per subclass)两种映射方案混合在同一个<class>标签下面使用(见下文)。

NHibernate也支持将subclass,union-subclass和joined-subclass的mapping分散在不同文件中,我们可以直接在各个文件的<hibernate-mapping>标签中定义类的mapping。这样就可以让你能够通过增加mapping文件来扩展整个类的层级结构。当然你必须在子类的mapping文件中设置extends属性来指向它的父类。

 <hibernate-mapping>
     <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
          <property name="name" type="string"/>
     </subclass>
 </hibernate-mapping>

一个继承一张表(table per class hierarchy)

假设我们有一个IPayment接口,有CreditCardPayment, CashPayment,ChequePayment三种实现方式。这种一个继承一张表的配置是这样:

<class name="IPayment" table="PAYMENT">
    <id name="Id" type="Int64" column="PAYMENT_ID">
        <generator class="native"/>
    </id>
    <discriminator column="PAYMENT_TYPE" type="String"/>
    <property name="Amount" column="AMOUNT"/>
    ...
    <subclass name="CreditCardPayment" discriminator-value="CREDIT">
        ...
    </subclass>
    <subclass name="CashPayment" discriminator-value="CASH">
        ...
    </subclass>
    <subclass name="ChequePayment" discriminator-value="CHEQUE">
        ...
    </subclass>
</class>

只需要一张表就可以了,但是这种方式有一个局限性:子类对应的列不能有NOT NULL的限制。

一个子类额外信息一张表(table per subclass)

一个一个子类额外信息一张表的配置是这样:

<class name="IPayment" table="PAYMENT">
    <id name="Id" type="Int64" column="PAYMENT_ID">
        <generator class="native"/>
    </id>
    <property name="Amount" column="AMOUNT"/>
    ...
    <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
        <key column="PAYMENT_ID"/>
        ...
    </joined-subclass>
    <joined-subclass name="CashPayment" table="CASH_PAYMENT">
        <key column="PAYMENT_ID"/>
        ...
    </joined-subclass>
    <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
        <key column="PAYMENT_ID"/>
        ...
    </joined-subclass>
</class>

这种配置方式需要四张表。三张字表的主键和父表关联(他们之间的关系实际上是一对一关系)。

一个子类全部信息一张表(table per concrete class),使用一个区分字段

我们注意到,在NHibernate中,一个子类额外信息一张表(table per subclass)的实现方式不需要区分字段,其他的实体/关系映射工具使用了一种不同的实现方式:他们的父表设置了一个区分类型的字段。NHibernate使用了一种更麻烦但是关系描述角度来看更加准确的的实现方式。如果你想在一个子类额外信息一张表的方式上实现区分字段,你需要把<subclass>和<join>标签结合起来使用如下:

<class name="Payment" table="PAYMENT">
    <id name="Id" type="Int64" column="PAYMENT_ID">
        <generator class="native"/>
    </id>
    <discriminator column="PAYMENT_TYPE" type="string"/>
    <property name="Amount" column="AMOUNT"/>
    ...
    <subclass name="CreditCardPayment" discriminator-value="CREDIT">
        <join table="CREDIT_PAYMENT">
            <key column="PAYMENT_ID"/>
            <property name="CreditCardType" column="CCTYPE"/>
            ...
        </join>
    </subclass>
    <subclass name="CashPayment" discriminator-value="CASH">
        <join table="CASH_PAYMENT">
            <key column="PAYMENT_ID"/>
            ...
        </join>
    </subclass>
    <subclass name="ChequePayment" discriminator-value="CHEQUE">
        <join table="CHEQUE_PAYMENT" fetch="select">
            <key column="PAYMENT_ID"/>
            ...
        </join>
    </subclass>
</class>

可选字段fetch="select"的声明告诉NHibernate在查询的时候不要使用outer join获得ChequePayment的子类。

父类信息和子类信息混合在同一张表内(Mixing table per class hierarchy with table per subclass)

你甚至可以将一个继承一张表(table per class hierarchy)和一个子类额外信息一张表(table per subclass)两种方式结合:

<class name="Payment" table="PAYMENT">
    <id name="Id" type="Int64" column="PAYMENT_ID">
        <generator class="native"/>
    </id>
    <discriminator column="PAYMENT_TYPE" type="string"/>
    <property name="Amount" column="AMOUNT"/>
    ...
    <subclass name="CreditCardPayment" discriminator-value="CREDIT">
        <join table="CREDIT_PAYMENT">
            <property name="CreditCardType" column="CCTYPE"/>
            ...
        </join>
    </subclass>
    <subclass name="CashPayment" discriminator-value="CASH">
        ...
    </subclass>
    <subclass name="ChequePayment" discriminator-value="CHEQUE">
        ...
    </subclass>
</class>

对于这些mapping的方式,要使用<many-to-one>映射一个IPayment的多态连接。

<many-to-one name="Payment" column="PAYMENT" class="IPayment"/>

一个子类全部信息一张表(table per concrete class)

有两种方式可以实现,第一种方式是使用<union-subclass>

<class name="Payment">
    <id name="Id" type="Int64" column="PAYMENT_ID">
        <generator class="sequence"/>
    </id>
    <property name="Amount" column="AMOUNT"/>
    ...
    <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
        <property name="CreditCardType" column="CCTYPE"/>
        ...
    </union-subclass>
    <union-subclass name="CashPayment" table="CASH_PAYMENT">
        ...
    </union-subclass>
    <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
        ...
    </union-subclass>
</class>

三个类分别表示三个表,每个表都定义了类的所有属性,也包括继承过来的属性。

这种方式的缺点在于如果属性继承自父类,那么列名在子类中必须完全一样。(我们可能在将来的版本中解决这个问题)序列的生成方式不能独立,主键种子(primary key seed)不许在所有union的子类中共用。

如果你的父类是abstract,就需要设置abstract="true"。当然,如果它不是abstract的,那么久需要一个额外的表(就像上面的一个继承一张表例子那样)来装父类的实例。

一个子类全部信息一张表,使用隐式多态(Table per concrete class, using implicit polymorphism)

另外的一个方式是使用隐式多态:

<class name="CreditCardPayment" table="CREDIT_PAYMENT">
    <id name="Id" type="Int64" column="CREDIT_PAYMENT_ID">
        <generator class="native"/>
    </id>
    <property name="Amount" column="CREDIT_AMOUNT"/>
    ...
</class>

<class name="CashPayment" table="CASH_PAYMENT">
    <id name="Id" type="Int64" column="CASH_PAYMENT_ID">
        <generator class="native"/>
    </id>
    <property name="Amount" column="CASH_AMOUNT"/>
    ...
</class>

<class name="ChequePayment" table="CHEQUE_PAYMENT">
    <id name="Id" type="Int64" column="CHEQUE_PAYMENT_ID">
        <generator class="native"/>
    </id>
    <property name="Amount" column="CHEQUE_AMOUNT"/>
    ...
</class>

我们可以注意到,我们不必要显示地声明IPayment接口。同时,我们也可以发现IPayment接口被映射到了在各个具体实现类上面了。如果你想要避免重复,可以考虑使用XML实体(例如,在mapping文件的DOCTYPE声明和&allproperties中[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] )

这种方式的缺点在于NHibernate在执行多态查询的时候不会产生SQL Union语句。

<any name="Payment" meta-type="string" id-type="Int64">
    <meta-value value="CREDIT" class="CreditCardPayment"/>
    <meta-value value="CASH" class="CashPayment"/>
    <meta-value value="CHEQUE" class="ChequePayment"/>
    <column name="PAYMENT_CLASS"/>
    <column name="PAYMENT_ID"/>
</any>

隐射多态混合其他的继承映射(Mixing implicit polymorphism with other inheritance mappings)

关于隐式多态还有一个需要注意的地方。因为子类的映射在各自的<class>标签之中配置(因为IPayment只是一个接口),每一个子类又有可能是其他的table-per-class或者table-per-subclass继承结构中的一员!(当然你可以继续使用IPayment的多态查询)

<class name="CreditCardPayment" table="CREDIT_PAYMENT">
    <id name="Id" type="Int64" column="CREDIT_PAYMENT_ID">
        <generator class="native"/>
    </id>
    <discriminator column="CREDIT_CARD" type="String"/>
    <property name="Amount" column="CREDIT_AMOUNT"/>
    ...
    <subclass name="MasterCardPayment" discriminator-value="MDC"/>
    <subclass name="VisaPayment" discriminator-value="VISA"/>
</class>

<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
    <id name="Id" type="Int64" column="TXN_ID">
        <generator class="native"/>
    </id>
    ...
    <joined-subclass name="CashPayment" table="CASH_PAYMENT">
        <key column="PAYMENT_ID"/>
        <property name="Amount" column="CASH_AMOUNT"/>
        ...
    </joined-subclass>
    <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
        <key column="PAYMENT_ID"/>
        <property name="Amount" column="CHEQUE_AMOUNT"/>
        ...
    </joined-subclass>
</class>

和上面的例子一样,我们仍然不需要显示声明IPayment接口。如果我们查询了一个IPayment接口的一个实例——例如,from IPayment——NHibernate就会自动返回CreditCardPayment(和它的子类,因为他们也实现了IPayment接口),CashPayment和ChequePayment的实例,但是不会返回NonelectronicTransaction的实例。

局限性

对于实现“隐式多态”的“一个子类全部信息一张表”方式,存在一些局限。如果使用<union-subclass>映射方式相对会少一些限制。

以下表格展示了NHibernate中一个子类全部信息一张表和隐式多态的映射方式的限制情况。

表格 继承映射的一些特性

继承类型 多对一的多态 一对一的多态 一对多的多态 多对多的多态 多态的load()/get() 多态查询 多态连接 外连接fetching的支持情况
一个继承一张表(table per class-hierarchy) <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.Get<IPayment>(id) from IPayment p from Order o join o.Payment p supported
一个子类额外信息一张表(table per subclass) <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.Get<IPayment>(id) from IPayment p from Order o join o.Payment p supported
一个子类全部信息一张表(table per concrete-class (union-subclass)) <many-to-one> <one-to-one> <one-to-many>(for inverse="true"only) <many-to-many> s.Get<IPayment>(id) from IPayment p from Order o join o.Payment p supported
一个子类全部信息一张表(table per concrete class (implicit polymorphism)) <any>

not supported

not supported <many-to-many> use a query from IPayment p


not supported

not supported
posted @ 2016-09-09 17:23  balavatasky  阅读(593)  评论(0编辑  收藏  举报