Hibernate.orgCommunity Documentation
関連マッピングはしばしば理解が最も難しいものになります。この章では、基本的な一つ一つのケースについて述べます。単方向のマッピングから始め、それから双方向のケースについて考えていきます。例として、 Person
と Address
を用います。
関連は、結合テーブルを入れるかかどうかと、多重度によって分類することにします。
null 可能な外部キーは従来型データモデリングの中では良い習慣と見なされていないため、すべての例で not null の外部キーを使用します。これは Hibernate の要件ではありません。 not null 制約を外したとしても、マッピングは問題なく動作します。
単方向多対一関連 は単方向関連の中で最も一般的なものです。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
外部キーの単方向一対一関連 はほとんど同じものです。唯一違うのは、カラムのユニークな制約です。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
主キーの単方向一対一関連 は通常、特別な ID ジェネレータを使います。 (この例では関連の方向が逆になっていることに注意してください)
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property"
>person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class
>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
外部キーの単方向一対多関連 はとても特殊なケースで、あまり推奨されていません。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId"
not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )
このような関連のために結合テーブルを使うことをお薦めします。
結合テーブルを使った単方向一対多関連 はより好ましいです。 unique="true"
の指定により、多重度が多対多から一対多に変わったことに注意して下さい。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
結合テーブルの単方向多対一関連 は関連が任意であるときに非常に一般的なものです。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
結合テーブルの単方向一対一関連 は、非常に特殊ですが不可能ではありません。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
最後に、 単方向多対多関連 を示します。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key )
双方向多対一関連 は最も一般的な関連です。 (標準的な親子関係です)
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
List
(または他のインデックス付きのコレクション)を使うなら、外部キーの key
カラムを not null
に設定し、コレクション側が各要素のインデックスをメンテナンスするように、関連を扱う必要があります (update="false"
かつ insert="false"
と設定して、反対側を仮想的に inverse にします):
<class name="Person">
<id name="id"/>
...
<many-to-one name="address"
column="addressId"
not-null="true"
insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class
>
If the underlying foreign key column is NOT NULL
, it is important that you define not-null="true"
on the <key>
element of the collection mapping. Do not only declare not-null="true"
on a possible nested <column>
element, but on the <key>
element.
外部キーの双方向一対一関連 は非常に一般的です。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
主キーの双方向一対一関連 は特殊な ID ジェネレータを使います。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property"
>person</param>
</generator>
</id>
<one-to-one name="person"
constrained="true"/>
</class
>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
結合テーブルの双方向一対多関連 です。 inverse="true"
が関連端、コレクション、結合のいずれかに設定できることに注意してください。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
unique="true"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
inverse="true"
optional="true">
<key column="addressId"/>
<many-to-one name="person"
column="personId"
not-null="true"/>
</join>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
結合テーブルの双方向一対一関連 は非常に特殊ですが、可能です。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true">
<key column="personId"
unique="true"/>
<many-to-one name="address"
column="addressId"
not-null="true"
unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress"
optional="true"
inverse="true">
<key column="addressId"
unique="true"/>
<many-to-one name="person"
column="personId"
not-null="true"
unique="true"/>
</join>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
最後に、 双方向多対多関連 を示します。
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class
>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key )
より複雑な関連結合は 極めて 稀です。マッピングドキュメントに SQL 文を埋め込むことで、さらに複雑な状況を扱うことができます。例えば、 accountNumber
、 effectiveEndDate
、 effectiveStartDate
カラムを持つ account (口座)情報の履歴を扱うテーブルは、以下のようにマッピングします。
<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula
>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>
そして、関連を 現時点の インスタンス (effectiveEndDate
が null であるもの)にマッピングします。以下のようになります:
<many-to-one name="currentAccountInfo"
property-ref="currentAccountKey"
class="AccountInfo">
<column name="accountNumber"/>
<formula
>'1'</formula>
</many-to-one
>
さらに複雑な例では、 Employee(従業員)
と Organization(組織)
間の関連が Employment(雇用)
テーブルで保持される場合を想像してください。このテーブルには雇用データの履歴がすべて含まれます。すると従業員の 最も最近の 雇用者を表す関連 (最も最近の startDate
を持つもの)は、このようにマッピングできます:
<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer"
class="Organization"
column="orgId"/>
</join
>
この機能は非常に強力です。しかしこのような場合、普通は HQL や criteria クエリを使う方がより実践的です。
製作著作 © 2004 Red Hat, Inc.