第9章 継承マッピング


9.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 戦略の同時使用は可能です(次の例を見てください)。
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>
Copy to Clipboard Toggle word wrap

9.1.1. クラス階層ごとのテーブル(table-per-class-hierarchy)

例えば、インターフェース Payment と、それを実装した CreditCardPaymentCashPaymentChequePayment があるとします。階層ごとのテーブルマッピングは以下のように表示されます:
<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>
Copy to Clipboard Toggle word wrap
ちょうど一つのテーブルが必要です。このマッピング戦略には制限が1つあります。CCTYPE のような、サブクラスで宣言されたカラムは NOT NULL 制約を持つことができません。

9.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>
Copy to Clipboard Toggle word wrap
4つのテーブルが必要です。3つのサブクラステーブルはスーパークラステーブルとの関連を示す主キーを持っており、実際、関係モデル上は一対一関連です。

9.1.3. 弁別子 を用いた 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>
Copy to Clipboard Toggle word wrap
オプションの fetch="select" 宣言は、スーパークラスのクエリ実行時に外部結合を使って、サブクラスの ChequePayment データを取得しないように指定するためのものです。

9.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>
Copy to Clipboard Toggle word wrap
いずれのマッピング戦略であっても、ルートである Payment クラスへのポリモーフィックな関連は <many-to-one> を使ってマッピングします。
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
Copy to Clipboard Toggle word wrap

9.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>
Copy to Clipboard Toggle word wrap
サブクラスごとに3つのテーブルが必要です。それぞれのテーブルは、継承プロパティを含んだ、クラスの全てのプロパティに対するカラムを定義します。
このアプローチにおける制限は、プロパティがスーパークラスにマッピングされていた場合、全てのサブクラスにおいてカラム名が同じでなければならないというものです。union subclass 継承では識別子生成戦略を使用できません。主キーを生成するためのシードは、全ての union subclass の階層内で共有する必要があるからです。
スーパークラスが抽象的であれば、abstract="true" とマッピングします。もちろん、スーパークラスが抽象的でないなら、スーパークラスのインスタンスを保持するために、テーブルの追加が必要となります (上の例でのデフォルトは PAYMENT )。

9.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>
Copy to Clipboard Toggle word wrap
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>
Copy to Clipboard Toggle word wrap

9.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>
Copy to Clipboard Toggle word wrap
もう一度述べますが、Payment は明示的に記載されません。Payment インターフェースに対してクエリを実行する場合、 例えば from Payment などから、 Hibernate は自動的に CreditCardPayment (と Payment の実装であるためCreditCardPayment のサブクラス)、および、CashPaymentChequePayment のインスタンスを返します。しかし、NonelectronicTransaction インスタンスは返しません。
トップに戻る
Red Hat logoGithubredditYoutubeTwitter

詳細情報

試用、購入および販売

コミュニティー

Red Hat ドキュメントについて

Red Hat をお使いのお客様が、信頼できるコンテンツが含まれている製品やサービスを活用することで、イノベーションを行い、目標を達成できるようにします。 最新の更新を見る.

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。このような変更は、段階的に実施される予定です。詳細情報: Red Hat ブログ.

会社概要

Red Hat は、企業がコアとなるデータセンターからネットワークエッジに至るまで、各種プラットフォームや環境全体で作業を簡素化できるように、強化されたソリューションを提供しています。

Theme

© 2025 Red Hat