第6章 コレクションのマッピング

6.1. コレクションの永続化

コレクション型のフィールドを永続化するには、 そのコレクション型がインターフェイス型である必要があります。 例えば、

public class Product {
    private String serialNumber;
    private Set parts = new HashSet();
    
    public Set getParts() { return parts; }
    void setParts(Set parts) { this.parts = parts; }
    public String getSerialNumber() { return serialNumber; }
    void setSerialNumber(String sn) { serialNumber = sn; }
}

実在するインターフェイスには java.util.Setjava.util.Collectionjava.util.Listjava.util.Mapjava.util.SortedSetjava.util.SortedMap などがあります。 または、任意のインターフェイスが使えます! (ただし、任意のインターフェイスを使用する場合は、 org.hibernate.usertype.UserCollectionType の実装クラスを作成する必要があります。)

HashSet のインスタンスを持つインスタンス変数が どのように初期化されるかに注目してみましょう。 これは新たに生成された(永続化されていない)コレクション型のプロパティを 初期化する最適な方法です。 (例えば persist() により)インスタンスを永続化しようとしたとき、 Hibernateは HashSet をHibernate独自の Set の実装クラスに置き換えます。 このため、次のようなエラーには注意が必要です。

Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
kittens = cat.getKittens(); // Okay, kittens collection is a Set
(HashSet) cat.getKittens(); // Error!

Hibernateにより注入された永続性コレクションは、インターフェイス型に応じて、 HashMapHashSetTreeMapTreeSetArrayList のように振舞います。

コレクションインスタンスは、値型として普通に振舞います。 永続化オブジェクトに参照されたときに自動的に永続化され、 参照がなくなったときに自動的に削除されます。 もしある永続化オブジェクトから別の永続化オブジェクトに渡されたら、 その要素は現在のテーブルから別のテーブルに移動するかもしれません。 2つのエンティティが同じコレクションインスタンスを共有してはいけません。 リレーショナルモデルをベースにしているため、コレクション型のプロパティに null値を代入しても意味がありません。 つまりHibernateは参照先のないコレクションと空のコレクションを区別しません。

しかしそれほど心配しなくても構いません。 普段使っているJavaのコレクションと同じように、永続化コレクションを使ってください。 双方向関連の意味を理解すればよいのです(これは後ほど説明します)。

6.2. コレクションのマッピング

コレクションをマッピングするためのマッピング要素は、インターフェイスの型に依存します。 例えば、<set> 要素は Set 型を マッピングするために使います。

<class name="Product">
    <id name="serialNumber" column="productSerialNumber"/>
    <set name="parts">
        <key column="productSerialNumber" not-null="true"/>
        <one-to-many class="Part"/>
    </set>
</class>

マッピング要素には <set> の他に <list><map><bag><array><primitive-array> があります。 代表として、<map> 要素を下記に示します。

<map
    name="propertyName"                                         (1)
    table="table_name"                                          (2)
    schema="schema_name"                                        (3)
    lazy="true|extra|false"                                     (4)
    inverse="true|false"                                        (5)
    cascade="all|none|save-update|delete|all-delete-orphan|delet(6)e-orphan"
    sort="unsorted|natural|comparatorClass"                     (7)
    order-by="column_name asc|desc"                             (8)
    where="arbitrary sql where condition"                       (9)
    fetch="join|select|subselect"                               (10)
    batch-size="N"                                              (11)
    access="field|property|ClassName"                           (12)
    optimistic-lock="true|false"                                (13)
    mutable="true|false"                                        (14)
    node="element-name|."
    embed-xml="true|false"
>

    <key .... />
    <map-key .... />
    <element .... />
</map>
(1)

name コレクション型であるプロパティの名前

(2)

table (オプション - デフォルトはプロパティ名)コレクションテーブルの名前 (一対多関連では使用しません)。

(3)

schema (オプション)テーブルスキーマの名前。 ルート要素で宣言されているスキーマより優先されます。

(4)

lazy (オプション - デフォルトは true) 遅延フェッチを無効にし、関連を常に即時にフェッチにするために使用します。 または、「extra-lazy」フェッチを有効にするために使用します。 「extra-lazy」フェッチは、ほとんどの操作ではコレクションを初期化しません (非常に大きなコレクションに適しています)。

(5)

inverse (オプション - デフォルトは false) このコレクションが双方向関連の「逆」側であるとマークします。

(6)

cascade (オプション - デフォルトは none) 子エンティティへのカスケード操作を有効にします。

(7)

sort (オプション)コレクションを自然な順序でソートする場合は natural を指定します。 あるいはComparatorクラスを指定します。

(8)

order-by (オプション、JDK1.4のみ) MapSet、bagのイテレーション順序を定義する テーブルカラムを指定すると共に、 オプションとして ascdesc を指定します。

(9)

where (オプション)コレクションの検索や削除の際に使う 任意のSQLのWHERE 条件を指定します (利用可能なデータの一部分だけをコレクションが含むべきときに、これは有用です)。

(10)

fetch(オプション - デフォルトは select) 外部結合によるフェッチ、順次選択フェッチ(sequential select fetch)、 順次サブセレクトフェッチ(sequential subselect fetch)のどれかを選択してください。

(11)

batch-size (オプション - デフォルトは 1) コレクションのインスタンスの遅延フェッチのための「バッチサイズ」を指定します。

(12)

access (オプション - デフォルトは property) コレクション型プロパティの値にアクセスするために使用する戦略です。

(13)

optimistic-lock(オプション - デフォルトは true) コレクションの状態を変えることによって、 そのオーナーであるエンティティのバージョンがインクリメントされるかを指定します。 (一対多関連では、ほとんどの場合において無効に設定するのが妥当です。)

(14)

mutable(オプション - デフォルトは truefalse 値は、コレクションの要素が変更されないことを表します (ある場合には、少しパフォーマンスを高めます)。

6.2.1. コレクションの外部キー

コレクションのインスタンスは、データベース内では、 そのコレクションを所有するエンティティの外部キーによって識別されます。 この外部キーはコレクションテーブルの コレクションキーカラム と呼ばれます。 コレクションキーカラムは <key> 要素によりマッピングします。

外部キーカラムにはnull設定制約があるかもしれません。 ほとんどのコレクションに当てはまるでしょう。 単方向の一対多関連において、外部キーカラムはデフォルトでnullを許す設定になっています。 よって、not-null="true" を指定する必要があるかもしれません。

<key column="productSerialNumber" not-null="true"/>

外部キーの制約が ON DELETE CASCADE を使うかもしれません。

<key column="productSerialNumber" on-delete="cascade"/>

<key> 要素のすべての定義については前の章を参照してください。

6.2.2. コレクションの要素

コレクションは他のHibernateの型のほとんど(すべての基本型、カスタム型、コンポーネント、 他のエンティティへの参照)を格納することができます。 次の点は重要な違いになります。 コレクションに格納されたオブジェクトが「値」セマンティクスとして扱われるのか (ライフサイクルはコレクションのオーナーに完全に依存します)、 もしくはそれ自身のライフサイクルを持った別のエンティティへの参照であるかのかという違いです。 後者は、2つのオブジェクト間の「リンク」をコレクションに保持していると見なしているだけです。

格納される型は コレクション要素型 と呼ばれます。 コレクション要素は、<element> または <composite-element> によりマッピングされ、エンティティへの参照の場合には <one-to-many> または <many-to-many> によりマッピングされます。 最初の二つは値として要素をマッピングし、次の二つはエンティティの関連をマッピングするのに使われます。

6.2.3. インデックス付きのコレクション

setとbagを除く全てのコレクションマッピングには、 コレクションテーブルの中に インデックス用のカラム が必要です。 そのカラムに、配列や List のインデックス、 もしくは Map のキーをマッピングします。 Map のインデックスは、 <map-key> によりマッピングされた基本型か、 <map-key-many-to-many> によりマッピングされたエンティティの関連か、 あるいは <composite-map-key> によりマッピングされたコンポジット型になります。 配列かリストのインデックスは、常に integer 型で、 <list-index> 要素によりマッピングします。 マッピングされたカラムにはシーケンシャルな整数を格納します(デフォルトでは0から番号が付けられます)。

<list-index 
        column="column_name"                (1)
        base="0|1|..."/>
(1)

column_name(必須):コレクションインデックスの値を保持するカラムの名前。

(2)

base(オプション、デフォルトは 0): リストもしくは配列の最初の要素に該当するインデックスカラムの値。

<map-key 
        column="column_name"                (1)
        formula="any SQL expression"        (2)
        type="type_name"                    (3)
        node="@attribute-name"
        length="N"/>
(1)

column(オプション): コレクションインデックスの値を保持するカラムの名前。

(2)

formula(オプション): Mapのキーを評価するのに使われるSQL式。

(3)

type(必須): Mapのキーの型。

<map-key-many-to-many
        column="column_name"                (1)
        formula="any SQL expression"        (2)
        class="ClassName"                   (3)
/>
(1)

column (オプション): コレクションインデックスの値のための外部キーカラムの名前。

(2)

formula (オプション): Mapのキーのための外部キーを評価するために使うSQL式。

(3)

class (必須): Mapのキーとして使われるエンティティクラス。

もしテーブルにインデックスカラムがなくても、プロパティ型として List を使いたければ、 Hibernateの <bag> としてプロパティをマッピングします。 bagはデータベースから復元される時、順序を保持しません。 しかし、(メモリ上で)ソートしたり、(SQLで)順序付けしたり(order by)することもできます。

多くの一般的なリレーショナルモデルをカバーしたために、 コレクションのために利用できるマッピングにはかなりの幅があります。 様々なマッピング宣言がどのようにデータベーステーブルに変換されるかを知るために、 スキーマ生成ツールを使ってみると良いでしょう。

6.2.4. 値のコレクションと多対多関連

値のコレクションや多対多関連は、専用の コレクションテーブル が必要です。 このテーブルは、外部キーカラムと、 コレクション要素のカラム と、 場合によってはインデックスカラムを持ちます。

値のコレクションのために、<element>タグを使用します。

<element
        column="column_name"                     (1)
        formula="any SQL expression"             (2)
        type="typename"                          (3)
        length="L"
        precision="P"
        scale="S"
        not-null="true|false"
        unique="true|false"
        node="element-name"
/>
(1)

column (オプション): コレクションの要素の値を保持するカラムの名前。

(2)

formula (オプション): 要素を評価するために使うSQL式。

(3)

type (必須)コレクションの要素の型。

多対多関連<many-to-many> 要素で指定します。

<many-to-many
        column="column_name"                               (1)
        formula="any SQL expression"                       (2)
        class="ClassName"                                  (3)
        fetch="select|join"                                (4)
        unique="true|false"                                (5)
        not-found="ignore|exception"                       (6)
        entity-name="EntityName"                           (7)
        property-ref="propertyNameFromAssociatedClass"     (8)
        node="element-name"
        embed-xml="true|false"
    />
(1)

column (オプション): 外部キーカラムの要素の名前。

(2)

formula (オプション): 外部キー値の要素を評価するために使うSQL式。

(3)

class (必須): 関連クラスの名前。

(4)

fetch (オプション - デフォルトは join): 関連のために、外部結合か順次選択フェッチを有効にします。 これは特殊なケースですが、エンティティと他のエンティティとの多対多関係を (1つの SELECT により)完全に即時にフェッチするためには、 そのコレクション自体だけでなく、 ネストした要素である <many-to-many> のこの属性についても join フェッチを有効する必要があります。

(5)

unique (オプション): 外部キーカラムのユニーク制約のDDL生成を有効にします。 これは、関連の多重度を事実上一対多にします。

(6)

not-found (オプション - デフォルトは exception) 参照先の行がない外部キーをどのように扱うかを指定します。 ignore にすると、行がないことを関連が無いものとして扱います。

(7)

entity-name (オプション): class の代替である関連クラスのエンティティ名。 class の代わりに指定する、関連クラスのエンティティ名。

(8)

property-ref: (オプション) この外部キーに結合する関連クラスのプロパティ名。 指定しなかった場合は、関連クラスの主キーを使います。

以下にいくつか例を示します。 まずはStringのsetに関しての例です。

<set name="names" table="person_names">
    <key column="person_id"/>
    <element column="person_name" type="string"/>
</set>

整数値を含むbag(bagは order-by 属性によって反復順序が定義されています)

<bag name="sizes" 
        table="item_sizes" 
        order-by="size asc">
    <key column="item_id"/>
    <element column="size" type="integer"/>
</bag>

エンティティの配列 - この場合、多対多の関連です。

<array name="addresses" 
        table="PersonAddress" 
        cascade="persist">
    <key column="personId"/>
    <list-index column="sortOrder"/>
    <many-to-many column="addressId" class="Address"/>
</array>

文字列と日付のmap

<map name="holidays" 
        table="holidays" 
        schema="dbo" 
        order-by="hol_name asc">
    <key column="id"/>
    <map-key column="hol_name" type="string"/>
    <element column="hol_date" type="date"/>
</map>

コンポーネントのlist(次の章で詳しく説明します)

<list name="carComponents" 
        table="CarComponents">
    <key column="carId"/>
    <list-index column="sortOrder"/>
    <composite-element class="CarComponent">
        <property name="price"/>
        <property name="type"/>
        <property name="serialNumber" column="serialNum"/>
    </composite-element>
</list>

6.2.5. 一対多関連

一対多関連 は、コレクション・テーブルを介さず、 外部キーにより2つのクラスのテーブルを関連付けます。 このマッピングは標準的なJavaのコレクションのセマンティクスをいくつか失います。

  • エンティティクラスのインスタンスは、 2つ以上のコレクションのインスタンスに属してはいけません。

  • コレクションに含まれるエンティティクラスのインスタンスは、 コレクションインデックスの値として2度以上現れてはいけません。

Product から Part への関連は、 Part テーブルへの外部キーカラムと、場合によってはインデックスカラムが必要です。 <one-to-many> タグは、これが一対多関連であることを表しています。

<one-to-many 
        class="ClassName"                                  (1)
        not-found="ignore|exception"                       (2)
        entity-name="EntityName"                           (3)
        node="element-name"
        embed-xml="true|false"
    />
(1)

class (必須): 関連クラスの名前。

(2)

not-found (オプション - デフォルトは exception): 参照先の行がないキャッシュされた識別子をどのように扱うかを指定します。 ignore を指定すると、行がないことを関連がないものとして扱います。

(3)

entity-name (オプション): class の代替である関連クラスのエンティティ名。 class の代わりに指定する、関連クラスのエンティティ名。

<one-to-many> 要素はカラムを宣言する必要がないことに注意してください。 同様に テーブル 名を指定する必要もありません。

とても重要な注意: もし <one-to-many> 関連の外部キーカラムが NOT NULLと宣言された場合、 <key> マッピングに not-null="true" を宣言するか、 コレクションマッピングに inverse="true" を付けた上で、 双方向関連を使う 必要があります。 双方向関連についてはこの章の後のほうで説明します。

次の例は、名称(Part の永続的なプロパティである partName) による Part エンティティのmapを表しています。 formulaによるインデックスを使っていることに注意してください。

<map name="parts"
        cascade="all">
    <key column="productId" not-null="true"/>
    <map-key formula="partName"/>
    <one-to-many class="Part"/>
</map>

6.3. 高度なコレクション・マッピング

6.3.1. ソートされたコレクション

Hibernateは java.util.SortedMapjava.util.SortedSet を実装したコレクションをサポートしています。 開発者はマッピング定義ファイルにコンパレータを指定しなければなりません。

<set name="aliases" 
            table="person_aliases" 
            sort="natural">
    <key column="person"/>
    <element column="name" type="string"/>
</set>

<map name="holidays" sort="my.custom.HolidayComparator">
    <key column="year_id"/>
    <map-key column="hol_name" type="string"/>
    <element column="hol_date" type="date"/>
</map>

sort 属性に設定できる値は unsortednatural および、java.util.Comparator を実装したクラスの名前です。

ソートされたコレクションは実質的には java.util.TreeSetjava.util.TreeMap のように振舞います。

もしデータベース自身にコレクションの要素を並べさせたいなら、 setbagmaporder-by 属性を使います。 この解決法はJDK1.4、もしくはそれ以上のバージョンで利用可能です (LinkedHashSet または LinkedHashMapを使って実装されています)。 整列はメモリ上ではなく、SQLクエリ内で実行されます。

<set name="aliases" table="person_aliases" order-by="lower(name) asc">
    <key column="person"/>
    <element column="name" type="string"/>
</set>

<map name="holidays" order-by="hol_date, hol_name">
    <key column="year_id"/>
    <map-key column="hol_name" type="string"/>
    <element column="hol_date" type="date"/>
</map>

order-by 属性の値がSQL命令であって、HQL命令ではないことに注意してください!

関連は、コレクションの filter() を使うことで、 実行時に任意のcriteriaによってソートすることも可能です。

sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();

6.3.2. 双方向関連

双方向関連 は関連のどちら「側」からでもナビゲーションできます。 2種類の双方向関連がサポートされています。

one-to-many

片側がsetかbag、もう片方が単一値です。

many-to-many

両側がsetかbagです。

2つの多対多関連で同じデータベーステーブルをマッピングし、 片方を inverse として宣言することで、 双方向の多対多関連を指定することが出来ます (どちらをinverseに選んだとしても、そちら側にはインデックス付きのコレクションは使えません)。

次に双方向の多対多関連の例を示します。 各カテゴリは多数のアイテムを持つことができ、各アイテムは多くのカテゴリに属することが出来ます。

<class name="Category">
    <id name="id" column="CATEGORY_ID"/>
    ...
    <bag name="items" table="CATEGORY_ITEM">
        <key column="CATEGORY_ID"/>
        <many-to-many class="Item" column="ITEM_ID"/>
    </bag>
</class>

<class name="Item">
    <id name="id" column="CATEGORY_ID"/>
    ...

    <!-- inverse end -->
    <bag name="categories" table="CATEGORY_ITEM" inverse="true">
        <key column="ITEM_ID"/>
        <many-to-many class="Category" column="CATEGORY_ID"/>
    </bag>
</class>

関連のinverse側にのみ行われた変更は永続化 されません。 これは、Hibernateは全ての双方向関連について、メモリ上に2つの表現を持っているという意味です。 つまり一つはAからBへのリンクで、もう一つはBからAへのリンクということです。 Javaのオブジェクトモデルについて考え、Javaで双方向関係をどうやって作るかを考えれば、 これは理解しやすいです。下記に、Javaでの双方向関連を示します。

category.getItems().add(item);          // The category now "knows" about the relationship
item.getCategories().add(category);     // The item now "knows" about the relationship

session.persist(item);                   // The relationship won't be saved!
session.persist(category);               // The relationship will be saved

関連のinverseではない側は、メモリ上の表現をデータベースに保存するのに使われます。

双方向の一対多関連を定義するには、 一対多関連を多対一関連と同じテーブルのカラムにマッピングし、 多側に inverse="true" と宣言します。

<class name="Parent">
    <id name="id" column="parent_id"/>
    ....
    <set name="children" inverse="true">
        <key column="parent_id"/>
        <one-to-many class="Child"/>
    </set>
</class>

<class name="Child">
    <id name="id" column="child_id"/>
    ....
    <many-to-one name="parent" 
        class="Parent" 
        column="parent_id"
        not-null="true"/>
</class>

関連の片側に inverse="true" を設定しても、 カスケード操作に影響を与えません。これらは直交した概念です!

6.3.3. インデックス付きコレクションと双方向関連

片側が <list><map> である 双方向関連は、特によく考える必要があります。 インデックスカラムにマップされる子クラスのプロパティがある場合は、問題ないです。 コレクションのマッピングで inverse="true" を使い続けられます。

<class name="Parent">
    <id name="id" column="parent_id"/>
    ....
    <map name="children" inverse="true">
        <key column="parent_id"/>
        <map-key column="name" 
            type="string"/>
        <one-to-many class="Child"/>
    </map>
</class>

<class name="Child">
    <id name="id" column="child_id"/>
    ....
    <property name="name" 
        not-null="true"/>
    <many-to-one name="parent" 
        class="Parent" 
        column="parent_id"
        not-null="true"/>
</class>

しかし、子クラスにそのようなプロパティがない場合は、 関連を真に双方向であると考えることができません (関連の片側に利用できる情報がありますが、もう一方にはありません)。 この場合は、コレクションに inverse="true" をマッピングできません。 代わりに、次のようなマッピングが使えます。

<class name="Parent">
    <id name="id" column="parent_id"/>
    ....
    <map name="children">
        <key column="parent_id"
            not-null="true"/>
        <map-key column="name" 
            type="string"/>
        <one-to-many class="Child"/>
    </map>
</class>

<class name="Child">
    <id name="id" column="child_id"/>
    ....
    <many-to-one name="parent" 
        class="Parent" 
        column="parent_id"
        insert="false"
        update="false"
        not-null="true"/>
</class>

注意: このマッピングでは、関連のコレクション値の側は、 外部キーをアップデートする責任があります。

6.3.4. 3項関連

3項関連のマッピングには3つのアプローチがあります。 1つ目は関連をインデックスとして Map を使用するアプローチです。

<map name="contracts">
    <key column="employer_id" not-null="true"/>
    <map-key-many-to-many column="employee_id" class="Employee"/>
    <one-to-many class="Contract"/>
</map>
<map name="connections">
    <key column="incoming_node_id"/>
    <map-key-many-to-many column="outgoing_node_id" class="Node"/>
    <many-to-many column="connection_id" class="Connection"/>
</map>

2つ目は単純に関連をエンティティ・クラスとしてモデルを作り直すアプローチで、 頻繁に使われます。

最後はcomposite要素を使うアプローチです。これに関する議論は後ほど行います。

6.3.5. <idbag>の使用

複合キーは悪いもので、エンティティは人工の識別子(代理キー)を持つべきであるという考え方からは、 多対多関連と値のコレクションを複合キーを用いたテーブルへマッピングするのは少し奇妙に感じるかもしれません! 確かにこの考え方には議論の余地があります。 純粋な関連テーブルは代理キーを使っても利益を得られないように思えるからです (合成値のコレクションは利益がある かも しれませんが)。 とはいえ、Hibernateは代理キーを持つテーブルへ多対多関連と値のコレクションを マッピングする機能も備えています。

bagのセマンティックスを持った List(または Collection)を <idbag> 要素にマッピングできます。

<idbag name="lovers" table="LOVERS">
    <collection-id column="ID" type="long">
        <generator class="sequence"/>
    </collection-id>
    <key column="PERSON1"/>
    <many-to-many column="PERSON2" class="Person" fetch="join"/>
</idbag>

ご存知のように <idbag> はエンティティ・クラスのように 人工的なidジェネレータを持っています! 異なる代理キーをそれぞれのコレクションの列に割り当てます。 しかしながら、Hibernateはある行の代理キーの値を見つけ出す機構を持っていません。

<idbag> を更新するパフォーマンスは 通常の <bag> よりも良いことに注目してください! Hibernateは個々の行を効果的に見つけることができ、 listやmap、setのように個別にその行を更新、削除できます。

現在の実装では、native というid生成戦略を <idbag> コレクションの識別子に対して使えません。

6.4. コレクションの例

これまでの節の説明では理解しにくいので、以下の例を見てください。

package eg;
import java.util.Set;

public class Parent {
    private long id;
    private Set children;

    public long getId() { return id; }
    private void setId(long id) { this.id=id; }

    private Set getChildren() { return children; }
    private void setChildren(Set children) { this.children=children; }

    ....
    ....
}

このクラスは Child インスタンスのコレクションを持っています。 もし各々のchildが最大でも一つのparentを持っているならば、最も自然なマッピングは一対多関連です。

<hibernate-mapping>

    <class name="Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children">
            <key column="parent_id"/>
            <one-to-many class="Child"/>
        </set>
    </class>

    <class name="Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
    </class>

</hibernate-mapping>

これは以下のテーブル定義にマッピングします。

create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
alter table child add constraint childfk0 (parent_id) references parent

もしparentが 要求 されるなら、双方向の一対多関連を使用してください。

<hibernate-mapping>

    <class name="Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children" inverse="true">
            <key column="parent_id"/>
            <one-to-many class="Child"/>
        </set>
    </class>

    <class name="Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
        <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
    </class>

</hibernate-mapping>

NOT NULL 制約に注意してください。

create table parent ( id bigint not null primary key )
create table child ( id bigint not null
                     primary key,
                     name varchar(255),
                     parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent

あるいは、もしこの関連は単方向であるべきと強く主張するのであれば、 <key>NOT NULL 制約を宣言できます。

<hibernate-mapping>

    <class name="Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children">
            <key column="parent_id" not-null="true"/>
            <one-to-many class="Child"/>
        </set>
    </class>

    <class name="Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
    </class>

</hibernate-mapping>

一方で、もしchildが複数のparentを持てるならば、多対多関連が妥当です。

<hibernate-mapping>

    <class name="Parent">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <set name="children" table="childset">
            <key column="parent_id"/>
            <many-to-many class="Child" column="child_id"/>
        </set>
    </class>

    <class name="Child">
        <id name="id">
            <generator class="sequence"/>
        </id>
        <property name="name"/>
    </class>

</hibernate-mapping>

テーブル定義は以下のようになります。

create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null,
                        child_id bigint not null,
                        primary key ( parent_id, child_id ) )
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child

parent/child関係のマッピングについてのより多くの例や完全な手順が必要であれば、 章 21. 例:親/子供 をご覧ください。

また、さらに特殊な関連マッピングも可能です。次の章で詳しく述べます。