Hibernate.orgCommunity Documentation

第10章 継承マッピング

10.1. 3つの戦略
10.1.1. クラス階層ごとのテーブル(table-per-class-hierarchy)
10.1.2. サブクラスごとのテーブル (table-per-subclass)
10.1.3. discriminator を用いた table-per-subclass
10.1.4. table-per-subclass と table-per-class-hierarchy の混合
10.1.5. 具象クラスごとのテーブル(table-per-concrete-class)
10.1.6. 暗黙的ポリモーフィズムを用いた table-per-concrete-class
10.1.7. 他の継承マッピングと暗黙的ポリモーフィズムの組み合わせ
10.2. 制限

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

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

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

subclassunion-subclassjoined-subclass マッピングを複数のマッピングドキュメントに定義することが出来、 hibernate-mapping の直下に配置します。これは新しいマッピングファイルを追加するだけで、クラス階層を拡張できるということです。あらかじめマップしたスーパークラスを指定して、サブクラスマッピングに extends 属性を記述しなければなりません。注記:この特徴により、以前はマッピングドキュメントの順番が重要でした。 Hibernate3 からは、 extends キーワードを使う場合、マッピングドキュメントの順番は問題になりません。1つのマッピングファイル内で順番付けを行うときは、依然として、サブクラスを定義する前にスーパークラスを定義する必要があります。)



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

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 データを取得しないように指定するためのものです。

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 )。

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


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

このマッピングについての更なる注意点があります。サブクラスが自身を <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 の実装であるため)、および、 CashPaymentChequePayment のインスタンスを返します。 NonelectronicTransaction インスタンスは返しません。

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

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