第6章 基本的な O/R マッピング
6.1. マッピング宣言 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
オブジェクト/リレーショナルマッピングは通常 XML ドキュメントで定義します。マッピングドキュメントは、読みやすく手作業で編集しやすいようにデザインされています。マッピング言語は Java 中心、つまりテーブル宣言ではなく永続クラスの宣言に基づいて構築されています。
多くの Hibernate ユーザーは XML マッピングの記述を手作業で行いますが、XDoclet、Middlegen、AndroMDA など、マッピングドキュメントの生成ツールも多数存在することを覚えておいてください。
マッピング例から始めましょう:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat"
table="cats"
discriminator-value="C">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="subclass"
type="character"/>
<property name="weight"/>
<property name="birthdate"
type="date"
not-null="true"
update="false"/>
<property name="color"
type="eg.types.ColorUserType"
not-null="true"
update="false"/>
<property name="sex"
not-null="true"
update="false"/>
<property name="litterId"
column="litterId"
update="false"/>
<many-to-one name="mother"
column="mother_id"
update="false"/>
<set name="kittens"
inverse="true"
order-by="litter_id">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat"
discriminator-value="D">
<property name="name"
type="string"/>
</subclass>
</class>
<class name="Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
マッピングドキュメントの内容を説明します。ただし、ここでは Hibernate が実行時に使うドキュメント要素と属性についてのみ説明します。マッピングドキュメントは、いくつかの追加のオプション属性と要素を含んでいます(例えば
not-null 属性)。それらはスキーマエクスポートツールが出力するデータベーススキーマに影響を与えるものです。
6.1.1. Doctype リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
XML マッピングのすべてにおいて、提示したようなドキュメント型を宣言すべきです。実際の DTD は、上記の URL の
hibernate-x.x.x/src/org/hibernate ディレクトリ、または hibernate.jar 内にあります。まずHibernate は常に、そのクラスパス内で DTD を探し始めます。インターネット接続を利用して DTD ファイルを探す場合、クラスパスの内容を見て、DTD 宣言を確認してください。
6.1.1.1. エンティティリゾルバ リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
前述したように、Hibernate はまずクラスパス内で DTD を解決しようとします。
org.xml.sax.EntityResolver のカスタム実装を XML ファイルを読み込むための SAXReader に登録することによって、DTD を解決します。このカスタムの EntityResolver は2つの異なるシステムIDの名前空間を認識します。
hibernate namespaceは、リゾルバがhttp://hibernate.sourceforge.net/で始まるシステム ID に到達したときに認識されます。そしてリゾルバは、Hibernate のクラスをロードしたクラスローダを用いて、これらのエンティティを解決しようとします。user namespaceは、リゾルバが URL プロトコルのclasspath://を使ったシステム ID に遭遇したときに、認識されます。そしてこのリゾルバは、(1) カレントスレッドのコンテキストクラスローダー、または (2) Hibernate のクラスをロードしたクラスローダを使って、これらのエンティティを解決しようとします。
下記は、ユーザー名前空間の使用例です:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>
<hibernate-mapping package="your.domain">
<class name="MyEntity">
<id name="id" type="my-custom-id-type">
...
</id>
<class>
&types;
</hibernate-mapping>
6.1.2. Hibernate-mapping リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
この要素にはいくつかオプション属性があります。
schema 属性と catalog 属性は、このマッピングで参照するテーブルが、指定のスキーマと(または)カタログに属することを特定します。この属性が指定されると、テーブル名は与えられたスキーマ名とカタログ名で修飾されます。これらの属性が指定されていなければ、テーブル名は修飾されません。default-cascade 属性は、cascade 属性を指定していないプロパティやコレクションに、どのカスケードスタイルを割り当てるかを指定します。auto-import 属性は、クエリ言語内で非修飾のクラス名を、デフォルトで使えるようにします。
<hibernate-mapping
schema="schemaName"
catalog="catalogName"
default-cascade="cascade_style"
default-access="field|property|ClassName"
default-lazy="true|false"
auto-import="true|false"
package="package.name"
/>
| schema(オプション):データベーススキーマの名前。
|
| catalog (オプション):データベースカタログの名前。
|
| default-cascade(オプション - デフォルトは none):デフォルトのカスケードスタイル。
|
| default-access (オプション - デフォルトは property):Hibernate が全プロパティにアクセスする際に取るべき戦略。PropertyAccessor のカスタム実装の場合もある。
|
| default-lazy (オプション - デフォルトは true ):lazy 属性が指定されていないクラスやコレクションマッピングに対するデフォルト値。
|
| auto-import(オプション - デフォルトは true):クエリ言語にて、このマッピング内のクラスにある非修飾のクラス名を使えるかどうかを指定します。
|
| package (オプション): マッピングドキュメント内で非修飾のクラス名に対して使用する、パッケージの接頭辞 (prefix) を指定します。
|
同じ非修飾名を持つ永続クラスが2つある場合、
auto-import="false" を設定すべきです。2つのクラスに同じ「インポート」名を割り当てようとすると、Hibernate は例外を送出します。
hibernate-mapping 要素は、前述のようにいくつかの永続 <class> マッピングをネストできます。しかし、1つのマッピングファイルにただひとつの永続クラス、またはひとつのクラス階層にマッピングし、さらに永続スーパークラスの後に指定するようにします(ツールによってはこのようなマッピングファイルを想定しています)。例: Cat.hbm.xml , Dog.hbm.xml , または継承を使う場合 Animal.hbm.xml 。
6.1.3. Class リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
class 要素を使って、永続クラスを宣言できます。例えば、
<class
name="ClassName"
table="tableName"
discriminator-value="discriminator_value"
mutable="true|false"
schema="owner"
catalog="catalog"
proxy="ProxyInterface"
dynamic-update="true|false"
dynamic-insert="true|false"
select-before-update="true|false"
polymorphism="implicit|explicit"
where="arbitrary sql where condition"
persister="PersisterClass"
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false"
entity-name="EntityName"
check="arbitrary sql check condition"
rowid="rowid"
subselect="SQL expression"
abstract="true|false"
node="element-name"
/>
| name (オプション):永続クラスまたはインターフェースの完全修飾 Java クラス名。この属性が抜けている場合、POJO 以外のエンティティに対するマッピングとして扱われます。
|
| table (オプション - デフォルトは非修飾クラス名):データベーステーブルの名前
|
| discriminator-value (オプション - デフォルトはクラス名): ポリモーフィックな動作に使われる個々のサブクラスを識別するための値。値は null か not null のいずれかを取ります。
|
| mutable (オプション、デフォルトは true ):そのクラスのインスタンスが更新可能(または不可能)であることを指定します。
|
| schema (オプション): ルートの <hibernate-mapping> 要素で指定したスキーマ名をオーバーライドします。
|
| catalog (オプション): ルートの <hibernate-mapping> 要素で指定したカタログ名をオーバーライドします。
|
| proxy (オプション):lazyな初期化プロキシに使うインターフェースを指定します。クラス名そのものを指定することも可能です。
|
| dynamic-update (オプション - デフォルトは false):UPDATE SQLを実行時に生成すべき点、また値を変更したカラムしか含むことができない点を指定します。
|
| dynamic-insert(オプション, デフォルトは false ):INSERT SQLを実行時に生成し、値が null ではないカラムだけを含むべきであると指定します。
|
| select-before-update (オプション、デフォルトは false): 実際にオブジェクトが変更されたか確実でない場合 Hibernate が SQL の UPDATE を 決して実行しない ことを指定します。一時オブジェクトが update() を使い、新しいセッションと関連付けられた時だけ、UPDATE が実際に必要かどうかを決定するために、 Hibernate が余分な SQL の SELECT を実行します。
|
| polymorphism (オプション、デフォルトでは implicit ): 暗黙か明示の、どちらのクエリポリモーフィズムを使うか決定します。
|
| where(オプション): このクラスのオブジェクトを検索するときに使用する、任意の SQL の WHERE 条件を指定します。
|
| persister(オプション):カスタム ClassPersister を指定します。
|
| batch-size (オプション、デフォルトは 1 ):識別子でこのクラスのインスタンスをフェッチするときの「バッチサイズ」を指定します。
|
| optimistic-lock (オプション、デフォルトは version ): 楽観的ロック戦略を決定します。
|
(16) | lazy (オプション): lazy="false" と設定することで、遅延フェッチを無効にできます。
|
(17) | entity-name (オプション、デフォルトはクラス名):Hibernate3 ではクラスが複数回マッピングでき(場合によっては違うテーブルに対しても)、Java レベルで Map や XML で表現されるエンティティマッピングも可能です。これらの場合、エンティティに対して任意の名前を、明示的に付けなくてはなりません。 詳しくは 「動的モデル」 と 19章XML マッピング を参照してください。
|
(18) | check (オプション):自動的にスキーマを生成するために、複数行の check 制約を生成するのに利用する SQL 式。
|
(19) | rowid (オプション):Hibernate はデータベース上でROWIDを利用可能です。例えば、Oracle であれば、このオプションに rowid を設定すると、 Hiberante は rowid の余分なカラムを使うことで更新を高速化することができます。ROWID は実装の詳細で、保存されたタプルの物理的な場所を表しています。
|
(20) | subselect (オプション):不変かつ読み取り専用であるエンティティをデータベースの副問合せ(subselect)にマッピングします。ベースのテーブルの代わりにビューを持ちたい場合は有用です。より詳しい情報は下記を参照してください。
|
(21) | abstract (オプション): <union-subclass> 階層内の抽象スーパークラスをマークするために使います。
|
指定の永続クラスがインターフェースであっても問題ありません。そのときは
<subclass> 要素を使って、そのインターフェースを実装するクラスを宣言してください。static な内部クラスでも永続化できます。 eg.Foo$Bar といった標準形式を使ってクラス名を指定してください。
不変クラス
mutable="false" では、アプリケーションによる更新や削除が出来ないことがあります。これにより、 Hibernate がパフォーマンスを少し改善することができます。
オプションの
proxy 属性により、クラスの永続インスタンスの遅延初期化が可能になります。最初にHibernate は指定したインターフェースを実装する CGLIB プロキシを返します。この永続オブジェクトはプロキシのメソッドを呼び出すときにロードします。以下の「初期化コレクションとプロキシ」を参照してください。
暗黙的 ポリモーフィズムとは次の二つを意味しています。1つは、そのクラスのインスタンスが、スーパークラスや実装したインターフェース、またはクラスを指定するクエリーにより返されること、もう一つは、そのクラスのサブクラスのインスタンスがそのクラス自身を指定したクエリによって返されることです。また、 明示的 ポリモーフィズムとは、次の二つを意味しています。一つはクラスのインスタンスが、そのクラスを明示的に指定したクエリによってのみ返されることで、もう一つはクラスを指定したクエリが、
<class> 要素の中で <subclass> や <joined-subclass> とマッピングされているサブクラスのインスタンスだけを返すことです。ほとんどの用途ではデフォルトの polymorphism="implicit" が適切です。明示的なポリモーフィズムは、2つの違ったクラスが同じテーブルにマッピングされているときに有用です これによってテーブルカラムのサブセットを含む、「軽量な」クラスが可能になります。
persister 属性により、クラスに使われる永続化戦略をカスタマイズできます。例えば org.hibernate.persister.EntityPersister 自身のサブクラスを指定したり、またストアドプロシージャコール、フラットファイルへシリアライズ、LDAP などを通した永続性を実装する org.hibernate.persister.ClassPersister インターフェースの完全に新しい実装でさえも提供できます。Hashtable の「永続化」に関する簡単な例に関しては org.hibernate.test.CustomPersister を参照してください。
dynamic-update と dynamic-insert の設定はサブクラスに継承されません。そのため <subclass> や <joined-subclass> 要素を指定することも出来ます。これらの設定はパフォーマンスを向上させる事もありますが、落とすこともありますので、慎重に使用してください。
select-before-update を使用すると、通常パフォーマンスが落ちてしまいます。Session へ分離インスタンスのグラフを再追加する場合にデータベース更新のトリガを不必要に呼び出さずに済む点で、非常に有用です。
dynamic-update を有効にすれば、楽観ロック戦略を選ぶことになります:
version:バージョン/タイムスタンプカラムをチェックします。all:すべてのカラムをチェックします。dirty:変更したカラムをチェックし、同時更新できるようにします。none:楽観ロックを使用しません。
Hibernate で楽観的ロック戦略を使う場合、バージョン/タイムスタンプカラムを使うことを 強く お勧めします。この戦略はパフォーマンスの観点からも最適であり、さらに分離インスタンスへの修正 (つまり
Session.marge() が使われるとき) を正確に処理します。
Hibernate のマッピングにとってビューと普通のテーブルの間に違いはなく、データベースレベルでは透過的です。ただし、更新のあるビューの場合など特に、正しくビューをサポートしていない DBMS もあります。ビューを使いたくても、データベースで作成できないことがあります(例えば、レガシースキーマの場合)。この場合には、不変かつ読み取り専用のエンティティに与えられた SQL の副問合せ文をマップできます:
<class name="Summary">
<subselect>
select item.name, max(bid.amount), count(*)
from item
join bid on bid.item_id = item.id
group by item.name
</subselect>
<synchronize table="item"/>
<synchronize table="bid"/>
<id name="name"/>
...
</class>
テーブルをこのエンティティと同期するように宣言してください。オートフラッシュが確実に起こるように、また導出エンティティに対するクエリが古いデータを返さないようにするためです。
<subselect> は属性とネストしたマッピング属性のどちらでも利用できます。
6.1.4. id リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
マップされたクラスはデータベーステーブルの主キーカラムを定義 しなければなりません。ほとんどのクラスにはインスタンスのユニークな識別子を保持する JavaBeans スタイルのプロパティも持っています。
<id> 要素は、そのプロパティから主キーカラムへのマッピングを定義します。
<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="null|any|none|undefined|id_value"
access="field|property|ClassName">
node="element-name|@attribute-name|element/@attribute|."
<generator class="generatorClass"/>
</id>
| name(オプション):識別子プロパティの名前。
|
| type(オプション):Hibernate の型を示す名前。
|
| column(オプション - デフォルトはプロパティ名): 主キーカラムの名前。
|
| unsaved-value(オプション - デフォルト値は 「sensible」):インスタンスが新しくインスタンス化された (保存されていない)ことを示す、識別子プロパティの値。以前の Session で保存またはロードされた一時的インスタンスと区別するために使います。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用すべき戦略。
|
name 属性がない場合は、クラスには識別子プロパティがないものとみなされます。
unsaved-value 属性は Hibernate3 ではほとんどの場合、必要ではありません。
複合キーを持つレガシーデータにアクセスできるように、
<composite-id> という代替の宣言があります。しかし他の用途としては全くお奨めできません。
6.1.4.1. ジェネレータ リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
オプションの
<generator> 子要素は、永続クラスのインスタンスのユニークな識別子を生成するために使う、 Java クラスを指定します。ジェネレータインスタンスの設定、もしくは初期化にパラメータが必要であれば、 <param> 要素を使って渡すことができます。
<id name="id" type="long" column="cat_id">
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>
すべてのジェネレータは、
org.hibernate.id.IdentifierGenerator インターフェースを実装します。これはとても単純なインターフェースなので、専用の実装を独自に用意するアプリケーションもあるかもしれません。しかし Hibernate は組み込みの実装をいくつも用意しています。組み込みのジェネレータには以下のショートカット名があります:
incrementlong,short,int型の識別子を生成します。これらは他のプロセスが同じテーブルにデータを挿入しないときだけユニークです。クラスタ内では使わないでください 。identity- DB2, MySQL, MS SQL Server, Sybase, HypersonicSQL の識別子カラムをサポートします。返される識別子の型は
long,short,intのいずれかです。 sequence- DB2, PostgreSQL, Oracle, SAP DB, McKoi のシーケンスや、 Interbase のジェネレータを使用します。返される識別子の型は
long,short,intのいずれかです。 hilolong,short,int型の識別子を効率的に生成する hi/lo アルゴリズムを使います。 hi 値のソースとして、テーブルとカラムを与えます(デフォルトではそれぞれhibernate_unique_keyとnext_hi)。 hi/lo アルゴリズムは特定のデータベースに対してのみユニークな識別子を生成します。seqhilolong,short,int型の識別子を効率的に生成する hi/lo アルゴリズムを使います。指定されたデータベースシーケンスを与えます。uuid- ( IP アドレスが使用される)ネットワーク内でユニークな文字列型の識別子を生成するために、 128 ビットの UUID アルゴリズムを使用します。UUID は長さ 32 の 16 進数字の文字列としてエンコードされます。
guid- MS SQL サーバーと MySQL でデータベースが生成する GUID 文字列を使用します。
native- 基盤となるデータベースの性能により
identity、sequence、hiloのいずれかを選択します。 assignedsave()が呼ばれる前に、アプリケーションがオブジェクトに識別子を割り当てられるようにします。<generator>要素が指定されていなければ、これがデフォルトの戦略になります。select- ユニークキーによる行の選択と主キーの値の取得により、データベーストリガが割り当てた主キーを取得します。
foreign- 他の関連オブジェクトの識別子を使います。普通は、
<one-to-one>主キー関連と組み合わせて使います。 sequence-identity- 実際の値の生成のためにデータベースシーケンスを使用する特別なシーケンス生成戦略ですが、JDBC3 getGeneratedKeys と結びついて、INSERT 文の実行の一部として生成された識別子の値を返します。この戦略は JDK 1.4 を対象とする Oracle 10g のドライバのみでサポートされています。これらの INSERT 文でのコメントは Oracle のドライバのバグにより無効にされていることに注意してください。
6.1.4.2. Hi/lo アルゴリズム リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
hilo と seqhilo ジェネレータは、hi/lo アルゴリズムの2つの代替実装を提供します。1番目の実装は、次回に利用される「hi」値を保持する「特別な」データベーステーブルを必要とします。サポートされている場合、2番目の実装は、 Oracle スタイルのシーケンスを使います。
<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
残念ながら Hibernate へ独自の
Connection を提供するときには、 hilo を使えません。Hibernate が JTA でリストされている接続を取得するためにアプリケーションサーバーのデータソースを使用する場合は、hibernate.transaction.manager_lookup_class を適切に設定しなければなりません。
6.1.4.3. UUID アルゴリズム リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
UUID には以下のものが含まれます:IP アドレス、JVM のスタートアップタイム(4分の1秒の正確さ)、システム時間、 JVM に対して一意のカウンタ値。 Java コードから MAC アドレスやメモリアドレスを取得することはできないため、これはJNI が使えないときの最良のオプションです。
6.1.4.4. 識別子カラムとシーケンス リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
識別子カラムをサポートしているデータベース(DB2, MySQL, Sybase, MS SQL)では、
identity キー生成が使えます。シーケンスをサポートするデータベース(DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB)では、 sequence スタイルのキー生成が使えます。どちらの戦略も、新しいオブジェクトを挿入するために、SQL クエリを2つ必要とします。例えば:
<id name="id" type="long" column="person_id">
<generator class="sequence">
<param name="sequence">person_id_sequence</param>
</generator>
</id>
<id name="id" type="long" column="person_id" unsaved-value="0">
<generator class="identity"/>
</id>
クロスプラットフォームの開発では、
native 戦略は identity、sequence、hilo 戦略の中から1つを選択しますが、これは基盤となるデータベースの能力に依存します。
6.1.4.5. 識別子の割り当て リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
( Hibernate が生成するのではなく)アプリケーションに識別子を割り当てさせたい場合、
assigned ジェネレータを使うことができます。この特別なジェネレータは、すでにオブジェクトの識別子プロパティに代入された値を識別子に使います。このジェネレータは主キーが代理キーの代わりに自然キーである場合に使用します。<generator> 要素を指定しない場合のデフォルトの動作になります。
assigned ジェネレータを選択すると、 Hibernate は unsaved-value="undefined" を使います。その結果、バージョンやタイムスタンプのプロパティがない場合や Interceptor.isUnsaved() を定義しなかった場合には、インスタンスが一時的(transient)なものであるのか、またはセッションから分離(detached)したものかどうかを決めるために、Hibernateは必ずデータベースを調べることになります。
6.1.4.6. トリガにより割り当てられた主キー リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Hibernate はトリガを使って DDL を生成しません。これはレガシースキーマ用となっています。
<id name="id" type="long" column="person_id">
<generator class="select">
<param name="key">socialSecurityNumber</param>
</generator>
</id>
上記の例の中で、
socialSecurityNumber という名前のユニークな値のプロパティがあります。これはクラスにより、自然キーや、値がトリガにより生成される person_id という名前の代理キーとして定義されます。
6.1.5. 拡張型の識別子ジェネレータ リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
識別子生成の2つの側面の見直しが行われ、リリース 3.2.3 から新たな識別子ジェネレータが2つ登場しました。一点目は、データベースの移植性で、2点目は最適化です。ここでいう最適化とは、新たな識別子の値に対するリクエストすべてに関してデータベースをクエリする必要がないという意味です。これら2つのジェネレータは、前述した指定ジェネレータの一部の代わりとされており、3.3.x から組み込まれています。ただし、最新のリリースには含まれており、FQNで参照することができます。
1つ目のジェネレータは、
org.hibernate.id.enhanced.SequenceStyleGeneratorで、まずはsequenceの代わりとされており、次に native よりも移植性が優れているジェネレータの提供を目的としています。通常identity と sequence との間で選択するためなのですが、identity と sequence は非常違ったセマンティクスを持っており、移植性を視野に入れるアプリケーションで微妙な問題を引き起こす可能性があるのです。しかし、org.hibernate.id.enhanced.SequenceStyleGenerator は違ったかたちで移植性を実現しています。データベース内のテーブルまたはシーケンスの中から選択し、使用される方言の性能に従い、インクリメント値を保存します。このジェネレータと native の違いは、テーブルベースとシーケンスベースのストレージは全く同じセマンティックを持つことです。実際、シーケンスは Hibernate がテーブルベースのジェネレータでエミュレートしようとしていることと全く同じです。このジェネレータにはいくつかの設定パラメーターが存在します:
sequence_name(オプション - デフォルトはhibernate_sequence): 使用するシーケンスもしくはテーブル名initial_valueオプション - デフォルトは1): シーケンス / テーブルから取得される初期値。シーケンス作成の用語にすると、これは通常「STARTS WITH」で指定される節に似ています。increment_size(オプション - デフォルトでは1): シーケンス/テーブルへの後続の呼出が違う値。シーケンス作成の用語にすると、これは通常「INCREMENT BY」で指定される節に似ています。force_table_use(オプション - デフォルトはfalse): 方言がシーケンスに対応する場合でも、補助構造としてテーブルの利用を強制すべきか?value_column(オプション - デフォルトはnext_val): テーブル構造にのみ該当。これは、値を保持する際に利用するテーブル上のカラム名です。
2つめのジェネレータは、
org.hibernate.id.enhanced.TableGeneratorで、実際 org.hibernate.id.MultipleHiLoPerTableGeneratorのように機能するにもかかわらずtableジェネレータの代わりとされています。 次に、このジェネレータは、プラグ可能なオプティマイザの概念を利用する org.hibernate.id.MultipleHiLoPerTableGenerator の再実装とすることを目的としています。 必然的に、明確にキー付けされた行を複数利用することで、このジェネレータは様々なインクリメント値を同時に保持可能なテーブルを定義します。このジェネレータには様々な設定パラメータがあります:
table_name(オプション - デフォルトはhibernate_sequences): 使用されるテーブル名value_column_name(オプション - デフォルトはnext_val): 値を保持するのに利用するテーブル上のカラム名。segment_column_name(オプション - デフォルトはsequence_name): 「セグメントキー」を保持するのに利用するテーブル上のカラム名。 これはどのインクリメント値を利用するかを指定する値です。segment_value(オプション - デフォルトはdefault): このジェネレータに対するインクリメント値を引き出したいと考えるセグメントの「セグメントキー」値。segment_value_length(オプション - デフォルトは255): スキーマ生成に使用;このセグメントキーコラムを作成するカラムサイズ。initial_value(オプション - デフォルトは1): テーブルから取得する初期値。increment_size(オプション - デフォルトは1): テーブルへの後続の呼出が異なる値。
6.1.6. 識別子ジェネレータの最適化 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
データベースに値を格納する識別子ジェネレータについて、新しい識別子の値を 生成するために呼出し毎(またはすべての呼出し)にデータベースをヒットするのは 効率がよくありません。代わりに、メモリ内のそれらを一つに集め、インメモリの値グループがいっぱいになったときにのみ データベースをヒットすることができます。これがプラグ可能なオプティマイザの役割です。現在、この操作をサポートするのは、 2つの拡張ジェネレータのみ (「拡張型の識別子ジェネレータ」 となっています。
none(通常、オプティマイザの指定がない場合これがデフォルトです。): この場合いかなる最適化も行われずすべてのリクエスト(およびリクエスト毎に)に対してデータベースをヒットします。hilo: 値を取得するデータベースにて hi/lo アルゴリズムを適用します。 このオプティマイザに対するデータベースからの値はシーケンシャルとなります。 また、このオプティマイザについてデータベース構造から取得した値は、「グループ番号」 を示します。increment_sizeがメモリ内の値と乗じることで 「hi value」というグループを定義します。pooled:hiloの場合、このオプティマイザは データベースへのヒット数を最小限に抑えようとします。しかし、ここでは、インメモリ のグループ化アルゴリズムと組み合わせた連続値ではなくデータベース構造に 「次のグループ」の開始値を格納するだけとなっています。ここでのincrement_sizeは、データベースから取った値を参照しています。
6.1.7. composite-id リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<composite-id
name="propertyName"
class="ClassName"
mapped="true|false"
access="field|property|ClassName">
node="element-name|."
<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName class="ClassName" column="column_name"/>
......
</composite-id>
複合キーのあるテーブルに対し、識別子プロパティとしてクラスの複数のプロパティをマッピングすることができます。
<composite-id> 要素は、子要素として <key-property> プロパティマッピングと <key-many-to-one> マッピングを受け入れます。
<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>
複合識別子の等価性を実装するためには、永続クラスが
equals() と hashCode() をオーバーライド しなければなりません 。また Serializable も実装しなければいけません。
残念ながら このアプローチは永続オブジェクトが自身の識別しであることを意味しています。オブジェクト自身を識別子とする以外に便利な「扱い方」はありません。複合キーに関連した永続状態を
load() 出来るようになる前に、永続クラス自身をインスタンス化し、識別子プロパティを設定しなければなりません。組み込みの 複合識別子と呼ばれるこのアプローチは、本格的なアプリケーションには向いていません。
2つ目の方法は マップされた 複合識別子と呼ばれるもので、
<composite-id>エレメント内で指定した識別プロパティが永続クラスと分離した識別子クラスの両方に重複して存在します。
<composite-id class="MedicareId" mapped="true">
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>
この例では、複合識別子クラス(
MedicareId )とエンティティクラス自身の両方が、 medicareNumber と dependent という名前のプロパティを持ちます。識別子クラスは、 equals() と hashCode() をオーバライドし、 Serializable を実装しなくてはなりません。この方法の短所は、主にコードが重複するという点にあります。
次の属性はマッピングした複合識別子を指定するために使用します:
mapped(オプション - デフォルトはfalse): マッピングした複合識別子が使用されることと、包含されたプロパティのマッピングが、エンティティクラスと複合識別子クラスの両方を参照することを示します。class(オプション - ただしマッピングした複合識別子には必須): 複合識別子として使用するクラス。
3つ目のさらに便利な方法は、複合識別子を 「複合識別子としてのコンポーネント」 のコンポーネントクラスとして実装することです。下で記述している属性は、この代替方法にのみ適用されます:
name(オプション - このアプローチでは必須): 複合識別子を保持するコンポーネントタイプのプロパティ。詳細は9章を参照してください)。access(オプション - デフォルトはproperty): Hibernate がプロパティの値にアクセスするために使用する戦略。class(オプション - デフォルトはリフレクションにより決定されるプロパティの型): 複合識別子として使われるコンポーネントのクラス。詳細は次の節を見てください。
この3つ目の方法は 識別子コンポーネント と呼び、ほとんどすべてのアプリケーションに対して推奨しています。
6.1.8. Discriminator リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<discriminator> 要素は、 table-per-class-hierarchy マッピング戦略を使うポリモーフィックな永続化に必要であり、テーブルの識別子カラムを宣言します。識別子カラムは、ある特定の行に対して永続層がどのサブクラスをインスタンス化するかを伝えるマーカー値を含んでいます。利用できる一連の型は以下に制限されます: string , character , integer, byte , short , boolean , yes_no , true_false.
<discriminator
column="discriminator_column"
type="discriminator_type"
force="true|false"
insert="true|false"
formula="arbitrary sql expression"
/>
| column(オプション - デフォルトは class ):識別子カラムの名前。
|
| type (オプション - デフォルトは string ): Hibernate の型を示す名前。
|
| force (オプション - デフォルトは false ):ルートクラスのすべてのインスタンスを検索する場合であっても、 Hibernate が使用可能な識別カラムの指定を「強制」します。
|
| insert (オプション - デフォルトは true ):識別カラムがマッピングする複合識別子の一部であれば、 false と設定してください。Hibernate に SQL の INSERT 内のカラムを含ませないよう伝えます。
|
| formula (オプション) 型が評価されるときに実行される任意の SQL 式。コンテンツベースの識別が可能になります。
|
識別カラムの実際の値は、
<class> と <subclass> 要素の discriminator-value 属性で指定されます。
永続クラスへマッピングされない「余分な」識別値を持つ行がテーブルにあれば、そのときに限り
force 属性は有効です。ただし、普通はそういうことはありません。
formula 属性を使うと、行の型を評価するために任意の SQL 式を宣言できます:例えば、
<discriminator
formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
type="integer"/>
6.1.9. Version(オプション) リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<version> 要素はオプションであり、テーブルがバージョンデータを含むことを示します。これは ロングトランザクション を使う予定であれば特に役立ちます。詳細は以下を参照してください。
<version
column="version_column"
name="propertyName"
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
generated="never|always"
insert="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>
| column (オプション - デフォルトはプロパティ名): バージョン番号を保持するカラムの名前。
|
| name:永続クラスのプロパティ名。
|
| type(オプション - デフォルトは integer):バージョン番号の型。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用すべき戦略。
|
| unsaved-value (オプション - デフォルトは undefined ):インスタンスが新しくインスタンス化されたことを示す(保存されていないことを示す) バージョンプロパティの値。以前の Session で保存またはロードされた分離(Detached)インスタンスと区別するために使います。undefined は識別子プロパティの値が使われべきである点を指定します。
|
| generated (オプション - デフォルトは never ): このバージョンのプロパティの値が、データベースによって生成されたことを指定します。詳細は「生成プロパティ」の議論を参照してください。
|
| insert (オプション - デフォルトは true ): SQLの insert 文にバージョンカラムを含めるべきかどうかを指定します。データベースカラムのデフォルト値が 0 と定義される場合は、false に設定すると良いでしょう。
|
バージョン番号は Hibernate の
long、integer、short、timestamp 、 calendar 型のいずれかです。
バージョンやタイムスタンプのプロパティは、分離されたインスタンスに対して null であってはなりません。そのためどのような
unsaved-value 戦略が指定されても、 Hibernate は null のバージョンやタイムスタンプを持ったすべてのインスタンスを、一時的なものであると検知します。 null を許容するバージョンやタイムスタンプのプロパティを宣言することは、 Hibernate において過渡的に一時オブジェクトとすることを防ぐ簡単な方法です。特に識別子の割り当てや複合キーを使用しているときには特に有用です。
6.1.10. Timestamp(オプション) リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
オプションの
<timestamp> 要素は、テーブルがタイムスタンプデータを含むことを示します。これはバージョン付けの代わりの方法として用意されています。タイムスタンプは楽観的ロックの中で安全性の低い実装ですが、時にアプリケーションがタイムスタンプを別の用途で使うこともあるかもしれません。
<timestamp
column="timestamp_column"
name="propertyName"
access="field|property|ClassName"
unsaved-value="null|undefined"
source="vm|db"
generated="never|always"
node="element-name|@attribute-name|element/@attribute|."
/>
| column(オプション - デフォルトはプロパティ名): タイムスタンプを保持するカラムの名前。
|
| name :永続クラスである Java の Date型または Timestamp 型 の、 JavaBeans スタイルプロパティの名前。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用する戦略。
|
| unsaved-value(オプション - デフォルトは null ):インスタンスが新しくインスタンス化された (保存されていない)ことを示すバージョンプロパティの値。以前の Session で保存またはロードされた分離(Detached)インスタンスと区別するために使われます。( undefined と指定すると、識別子プロパティの値を使う必要があります。
|
| source (オプション - デフォルトは vm ): Hibernate はどこからタイムスタンプの値を取得するべきでしょうか?データベースからでしょうか、現在の JVM からでしょうか?データベースによるタイムスタンプは、 Hibernate が 「次の値」を決定するためにデータベースをヒットしなければならないため、オーバヘッドを招きます。しかしクラスタ環境では JVM から取得するより安全です。データベースの現在のタイムスタンプの取得をサポートするすべての Dialect が知られているわけではないことに注意してください。また一方で、精密さを欠くために、ロックで使用するには安全でないものもあります (例えば Oracle 8 )。
|
| generated (オプション - デフォルトは never ): このタイムスタンプのプロパティの値が実際に、データベースによって生成されることを指定します。詳細は「生成プロパティ」 の議論を参照してください。
|
注記
<Timestamp> は <version type="timestamp"> と等価であることに注意してください。また、<timestamp source="db"> は <version type="dbtimestamp"> と等価であることに注意してください。
6.1.11. Property リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<property> 要素は、クラスの永続的な JavaBean スタイルのプロパティを宣言します。
<property
name="propertyName"
column="column_name"
type="typename"
update="true|false"
insert="true|false"
formula="arbitrary SQL expression"
access="field|property|ClassName"
lazy="true|false"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
generated="never|insert|always"
node="element-name|@attribute-name|element/@attribute|."
index="index_name"
unique_key="unique_key_id"
length="L"
precision="P"
scale="S"
/>
| name: 小文字で始まるプロパティ名。
|
| column(オプション - デフォルトはプロパティ名): マッピングされたデータベーステーブルのカラムの名前。これはネストした <column> 要素でも指定できます。
|
| type(オプション):Hibernate の型を示す名前。
|
| update, insert(オプション - デフォルトは true ):マッピングされたカラムが SQL の UPDATE および/または INSERT に含まれることを指定します。両方をfalse に設定すると、同じカラムにマッピングされた他のプロパティやトリガや他のアプリケーション によって値が初期化された純粋な「導出」プロパティが可能になります。
|
| formula(オプション):計算 プロパティのための値を定義する SQL 式。計算されたプロパティは自身のカラムへのマッピングがありません。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用する戦略。
|
| lazy (オプション - デフォルトは false ): インスタンス変数に最初にアクセスしたときに、プロパティを遅延して取得するよう指定します。バイトコード実装を作成する時間が必要になります。
|
| unique (オプション):カラムにユニーク制約をつける DDL の生成を可能にします。また、property-ref のターゲットとすることもできます。
|
| not-null (オプション):カラムに null 値を許可する DDL の生成を可能にします。
|
| optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。
|
|
typename には以下の値が可能です:
- Hibernate の基本型の名前:例
integer, string, character, date, timestamp, float, binary, serializable, object, blob。 - デフォルトの基本型の Java クラス名 :例
int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clobなど。 - シリアライズ可能な Java クラスの名前。
- カスタム型のクラス名:例
com.illflow.type.MyCustomType。
型を指定しなければ、Hibernate は正しい Hibernate の型を推測するために、指定されたプロパティに対してリフレクションを使います。Hibernate はルール2, 3, 4をその順序に使い、getter プロパティの返り値のクラス名を解釈しようとします。しかしこれで常に十分であるとは限りません。場合によっては、
type 属性が必要な場合があります。例えば Hibernate.DATE と Hibernate.TIMESTAMP を区別するため、またはカスタム型を指定するためなどです。
access 属性で、実行時に Hibernate がどのようにプロパティにアクセスするかを制御できます。デフォルトでは Hibernate はプロパティの get/set のペアをコールします。access="field" と指定すれば、Hibernate はリフレクションを使い get/set のペアを介さずに、直接フィールドにアクセスします。インターフェース org.hibernate.property.PropertyAccessor を実装するクラスを指定することで、プロパティへのアクセスに独自の戦略を指定することができます。
特に強力な特徴は生成プロパティです。これらのプロパティは当然読み取り専用であり、プロパティの値はロード時に計算されます。計算を SQL 式として宣言すると、このプロパティはインスタンスをロードする SQL クエリの
SELECT 句のサブクエリに変換されます:
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>
特定のカラムのエイリアスを宣言することなく、エンティティ自身のテーブルを参照できることに注意してください。例では
customerId がそれにあたります。 属性を使用したくない場合、ネストした <formula> マッピング要素を使えることにも注意してください。
6.1.12. Many-to-one リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
他の永続クラスへの通常の関連は
many-to-one 要素を使って定義します。リレーショナルモデルは多対一関連です。つまりあるテーブルの外部キーは、ターゲットとなるテーブルの主キーカラムを参照しています。
<many-to-one
name="propertyName"
column="column_name"
class="ClassName"
cascade="cascade_style"
fetch="join|select"
update="true|false"
insert="true|false"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
lazy="proxy|no-proxy|false"
not-found="ignore|exception"
entity-name="EntityName"
formula="arbitrary SQL expression"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
index="index_name"
unique_key="unique_key_id"
foreign-key="foreign_key_name"
/>
| name:プロパティ名。
|
| column (オプション):外部キーカラムの名前。ネストした <column> 要素によっても指定されます。
|
| class(オプション - デフォルトはリフレクションにより決定されるプロパティの型):関連クラスの名前。
|
| cascade(オプション):親オブジェクトから関連オブジェクトへ、どの操作をカスケードするかを指定します。
|
| fetch(オプション - デフォルトは select ):外部結合フェッチと順次選択フェッチのどちらかを選択します。
|
| update, insert(オプション - デフォルトは true ):マッピングされたカラムが SQL の UPDATE または INSERT 文に含まれることを指定します。両方をfalse に設定すると、その値が同じカラムにマッピングされた他のプロパティやトリガや他のアプリケーションによって初期化された純粋な「導出」プロパティが可能になります。
|
| property-ref (オプション): 外部キーに結合された、 関連クラスのプロパティ名。指定されていない場合は、関連クラスの主キーを使用します。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用する戦略。
|
| unique(オプション):外部キーカラムに対してユニーク制約をつけた DDL の生成を可能にします。また、property-ref のターゲットにすることで、関連の多重度を効果的に一対一にします。
|
| not-null (オプション): 外部キーカラムに対して、 null 値を許可する DDL の生成を可能にします。
|
| optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。
|
| lazy (オプション - デフォルトは proxy ): デフォルトでは、多重度1の関連がプロキシとなります。 lazy="no-proxy" は、インスタンス変数に最初にアクセスしたときに、プロパティを遅延フェッチするよう指定します 。ビルド時にバイトコード実装が必要になります。 lazy="false" は関連を常に即時にフェッチするよう指定します。
|
| not-found(オプション - デフォルトは exception): 参照先の行がない外部キーをどのように扱うかを指定します: ignore を指定すると、行がないことを関連がないものとして扱います。
|
| entity-name (オプション):関連したクラスのエンティティ名。
|
| formula (オプション): 計算された 外部キーに対して値を定義する SQL 式
|
cascade 属性に none 以外の意味のある値を設定すると、関連オブジェクトへある操作が伝播することになります。意味のある値は3つに分類することができます。1つ目は、 Hibernate の基本操作名のことで、 persist,merge, delete, save-update, evict, replicate, lock and refresh を含みます。2つ目は、特別な値でdelete-orphan、3つ目は操作名をカンマで区切った組み合わせのすべて(例:cascade="persist,merge,evict" や cascade="all,delete-orphan")となっています。 詳しい説明は 「連鎖的な永続化」を参照してください。 値が一つの関連 (many-to-one と one-to-one関連) は、単独での削除 (orphan delete)をサポートしていないことに注意してください。
典型的な
many-to-one 宣言は次の通りです。
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
property-ref 属性は、外部キーが関連テーブルのユニークキー(主キー以外)を参照しているレガシーデータをマップするためにだけ使うべきです。このリレーショナルモデルは複雑で分かりにくくなっています。例えば Product クラスが、主キーでないユニークなシリアルナンバーを持っていると仮定してみてください。unique 属性は SchemaExport ツールを使った Hibernate の DDL 生成を制御します。
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
以下のように
OrderItem に対してマッピングを使えます:
<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>
しかし、これは推奨できません。
参照したユニークキーが、関連するエンティティの多数のプロパティから構成される場合、指定した
<properties> 要素内で、参照するプロパティをマッピングするべきです。
参照したユニークキーがコンポーネントのプロパティである場合は、プロパティのパスを指定できます:
<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>
6.1.13. One-to-one リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
他の永続クラスへの一対一関連は、
one-to-one 要素で定義します。
<one-to-one
name="propertyName"
class="ClassName"
cascade="cascade_style"
constrained="true|false"
fetch="join|select"
property-ref="propertyNameFromAssociatedClass"
access="field|property|ClassName"
formula="any SQL expression"
lazy="proxy|no-proxy|false"
entity-name="EntityName"
node="element-name|@attribute-name|element/@attribute|."
embed-xml="true|false"
foreign-key="foreign_key_name"
/>
| name:プロパティ名。
|
| class(オプション - デフォルトはリフレクションにより決定されるプロパティの型):関連クラスの名前。
|
| cascade(オプション):親オブジェクトから関連オブジェクトへ、どの操作をカスケードするかを指定します。
|
| constrained(オプション): マッピングされたテーブルの主キーに対する外部キー制約が、関連クラスのテーブルを参照することを指定します。このオプションは save() と delete() がカスケードされる順序に影響し、そして関連がプロキシされるかどうかにも影響します 。そしてスキーマエクスポートツールにも使われます。
|
| fetch(オプション - デフォルトは select ):外部結合フェッチと順次選択フェッチのどちらかを選択します。
|
| property-ref(オプション):このクラスの主キーに結合された関連クラスのプロパティ名。指定されなければ、関連クラスの主キーが使われます。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用する戦略。
|
| formula (オプション): ほとんどすべての一対一関連は所有エンティティの 主キーへとマッピングされます。これ以外の稀な場合は、他のカラムや、 複数のカラム、 SQL 構文を使った結合するための式を指定できます。例は org.hibernate.test.onetooneformula を参照してください。
|
| lazy (オプション - デフォルトは proxy ): デフォルトでは、多重度1の関連がプロキシとなります。 lazy="no-proxy" は、インスタンス変数に最初にアクセスしたときに、プロパティを遅延フェッチするよう指定します (ビルド時にバイトコード実装が必要になります)。lazy="false" は関連を常に即時にフェッチするよう指定します。 constrained="false" ならばプロキシは使用不可能となり、Hibernateは関連を即時にフェッチすることに注意してください。
|
| entity-name (オプション):関連したクラスのエンティティ名。
|
一対一関連には2種類あります:
- 主キー関連
- ユニーク外部キー関連
主キー関連には、余分なテーブルカラムは必要ありません。2つの行が関連により関係していれば、2つのテーブルは同じ主キーの値を共有します。そのため2つのオブジェクトを主キー関連によって関連付けたい場合、確実に同じ識別子の値を代入しなければなりません。
主キー関連を行うためには、以下のマッピングを
Employee と Person のそれぞれに追加してください.
<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>
必ず、PERSON と EMPLOYEE テーブルの関係する行の主キーが同じであるように してください。ここでは、
foreign という特殊な Hibernate 識別子生成戦略を使います:
<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property">employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class>
新たに保存された
Person のインスタンスが、そのPerson の employee プロパティで参照したEmployee インスタンス と同じ主キーの値を割り当てます。
もう1つの方法として、
Employee から Person へのユニーク制約を使った外部キー関連は以下のように表現することができます:
<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>
この関連は、以下の記述を
Person のマッピングに追加することで双方向にすることができます:
<one-to-one name="employee" class="Employee" property-ref="person"/>
6.1.14. Natural-id リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<natural-id mutable="true|false"/>
<property ... />
<many-to-one ... />
......
</natural-id>
主キーとして代理キーの使用を推奨しますが、すべてのエンティティに対して自然キーを識別するようにすべきです。自然キーはユニークかつ非 null な一つのプロパティ、またはプロパティの連結です。また、これは不変となっています。
<natural-id> 要素内で自然キーのプロパティをマッピングします。 Hibernate は必然的にユニークなキーかつ null 値を許可する制約を生成し、その結果マッピングはより自己記述的になります。
エンティティの自然キープロパティの比較には、
equals() と hashCode() の実装をお勧めします。
このマッピングは自然主キーを使ったエンティティでの使用は視野にいれていません。
mutable(オプション、 デフォルトはfalse): デフォルトでは、自然識別子プロパティは不変(定数)と想定されています。
6.1.15. Componentおよびdynamic-component リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<component> 要素は、子オブジェクトのプロパティを親クラスのテーブルのカラムへマッピングします。代わりに、コンポーネントは自分のプロパティ、コンポーネント、コレクションを宣言することができます。以下の「コンポーネント」を見てください。
<component
name="propertyName"
class="className"
insert="true|false"
update="true|false"
access="field|property|ClassName"
lazy="true|false"
optimistic-lock="true|false"
unique="true|false"
node="element-name|."
>
<property ...../>
<many-to-one .... />
........
</component>
| name:プロパティ名。
|
| class(オプション - デフォルトはリフレクションにより決定されるプロパティの型):コンポーネント(子)クラスの名前。
|
| insert:マッピングされたカラムを SQL の INSERT に表しますか?
|
| update:マッピングされたカラムが SQL の UPDATE に表しますか?
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用する戦略。
|
| lazy (オプション - デフォルトは false ): インスタンス変数に最初にアクセスしたときに、コンポーネントを遅延してフェッチするよう指定します。ビルド時の バイトコード実装を作成する時間が必要になります。
|
| optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に、楽観ロックの取得を要求するかどうかを指定します。言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。
|
| unique (オプション - デフォルトは false ): コンポーネントにあるマッピングされたカラムすべてに、ユニーク制約が存在するかを指定します。
|
子の
<property> タグで、子のクラスのプロパティをテーブルカラムにマッピングします。
<component> 要素は、親エンティティへ戻る参照として、コンポーネントのクラスのプロパティをマッピングする <parent> サブ要素を許可します。
<dynamic-component> 要素は、 Map がコンポーネントとしてマッピングされることを可能にします。プロパティ名は map のキーを参照します。詳細は「動的コンポーネント」 を参照してください。
6.1.16. プロパティ リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
<properties> 要素はクラスのプロパティの指定された、論理的なグルーピングを可能にします。この構造の最も重要な使用方法は、 property-ref のターゲットになるプロパティの結合を許可することです。それはまた、複数カラムのユニーク制約を定義する簡単な方法でもあります。
<properties
name="logicalName"
insert="true|false"
update="true|false"
optimistic-lock="true|false"
unique="true|false"
>
<property ...../>
<many-to-one .... />
........
</properties>
| name : グルーピングの論理名。実際のプロパティ名では ありません。
|
| insert:マッピングされたカラムを SQL の INSERT に表しますか?
|
| update:マッピングされたカラムが SQL の UPDATE に表しますか?
|
| optimistic-lock (オプション - デフォルトは true ): これらのプロパティの更新に楽観的ロックの取得を要求するかどうかを指定します。このプロパティがダーティであるときにバージョンを増やすべきかを決定します。
|
| unique (オプション - デフォルトは false ): コンポーネントにあるマッピングされたカラムすべてに、ユニーク制約が存在するかを指定します。
|
例えば、以下のような
<properties> マッピングがあった場合:
<class name="Person">
<id name="personNumber"/>
...
<properties name="name"
unique="true" update="false">
<property name="firstName"/>
<property name="initial"/>
<property name="lastName"/>
</properties>
</class>
主キーの代わりに
Person テーブルにある このユニークキーを参照する、レガシーデータの関連を持つかもしれません:
<many-to-one name="person"
class="Person" property-ref="name">
<column name="firstName"/>
<column name="initial"/>
<column name="lastName"/>
</many-to-one>
しかし、このようなレガシーデータマッピングのコンテキスト外への使用は推奨しません。
6.1.17. Subclass リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
ポリモーフィックな永続化には、ルートの永続クラスの各サブクラスを定義する必要があります。table-per-class-hierarchy マッピング戦略では、
<subclass> 定義が使われます。例えば、
<subclass
name="ClassName"
discriminator-value="discriminator_value"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
entity-name="EntityName"
node="element-name"
extends="SuperclassName">
<property .... />
.....
</subclass>
| name:サブクラスの完全修飾されたクラス名。
|
| discriminator-value(オプション - デフォルトはクラス名):個々のサブクラスを区別するための値。
|
| proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェースを指定します。
|
| lazy (オプション、デフォルトは true ): lazy="false" とすると遅延フェッチが使用できません。
|
各サブクラスでは、永続プロパティとサブクラスを宣言します。
<version> と <id> プロパティは、ルートクラスから継承されると仮定されます。階層構造におけるサブクラスは、ユニークな discriminator-value を定義しなければなりません。これが指定されていないと、完全修飾された Java クラス名が使われます。
継承のマッピングに関する情報は 10章継承マッピング を参照してください。
6.1.18. Joined-subclass リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
各サブクラスを自身のテーブルへマッピングすることができ、これは、 table-per-subclass マッピング戦略と呼ばれています。継承した状態はスーパークラスのテーブルを使った結合で検索します。
<joined-subclass> 要素を使用します。例えば、
<joined-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<key .... >
<property .... />
.....
</joined-subclass>
| name:サブクラスの完全修飾されたクラス名。
|
| table :サブクラステーブルの名前。
|
| proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェースを指定します。
|
| lazy (オプション、デフォルトは true ): lazy="false" と設定すると、遅延フェッチが無効になります。
|
このマッピング戦略には、識別カラムは必要ありません。しかし各サブクラスは
<key> 要素を使い、オブジェクト識別子を保持するテーブルカラムを宣言しなければなりません。この章の初めのマッピングは以下のように書き直せます:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>
<class name="eg.Dog">
<!-- mapping for Dog could go here -->
</class>
</hibernate-mapping>
継承のマッピングに関する情報は 10章継承マッピング を参照してください。
6.1.19. Union-subclass リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
3つ目の選択肢は、継承階層の具象クラスのみをテーブルにマッピングすることで、これは、table-per-concrete-class 戦略と呼ばれます。各テーブルは継承の状態を含めすべてのクラスの永続状態を定義します。Hibernate ではその様な継承階層が必要ではなく、単純に各クラスを、別々の
<class> 宣言を使ってマッピングすることができます。しかしポリモーフィックな関連 (例えば階層のスーパークラスへの関連) を使いたい場合、<union-subclass> マッピングを使う必要があります。例えば、
<union-subclass
name="ClassName"
table="tablename"
proxy="ProxyInterface"
lazy="true|false"
dynamic-update="true|false"
dynamic-insert="true|false"
schema="schema"
catalog="catalog"
extends="SuperclassName"
abstract="true|false"
persister="ClassName"
subselect="SQL expression"
entity-name="EntityName"
node="element-name">
<property .... />
.....
</union-subclass>
| name:サブクラスの完全修飾されたクラス名。
|
| table :サブクラステーブルの名前。
|
| proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェースを指定します。
|
| lazy (オプション、デフォルトは true ): lazy="false" と設定すると、遅延フェッチが無効になります。
|
このマッピング戦略では識別カラムやキーカラムは必要ありません。
継承のマッピングに関する情報は 10章継承マッピング を参照してください。
6.1.20. Join リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
テーブル間に一対一の関係があるとき、
<join> 要素を使うことで、1つのクラスのプロパティをいくつかのテーブルにマッピングすることができます。例えば、
<join
table="tablename"
schema="owner"
catalog="catalog"
fetch="join|select"
inverse="true|false"
optional="true|false">
<key ... />
<property ... />
...
</join>
| table :結合したテーブルの名前。
|
| schema (オプション): ルートの <hibernate-mapping> 要素で指定したスキーマ名をオーバーライドします。
|
| catalog (オプション): ルートの <hibernate-mapping> 要素で指定したカタログ名をオーバーライドします。
|
| fetch (オプション - デフォルトは join ): join を設定した場合、 Hibernate はデフォルトで、クラスやスーパークラスで定義された <join> を検索するのに内部結合を使い、サブクラスで定義された <join> を検索するのに外部結合を使います。select を設定した場合には、Hibernate はサブクラスで定義された <join> の選択に順次選択を使います。この場合、行がサブクラスのインスタンスを代表する場合にのみ発行されます。内部結合はクラスやそのスーパークラスで定義された <join> を検索するために使用します。
|
| inverse (オプション - デフォルトは false ): 有効にすると、Hibernate はこの結合で定義されているプロパティに対し挿入や更新を行いません。
|
| optional (オプション - デフォルトは false ): 有効にすると、 Hibernate はこの結合で定義されたプロパティが null でない場合にのみ行を挿入し、そのプロパティの検索には常に外部結合を使用します。
|
例えば、すべてのプロパティに対して値型のセマンティクスを保持しつつ、 人のアドレスの情報を別のテーブルにマッピングすることが可能です:
<class name="Person"
table="PERSON">
<id name="id" column="PERSON_ID">...</id>
<join table="ADDRESS">
<key column="ADDRESS_ID"/>
<property name="address"/>
<property name="zip"/>
<property name="country"/>
</join>
...
この特徴は通常、レガシーデータモデルに対してのみ有用ですが、推奨されるのは、クラスよりも テーブルを少なくし、きめの細かいドメインモデルを利用することです。しかし後で説明するように、1つのクラス階層で継承のマッピング戦略を切り替える時には有用です。
6.1.21. Key リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
このガイドで、今まで何度か
<key> 要素が出てきましたが、親マッピング要素がオリジナルテーブルの主キーを参照する新規テーブルへの結合を定義 する場合、この要素はどこにでも出現し、結合テーブルでも外部キーを定義します。
<key
column="columnname"
on-delete="noaction|cascade"
property-ref="propertyName"
not-null="true|false"
update="true|false"
unique="true|false"
/>
| column (オプション):外部キーカラムの名前。ネストした <column> 要素によっても指定されます。
|
| on-delete (オプション - デフォルトは noaction): 外部キー制約がデータベースレベルでカスケード削除を有効にするかどうかを指定します。
|
| property-ref (オプション): オリジナルテーブルの主キーではないカラムを参照する外部キーを指定します。これはレガシーデータに対して提供されます。
|
| not-null (オプション): 外部キーカラムが null 値を許容しないことを指定します。このことは外部キーが主キーの一部であることを暗黙的に示します。
|
| update (オプション): 外部キーを決して更新してはならないことを指定します。このことは外部キーが主キーの一部であることを暗黙的に示します。
|
| unique (オプション): 外部キーがユニーク制約を持つべきであることを指定します。このことは外部キーが主キーの一部であることを暗黙的に示します、
|
削除のパフォーマンスが重要であるシステムには、すべてのキーを
on-delete="cascade" と定義することを推奨します。そうすることで Hibernate は、多くのDELETE 文ではなくデータベースレベルの ON CASCADE DELETE 制約を使用します。この特徴はバージョン付けられたデータに対する Hibernate の通常の楽観的ロック戦略を無視するということに注意してください。
not-null と update 属性は、単方向一対多関連の時には有用です。単方向一対多関連を null を許容しない外部キーにマッピングするときは、 <key not-null="true"> を使ってキーカラムを宣言 しなくてはなりません。
6.1.22. Column と formula 要素 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
column 属性を受け入れるマッピング要素は、代わりに<column> サブ要素を受け入れます。同様に <formula> も formula 属性の 代わりとなります。例えば、
<column
name="column_name"
length="N"
precision="N"
scale="N"
not-null="true|false"
unique="true|false"
unique-key="multicolumn_unique_key_name"
index="index_name"
sql-type="sql_type_name"
check="SQL expression"
default="SQL expression"/>
<formula>SQL expression</formula>
特殊な結合条件などを表現する、同様のプロパティや関連のマッピングの中で、
column と formula 属性を組み合わせることができます。
<many-to-one name="homeAddress" class="Address"
insert="false" update="false">
<column name="person_id" not-null="true" length="10"/>
<formula>'MAILING'</formula>
</many-to-one>
6.1.23. Import リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
アプリケーションに同名の永続クラスが2つあり、Hibernate クエリで完全修飾された(パッケージの)名前を指定したくない場合は
auto-import="true" に頼らず、明示的にクラスを 「インポート」することができます。また、明示的にマッピングされていないクラスやインターフェースもインポートできます。
<import class="java.lang.Object" rename="Universe"/>
<import
class="ClassName"
rename="ShortName"
/>
| class: Java クラスの完全修飾されたクラス名。
|
| rename(オプション - デフォルトは修飾されていないクラス名):クエリ言語で利用可能な名前。
|
6.1.24. Any リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
プロパティマッピングにはさらにもう1つの型があります。
<any> マッピング要素は、複数のテーブルからクラスへのポリモーフィックな関連を定義します。この型のマッピングには必ず複数のカラムが必要です。1番目のカラムは関連エンティティの型を保持します。残りのカラムは識別子を保持します。この種類の関連には外部キー制約を指定することはできません。これは、ポリモーフィック な関連のマッピングをする通常の方法ではありません。ですから、 検査ログやユーザーセッションデータなど、特別な場合に限りこれを使うべきです。
meta-type により、アプリケーションはカスタム型を指定できます。このカスタム型はデータベースカラムの値を、id-type で指定した型の識別子プロパティを持った永続クラスへマッピングします。meta-type の値からクラス名へのマッピングを指定しなければなりません。
<any name="being" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any>
<any
name="propertyName"
id-type="idtypename"
meta-type="metatypename"
cascade="cascade_style"
access="field|property|ClassName"
optimistic-lock="true|false"
>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
</any>
| name: プロパティ名。
|
| id-type: 識別子の型。
|
| meta-type(オプション - デフォルトは string ):ディスクリミネータマッピングで許されたいずれかの型。
|
| cascade(オプション - デフォルトは none ): カスケードのスタイル。
|
| access (オプション - デフォルトは property ): Hibernate がプロパティの値にアクセスするために使用する戦略。
|
| optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要求するかどうかを指定します。このプロパティがダーティである場合にバージョンを増やすべきかを定義します。
|














