Hibernate.org Community Documentation

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

## 第9章 コンポーネントのマッピング

コンポーネント の概念は、 Hibernate を通して様々な状況の中で異なる目的のために再利用されます。

## 9.1. 依存オブジェクト

コンポーネントは、エンティティの参照ではなく値型として永続化された、包含されたオブジェクトです。「コンポーネント」という言葉については、コンポジションというオブジェクト指向の概念を参照してください（アーキテクチャレベルのコンポーネントではありません）。例えば、以下の Person モデルのようなものです。

    public class Person {

        private java.util.Date birthday;

        private Name name;

        private String key;

        public String getKey() {

            return key;

        }

        private void setKey(String key) {

            this.key=key;

        }

        public java.util.Date getBirthday() {

            return birthday;

        }

        public void setBirthday(java.util.Date birthday) {

            this.birthday = birthday;

        }

        public Name getName() {

            return name;

        }

        public void setName(Name name) {

            this.name = name;

        }

        ......

        ......

    }

    public class Name {

        char initial;

        String first;

        String last;

        public String getFirst() {

            return first;

        }

        void setFirst(String first) {

            this.first = first;

        }

        public String getLast() {

            return last;

        }

        void setLast(String last) {

            this.last = last;

        }

        public char getInitial() {

            return initial;

        }

        void setInitial(char initial) {

            this.initial = initial;

        }

    }

いま、 `Name` は `Person` のコンポーネントとして永続化することが出来ます。ここで `Name` は永続化属性に対して getter 、 setter メソッドを定義しますが、インターフェースや識別子プロパティを定義する必要がないことに注意して下さい。

マッピング定義は以下のようになります。

    <class name="eg.Person" table="person">

        <id name="Key" column="pid" type="string">

            <generator class="uuid"/>

        </id>

        <property name="birthday" type="date"/>

        <component name="Name" class="eg.Name"

    > <!-- class attribute optional -->

            <property name="initial"/>

            <property name="first"/>

            <property name="last"/>

        </component>

    </class

    >

Person テーブルは `pid`、 `birthday`、 `initial`、 `first`、 `last` カラムを持ちます。

全ての値型のように、コンポーネントは参照の共有をすることができません。言い換えると、二人の Person は同じ名前を持つことができますが、二つの Person オブジェクトは「値が同じだけ」の別々の name オブジェクトを含んでいるということです。コンポーネントの null 値のセマンティクスは アドホック です。コンポーネントのオブジェクトを再読み込みする際、 Hibernate はコンポーネントのすべてのカラムが null であるならコンポーネント自体が null であると考えます。これは大抵の場合問題ありません。

コンポーネントの属性はどんな Hibernate の型でも構いません（コレクション、 many-to-one 関連、他のコンポーネントなど）。ネストされたコンポーネントは滅多に使わないと考えるべきでは ありません 。 Hibernate は非常にきめの細かいオブジェクトモデルをサポートするように意図されています。

`<component>` 要素は、親エンティティへ戻る参照として、コンポーネントのクラスのプロパティをマッピングする `<parent>` サブ要素を許可します。

    <class name="eg.Person" table="person">

        <id name="Key" column="pid" type="string">

            <generator class="uuid"/>

        </id>

        <property name="birthday" type="date"/>

        <component name="Name" class="eg.Name" unique="true">

            <parent name="namedPerson"/> <!-- reference back to the Person -->

            <property name="initial"/>

            <property name="first"/>

            <property name="last"/>

        </component>

    </class

    >

## 9.2. 従属するオブジェクトのコレクション

Hibernate はコンポーネントのコレクションをサポートしています（例えば `Name` 型の配列）。 `<element>` タグを `<composite-element>` タグに取り替えることによりコンポーネントコレクションを宣言してください。

    <set name="someNames" table="some_names" lazy="true">

        <key column="id"/>

        <composite-element class="eg.Name"

    > <!-- class attribute required -->

            <property name="initial"/>

            <property name="first"/>

            <property name="last"/>

        </composite-element>

    </set

    >

## 重要項目

注記: コンポジットエレメントの `Set` を定義したなら、 `equals()` と `hashCode()` を正しく実装することが重要です。

コンポジットエレメントはコレクションを含まず、コンポーネントを含むこともあります。コンポジットエレメント自身がコンポーネントを含んでいる場合は `<nested-composite-element>` を使用してください。コンポーネントのコレクション自身がコンポーネントを持つというケースはめったにありません。この段階までに、 one-to-many 関連の方がより適切でないかと熟考してください。コンポジットエレメントをエンティティとして再度モデリングしてみてください。しかしこれは Java のモデルとしては同じですが、リレーショナルモデルと永続動作はまだ若干異なることに注意してください。

もし `<set>` を使用するのであれば、コンポジットエレメントのマッピングが null 値が可能な属性をサポートしていないことに注意してください。 Hibernate はオブジェクトを削除するとき、レコードを識別するためにそれぞれのカラムの値を使用する必要があるため、 null 値を持つことが出来ません （コンポジットエレメントテーブルには別の主キーカラムはありません）。 コンポジットエレメントに not-null の属性のみを使用するか、または `<list>`、`<map>`、 `<bag>`、`<idbag>` を選択する必要があります。

コンポジットエレメントの特別なケースとして、ネストされた `<many-to-one>` 属性を持つコンポジットエレメントがあります。このマッピングは、コンポジットエレメントクラスを多対多関連テーブルの余分なカラムへマッピングします。次の例は `Order` から、`Item` への多対多関連です。 `purchaseDate`、 `price`、 `quantity` は関連の属性となります。

    <class name="eg.Order" .... >

        ....

        <set name="purchasedItems" table="purchase_items" lazy="true">

            <key column="order_id">

            <composite-element class="eg.Purchase">

                <property name="purchaseDate"/>

                <property name="price"/>

                <property name="quantity"/>

                <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->

            </composite-element>

        </set>

    </class

    >

もちろん、双方向関連のナビゲーションのために反対側から purchase への参照を作ることは出来ません。コンポーネントは値型であり、参照を共有できないことを覚えておいてください。一つの `Purchase` は一つの `Order` の set に存在できますが、同時に `Item` から参照することは出来ません。

3項関連（あるいは4項など）も可能です。

    <class name="eg.Order" .... >

        ....

        <set name="purchasedItems" table="purchase_items" lazy="true">

            <key column="order_id">

            <composite-element class="eg.OrderLine">

                <many-to-one name="purchaseDetails class="eg.Purchase"/>

                <many-to-one name="item" class="eg.Item"/>

            </composite-element>

        </set>

    </class

    >

コンポジットエレメントは他のエンティティへの関連として、同じシンタックスを使っているクエリ内で使用できます。

## 9.3. Map のインデックスとしてのコンポーネント

`<composite-map-key>` 要素は `Map` のキーとしてコンポーネントクラスをマッピングします。コンポーネントクラス上で `hashCode()` と `equals()` を正確にオーバーライドしてください。

## 9.4. 複合識別子としてのコンポーネント

コンポーネントをエンティティクラスの識別子として使うことができます。コンポーネントクラスは以下の条件を満たす必要があります。

- `java.io.Serializable` を実装しなければなりません。

- データベース上の複合キーの等価性と矛盾のないように、 `equals()` と `hashCode()` を再実装しなければなりません。

## 注記

Hibernate3 において、2番目の条件は絶対的な条件ではありません。それでもやはり条件を満たしてください。

複合キーを生成するために `IdentifierGenerator` を使用することはできません。代わりにアプリケーションが識別子を割り当てなくてはなりません。

通常の `<id>` 宣言の代わりに `<composite-id>` タグを （ネストされた `<key-property>` 属性と共に） 使います。以下の例では、 `OrderLine` クラスは `Order` の（複合）主キーに依存した主キーを持っています。

    <class name="OrderLine">

        <composite-id name="id" class="OrderLineId">

            <key-property name="lineId"/>

            <key-property name="orderId"/>

            <key-property name="customerId"/>

        </composite-id>

        <property name="name"/>

        <many-to-one name="order" class="Order"

                insert="false" update="false">

            <column name="orderId"/>

            <column name="customerId"/>

        </many-to-one>

        ....

    </class

    >

このとき、 `OrderLine` テーブルへ関連する外部キーもまた複合です。他のクラスのマッピングでこれを宣言しなければなりません。 `OrderLine` への関連は次のようにマッピングされます。

    <many-to-one name="orderLine" class="OrderLine">

    <!-- the "class" attribute is optional, as usual -->

        <column name="lineId"/>

        <column name="orderId"/>

        <column name="customerId"/>

    </many-to-one

    >

## ティップ

The `column` element is an alternative to the `column` attribute everywhere. Using the `column` element just gives more declaration options, which are mostly useful when utilizing `hbm2ddl`

`OrderLine` への `many-to-many` 関連も複合外部キーを使います。

    <set name="undeliveredOrderLines">

        <key column name="warehouseId"/>

        <many-to-many class="OrderLine">

            <column name="lineId"/>

            <column name="orderId"/>

            <column name="customerId"/>

        </many-to-many>

    </set

    >

`Order` にある `OrderLine` のコレクションは次のものを使用します。

    <set name="orderLines" inverse="true">

        <key>

            <column name="orderId"/>

            <column name="customerId"/>

        </key>

        <one-to-many class="OrderLine"/>

    </set

    >

（`<one-to-many>` 属性は、例によってカラムを宣言しません）

`OrderLine` 自身がコレクションを持っている場合、同時に複合外部キーも持っています。

    <class name="OrderLine">

        ....

        ....

        <list name="deliveryAttempts">

            <key

    >   <!-- a collection inherits the composite key type -->

                <column name="lineId"/>

                <column name="orderId"/>

                <column name="customerId"/>

            </key>

            <list-index column="attemptId" base="1"/>

            <composite-element class="DeliveryAttempt">

                ...

            </composite-element>

        </set>

    </class

    >

## 9.5. 動的コンポーネント

`Map` 型のプロパティのマッピングも可能です。

    <dynamic-component name="userAttributes">

        <property name="foo" column="FOO" type="string"/>

        <property name="bar" column="BAR" type="integer"/>

        <many-to-one name="baz" class="Baz" column="BAZ_ID"/>

    </dynamic-component

    >

`<dynamic-component>` マッピングのセマンティクスは `<component>` と同一のものです。この種のマッピングの利点は、マッピングドキュメントの編集により、配置時に Bean の属性を決定できる点です。また、 DOM パーサを利用して、マッピングドキュメントのランタイム操作が可能です。さらに、 `Configuration` オブジェクト経由で Hibernate のコンフィグレーション時のメタモデルにアクセス（または変更）が可能です。

製作著作 © 2004 Red Hat, Inc.

- 前のページ第8章 関連マッピング
- 上に戻る
- ホーム
- 次のページ第10章 継承マッピング