Hibernate.org Community Documentation

- 前のページ
- 次のページ

## 第10章 継承マッピング

## 10.1. 3つの戦略

Hibernate は3つの基本的な継承のマッピング戦略をサポートします。

- クラス階層ごとのテーブル （table-per-class-hierarchy）

- table per subclass

- 具象クラスごとのテーブル （table-per-concrete-class）

加えて4つ目に、 Hibernate はわずかに異なる性質を持ったポリモーフィズムをサポートします。

- 暗黙的ポリモーフィズム

同一の継承階層の異なるブランチに対して異なるマッピング戦略を使うことができます。その場合には全体の階層に渡るポリモーフィズムを実現するために暗黙的ポリモーフィズムを使用します。しかし、 Hibernate は同じルート `<class>` 要素内で `<subclass>` マッピング、 `<joined-subclass>` マッピング、 `<union-subclass>` マッピングの同時使用をサポートしていません。 `<subclass>` 要素と `<join>` 要素を組み合わせることで、同一 `<class>` 要素内での table-per-hierarchy 戦略と table-per-subclass 戦略の同時使用は可能です。次の例を見てください。

`subclass`、 `union-subclass` と `joined-subclass` マッピングを複数のマッピングドキュメントに定義することが出来、 `hibernate-mapping` の直下に配置します。これは新しいマッピングファイルを追加するだけで、クラス階層を拡張できるということです。あらかじめマップしたスーパークラスを指定して、サブクラスマッピングに `extends` 属性を記述しなければなりません。注記：この特徴により、以前はマッピングドキュメントの順番が重要でした。 Hibernate3 からは、 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` があるとします。階層ごとのテーブルマッピングは以下のようになります:

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

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

    >

このマッピングには4つのテーブルが必要です。3つのサブクラステーブルはスーパークラステーブルとの関連を示す主キーを持っています （実際、関係モデル上は一対一関連です）。

### 10.1.3. discriminator を用いた table-per-subclass

Hibernate の table-per-subclass 実装は、 discriminator カラムを必要としないことを覚えておいてください。 Hibernate 以外の O/R マッパーは、 table-per-subclass に異なる実装を用います。それは、スーパークラスのテーブルにタイプ discriminator カラムを必要とします。このアプローチは実装が困難になりますが、関係の視点から見ると、より正確なものです。 table-per-subclass 戦略で discriminator カラムを使いたければ、 `<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"` 宣言は、スーパークラスのクエリ実行時に外部結合を使って、サブクラスの `ChequePayment` データを取得しないように指定するためのものです。

### 10.1.4. table-per-subclass と table-per-class-hierarchy の混合

このアプローチを使用すると、 table-per-hierarchy と table-per-subclass 戦略を組み合わせる事も可能です。

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

table-per-concrete-class 戦略のマッピングに対するアプローチは、2つあります。1つ目は `<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

    >

サブクラスごとに3つのテーブルが必要です。それぞれのテーブルは、継承プロパティを含んだ、クラスの全てのプロパティに対するカラムを定義します。

The limitation of this approach is that if a property is mapped on the superclass, the column name must be the same on all subclass tables. The identity generator strategy is not allowed in union subclass inheritance. The primary key seed has to be shared across all unioned subclasses of a hierarchy.

もしスーパークラスが抽象クラスなら、 `abstract="true"` とマッピングします。もちろん、スーパークラスが抽象クラスでないなら、スーパークラスのインスタンスを保持するためのテーブルの追加が必要となります (上の例でのデフォルトは `PAYMENT` )。

### 10.1.6. 暗黙的ポリモーフィズムを用いた table-per-concrete-class

もう一つのアプローチは暗黙的ポリモーフィズムの使用です：

    <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 がポリモーフィックなクエリの実行時に SQL `UNION` を生成しない点です。

このマッピング戦略に対しては、 `Payment` へのポリモーフィックな関連は常に、 `<any>` を使ってマッピングされます。

    <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` （と CreditCardPayment のサブクラス、 `Payment` の実装であるため）、および、 `CashPayment` 、 `ChequePayment` のインスタンスを返します。 `NonelectronicTransaction` インスタンスは返しません。

## 10.2. 制限

table-per-concrete-class マッピング戦略への「暗黙的ポリモーフィズム」アプローチにはいくつかの制限があります。 `<union-subclass>` マッピングに対しても少し弱めの制限があります。

The following table shows the limitations of table per concrete-class mappings, and of implicit polymorphism, in Hibernate.

表10.1 継承マッピングの機能

継承戦略 | Polymorphic many-to-one | 一対一のポリモーフィズム | 一対多のポリモーフィズム | 多対多のポリモーフィズム | ポリモーフィックな `load()/get()` | ポリモーフィズムを使ったクエリ | ポリモーフィズムを使った結合 | Outer join fetching
--- | --- | --- | --- | --- | --- | --- | --- | ---
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
table per concrete-class (union-subclass) | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` (for `inverse="true"` only) | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
table per concrete class (implicit polymorphism) | `<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章 コンポーネントのマッピング
- 上に戻る
- ホーム
- 次のページ第11章 オブジェクトを扱う