Hibernate.org Community Documentation

- 上一页
- 下一页

## 第 10 章 继承映射（Inheritance Mapping）

## 10.1. 三种策略

Hibernate 支持三种基本的继承映射策略：

- 每个类分层结构一张表（table per class hierarchy）

- table per subclass

- 每个具体类一张表（table per concrete class）

此外，Hibernate 还支持第四种稍有不同的多态映射策略：

- 隐式多态（implicit polymorphism）

对于同一个继承层次内的不同分支，可以采用不同的映射策略，然后用隐式多 态来完成跨越整个层次的多态。但是在同一个 `<class>` 根元素下，Hibernate 不支持混合了元素 `<subclass>`、`<joined-subclass>` 和 `<union-subclass>` 的映射。在同一个 `<class>` 元素下，可以混合使用“每个类分层结构一张表”（table per hierarchy）和“每个子类一张表”（table per subclass） 这两种映射策略，这是通过结合元素 `<subclass>` 和 `<join>` 来实现的（见后）。

在多个映射文件中，可以直接在 `hibernate-mapping` 根下定义 `subclass`，`union-subclass` 和 `joined-subclass`。也就是说，你可以仅加入一个新的映射文件来扩展类层次。你必须在 subclass 的映射中指明 `extends` 属性，给出一个之前定义的超类的名字。注意，在以前，这一功能对映射文件的顺序有严格的要求，从 Hibernate 3 开始，使用 extends 关键字的时侯，对映射文件的顺序不再有要求；但在每个映射文件里，超类必须在子类之前定义。

     <hibernate-mapping>

         <subclass name="DomesticCat" extends="Cat" discriminator-value="D">

              <property name="name" type="string"/>

         </subclass>

     </hibernate-mapping

    >

### 10.1.1. 每个类分层结构一张表（Table per class hierarchy）

假设我们有接口`Payment`和它的几个实现类： `CreditCardPayment`, `CashPayment` 和`ChequePayment`。则“每个类分层结构一张表”（Table per class hierarchy）的映射代码如下所示：

    <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

    >

采用这种策略只需要一张表即可。它有一个很大的限制：要求那些由子类定义的字段， 如 `CCTYPE`，不能有`非空（NOT NULL）`约束。

### 10.1.2. 每个子类一张表（Table per subclass）

对于上例中的几个类而言，采用“每个子类一张表”的映射策略，代码如下所示：

    <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"/>

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </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

    >

需要四张表。三个子类表通过主键关联到超类表（因而关系模型实际上是一对一关联）。

### 10.1.3. 每个子类一张表（Table per subclass），使用辨别标志（Discriminator）

注意，对“每个子类一张表”的映射策略，Hibernate 的实现不需要辨别字段，而其他的对象／关系映射工具使用了一种不同于Hibernate的实现方法，该方法要求在超类表中有一个类型辨别字段（type discriminator column）。Hibernate 采用的方法更难实现，但从关系（数据库）的角度来看，按理说它更正确。若你愿意使用带有辨别字段的“每个子类一张表”的策略，你可以结合使用 `<subclass>` 与`<join>`，如下所示：

    <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">

                <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"`，是用来告诉 Hibernate，在查询超类时，不要使用外部连接（outer join）来抓取子类 `ChequePayment` 的数据。

### 10.1.4. 混合使用“每个类分层结构一张表”和“每个子类一张表”

你甚至可以采取如下方法混和使用“每个类分层结构一张表”和“每个子类一张表”这两种策略：

    <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

    >

对上述任何一种映射策略而言，指向根类 `Payment` 的关联是使用 `<many-to-one>` 进行映射的。

    <many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

### 10.1.5. 每个具体类一张表（Table per concrete class）

对于“每个具体类一张表”的映射策略，可以采用两种方法。第一种方法是使用 `<union-subclass>`。

    <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

    >

这里涉及三张与子类相关的表。每张表为对应类的所有属性（包括从超类继承的属性）定义相应字段。

这种方式的局限在于，如果一个属性在超类中做了映射，其字段名必须与所有子类表中定义的相同。（我们可能会在 Hibernate 的后续发布版本中放宽此限制。）不允许在联合子类（union subclass）的继承层次中使用标识生成器策略（identity generator strategy），实际上，主键的种子（primary key seed）不得不为同一继承层次中的全部被联合子类所共用。

假若超类是抽象类，请使用 `abstract="true"`。当然，假若它不是抽象的，需要一个额外的表（上面的例子中，默认是 `PAYMENT`），来保存超类的实例。

### 10.1.6. 每个具体类一张表，使用隐式多态

另一种可供选择的方法是采用隐式多态：

    <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

    >

请注意，这里没有显性地提及 `Payment` 接口。`Payment` 的属性映射到每个子类。如果你想避免重复，请考虑使用 XML 实体（如：`DOCTYPE` 声明里的 `[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]` 和映射里的 `&allproperties;`）。

这种方法的缺陷在于，在 Hibernate 执行多态查询时（polymorphic queries）无法生成带 `UNION` 的 SQL 语句。

对于这种映射策略而言，通常用 `<any>` 来实现到 `Payment` 的多态关联映射。

    <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

    >

### 10.1.7. 隐式多态和其他继承映射混合使用

对这一映射还有一点需要注意。因为每个子类都在各自独立的元素 `<class>` 中映射（并且 `Payment` 只是一个接口），每个子类可以很容易的成为另一个继承体系中的一部分！（你仍然可以对接口 `Payment` 使用多态查询。）

    <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

    >

我们还是没有明确的提到 `Payment`。如果我们针对接口 `Payment` 执行查询 — 如 `from Payment` — Hibernate 自动返回 `CreditCardPayment`（和它的子类，因为 它们也实现了接口 `Payment`）、`CashPayment` 和 `Chequepayment` 的实例，但不返回 `NonelectronicTransaction` 的实例。

## 10.2. 限制

对“每个具体类映射一张表”（table per concrete-class）的映射策略而言，隐式多态的方式有一定的限制。而 `<union-subclass>` 映射的限制则没有那么严格。

下面表格中列出了在 Hibernte 中“每个具体类一张表”的策略和隐式多态的限制。

表 10.1. 继承映射特性（Features of inheritance mappings）

继承策略（Inheritance strategy） | 多态多对多 | 多态一对一 | 多态一对多 | 多态多对多 | Polymorphic `load()/get()` | 多态查询 | 多态连接（join） | 支持外连接（Outer join）读取。
--- | --- | --- | --- | --- | --- | --- | --- | ---
每个类分层结构一张表（table per class hierarchy） | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment 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(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
每个具体类一张表（union-subclass） | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` (仅适用于 `inverse="true"`) | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
每个具体类一张表（隐式多态） | `<any>` | not supported | not supported | `<many-to-any>` | `s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()` | `from Payment p` | not supported | not supported

版权 © 2004 Red Hat, Inc.

- 上一页第 9 章 组件（Component）映射
- 上一级
- 起始页
- 下一页第 11 章 与对象共事