hibernate-orm/reference/zh-cn/modules/inheritance_mapping.xml

448 lines
18 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<chapter id="inheritance">
<title>继承映射(Inheritance Mappings)</title>
<sect1 id="inheritance-strategies" revision="2">
<title> 三种策略</title>
<para>
Hibernate支持三种基本的继承映射策略
</para>
<itemizedlist>
<listitem>
<para>
每个类分层结构一张表(table per class hierarchy)
</para>
</listitem>
<listitem>
<para>
每个子类一张表(table per subclass)
</para>
</listitem>
<listitem>
<para>
每个具体类一张表(table per concrete class)
</para>
</listitem>
</itemizedlist>
<para>
此外Hibernate还支持第四种稍有不同的多态映射策略
</para>
<itemizedlist>
<listitem>
<para>
隐式多态(implicit polymorphism)
</para>
</listitem>
</itemizedlist>
<para>
对于同一个继承层次内的不同分支,可以采用不同的映射策略,然后用隐式多
态来完成跨越整个层次的多态。但是在同一个<literal>&lt;class&gt;</literal>根元素
Hibernate不支持混合了元素<literal>&lt;subclass&gt;</literal>
<literal>&lt;joined-subclass&gt;</literal><literal>&lt;union-subclass&gt;</literal>
的映射。在同一个<literal>&lt;class&gt;</literal>元素下,可以混合使用
“每个类分层结构一张表”table per hierarchy 和“每个子类一张表”table per subclass
这两种映射策略,这是通过结合元素<literal>&lt;subclass&gt;</literal>
<literal>&lt;join&gt;</literal>来实现的(见后)。
</para>
<sect2 id="inheritance-tableperclass" >
<title>每个类分层结构一张表(Table per class hierarchy)</title>
<para>
假设我们有接口<literal>Payment</literal>和它的几个实现类:
<literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
<literal>ChequePayment</literal>。则“每个类分层结构一张表”(Table per
class hierarchy)的映射代码如下所示:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>]]></programlisting>
<para>
采用这种策略只需要一张表即可。它有一个很大的限制:要求那些由子类定义的字段,
<literal>CCTYPE</literal>,不能有<literal>非空(NOT NULL)</literal>约束。
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>每个子类一张表(Table per subclass)</title>
<para>
对于上例中的几个类而言,采用“每个子类一张表”的映射策略,代码如下所示:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" 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"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
</class>]]></programlisting>
<para>
需要四张表。三个子类表通过主键关联到超类表(因而关系模型实际上是一对一关联)。
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator">
<title>每个子类一张表(Table per subclass),使用辨别标志(Discriminator)</title>
<para>
注意对“每个子类一张表”的映射策略Hibernate的实现不需要辨别字段而其他
的对象/关系映射工具使用了一种不同于Hibernate的实现方法该方法要求在超类
表中有一个类型辨别字段(type discriminator column)。Hibernate采用的方法更
难实现,但从关系(数据库)这点上来看,按理说它更正确。若你愿意使用带有辨别字
段的“每个子类一张表”的策略,你可以结合使用<literal>&lt;subclass&gt;</literal>
<literal>&lt;join&gt;</literal>,如下所示:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" 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">
<join table="CASH_PAYMENT">
...
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
<join table="CHEQUE_PAYMENT" fetch="select">
...
</join>
</subclass>
</class>]]></programlisting>
<para>
可选的声明<literal>fetch="select"</literal>是用来告诉Hibernate在查询超类时
不要使用外部连接(outer join)来抓取子类<literal>ChequePayment</literal>的数据。
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>混合使用“每个类分层结构一张表”和“每个子类一张表”</title>
<para>
你甚至可以采取如下方法混和使用“每个类分层结构一张表”和“每个子类一张表”这两种策略:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
<id name="id" type="long" 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>]]></programlisting>
<para>
对上述任何一种映射策略而言,指向根类<literal>Payment</literal>
关联是使用<literal>&lt;many-to-one&gt;</literal>进行映射的。
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="1">
<title>每个具体类一张表(Table per concrete class)</title>
<para>
对于“每个具体类一张表”的映射策略,可以采用两种方法。第一种方法是使用
<literal>&lt;union-subclass&gt;</literal>
</para>
<programlisting><![CDATA[<class name="Payment">
<id name="id" type="long" 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>]]></programlisting>
<para>
这里涉及三张表。每张表为对应类的所有属性(包括从超类继承的属性)定义相应字段。
</para>
<para>
这种方式的局限在于,如果一个属性在超类中做了映射,其字段名必须与所有子类
表中定义的相同。(我们可能会在Hibernate的后续发布版本中放宽此限制。)
不允许在联合子类(union subclass)的继承层次中使用标识生成器策略(identity generator strategy),
实际上, 主键的种子(primary key seed)不得不为同一继承层次中的全部被联合子类所共用.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Table per concrete class, using implicit polymorphism</title>
<title>每个具体类一张表,使用隐式多态</title>
<para>
另一种可供选择的方法是采用隐式多态:
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" 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="long" 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="long" column="CHEQUE_PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</class>]]></programlisting>
<para>
注意,我们没有在任何地方明确的提及接口<literal>Payment</literal>。同时注意
<literal>Payment</literal>的属性在每个子类中都进行了映射。如果你想避免重复,
可以考虑使用XML实体(例如:位于<literal>DOCTYPE</literal>声明内的
<literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
和映射中的<literal>&amp;allproperties;</literal>)。
</para>
<para>
这种方法的缺陷在于在Hibernate执行多态查询时(polymorphic queries)无法生成带
<literal>UNION</literal>的SQL语句。
</para>
<para>
对于这种映射策略而言,通常用<literal>&lt;any&gt;</literal>来实现到
<literal>Payment</literal>的多态关联映射。
</para>
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
<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>]]></programlisting>
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>隐式多态和其他继承映射混合使用</title>
<para>
对这一映射还有一点需要注意。因为每个子类都在各自独立的元素<literal>&lt;class&gt;</literal>
中映射(并且<literal>Payment</literal>只是一个接口),每个子类可以很容易的成为另一
个继承体系中的一部分!(你仍然可以对接口<literal>Payment</literal>使用多态查询。)
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" 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="long" 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>]]></programlisting>
<para>
我们还是没有明确的提到<literal>Payment</literal>
如果我们针对接口<literal>Payment</literal>执行查询
——如<literal>from Payment</literal>—— Hibernate
自动返回<literal>CreditCardPayment</literal>(和它的子类,因为
它们也实现了接口<literal>Payment</literal>)、
<literal>CashPayment</literal><literal>Chequepayment</literal>的实例,
但不返回<literal>NonelectronicTransaction</literal>的实例。
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>限制</title>
<para>
对“每个具体类映射一张表”table per concrete-class的映射策略而言隐式多态的
方式有一定的限制。而<literal>&lt;union-subclass&gt;</literal>映射的限制则没有那
么严格。
</para>
<para>
下面表格中列出了在Hibernte中“每个具体类一张表”的策略和隐式多态的限制。
</para>
<table frame="topbot">
<title>继承映射特性(Features of inheritance mappings) </title>
<tgroup cols='8' align='left' colsep='1' rowsep='1'>
<colspec colname='c1' colwidth="1*"/>
<colspec colname='c2' colwidth="1*"/>
<colspec colname='c3' colwidth="1*"/>
<colspec colname='c4' colwidth="1*"/>
<colspec colname='c5' colwidth="1*"/>
<colspec colname='c6' colwidth="1*"/>
<colspec colname='c7' colwidth="1*"/>
<colspec colname='c8' colwidth="1*"/>
<thead>
<row>
<entry>继承策略(Inheritance strategy)</entry>
<entry>多态多对一</entry>
<entry>多态一对一</entry>
<entry>多态一对多</entry>
<entry>多态多对多</entry>
<entry>多态 <literal>load()/get()</literal></entry>
<entry>多态查询</entry>
<entry>多态连接(join)</entry>
<entry>外连接(Outer join)抓取</entry>
</row>
</thead>
<tbody>
<row>
<entry>每个类分层结构一张表</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>支持</emphasis></entry>
</row>
<row>
<entry>每个子类一张表</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>支持</emphasis></entry>
</row>
<row>
<entry>每个具体类一张表(union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (仅对于<literal>inverse="true"</literal>的情况)</entry>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>支持</emphasis></entry>
</row>
<row>
<entry>每个具体类一张表(隐式多态)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>不支持</emphasis></entry>
<entry><emphasis>不支持</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>不支持</emphasis></entry>
<entry><emphasis>不支持</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>