Hibernate.orgCommunity Documentation
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 キーワードを使う場合、マッピングドキュメントの順番は問題になりません。1つのマッピングファイル内で順番付けを行うときは、依然として、サブクラスを定義する前にスーパークラスを定義する必要があります。)
<hibernate-mapping>
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</hibernate-mapping
>
例えば、インターフェース 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
制約を持てません。
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つのサブクラステーブルはスーパークラステーブルとの関連を示す主キーを持っています (実際、関係モデル上は一対一関連です)。
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-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"/>
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
の実装であるため)、および、 CashPayment
、 ChequePayment
のインスタンスを返します。 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.
表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.