第5章 基本的なO/Rマッピング

5.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">
                <!-- ここにDog用のマッピング書きます -->
        </class>

</hibernate-mapping>

マッピングドキュメントの内容を説明します。。 ただし、ここではHibernateが実行時に使うドキュメント要素と属性についてのみ説明します。 マッピングドキュメントは、いくつかのオプション属性と要素を含んでいます(例えば not-null 属性)。 それらはスキーマエクスポートツールが出力するデータベーススキーマに影響を与えるものです。

5.1.1. Doctype

XMLマッピングでは、お見せしたようなドキュメント型を必ず定義すべきです。 実際のDTDは、上記のURLの hibernate-x.x.x/src/org/hibernate ディレクトリ、 または hibernate.jar 内にあります。 Hibernateは常に、そのクラスパス内でDTDを探し始めます。 インターネットにあるDTDファイルを探そうとしたなら、 クラスパスの内容を見て、DTD宣言を確認してください。

5.1.1.1. ●EntityResolver● ●エンティティ・リゾルバ●

● As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The manner in which it does this is by registering a custom org.xml.sax.EntityResolver implementation with the SAXReader it uses to read in the xml files. This custom EntityResolver recognizes two different systemId namespaces. ● ● 前述したように、Hibernateはまずクラスパス内でDTDを解決しようとします。 org.xml.sax.EntityResolver のカスタム実装を XMLファイルを読み込むためのSAXReaderに登録することによって、DTDを解決します。 このカスタムの EntityResolver は2つの異なるシステムID名前空間を認識します。 ●

  • ● a hibernate namespace is recognized whenever the resolver encounteres a systemId starting with http://hibernate.sourceforge.net/; the resolver attempts to resolve these entities via the classlaoder which loaded the Hibernate classes. ● ● Hibernate名前空間 は、リゾルバが http://hibernate.sourceforge.net/ で始まるシステムIDに到達したときに、 認識されます。 そしてリゾルバは、Hibernateのクラスをロードしたクラスローダを用いて、 これらのエンティティを解決しようとします。 ●

  • ● a user namespace is recognized whenever the resolver encounteres a systemId using a classpath:// URL protocol; the resolver will attempt to resolve these entities via (1) the current thread context classloader and (2) the classloader which loaded the Hibernate classes. ● ● ユーザ名前空間 は、リゾルバが URLプロトコルの classpath:// を使ったシステムIDに到達したときに、 認識されます。そしてリゾルバは、(1)カレントスレッドのコンテキストクラスローダー、 または(2)Hibernateのクラスをロードしたクラスローダを使って、 これらのエンティティを解決しようとします。 ●

● An example of utilizing user namespacing: ● ● 下記は、ユーザ名前空間を使った例です: ●

<?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>

● Where types.xml is a resource in the your.domain package and contains a custom 項5.2.3. 「カスタム型」. ● ● ここで types.xmlyour.domain パッケージ内のリソースであり、 カスタム 項5.2.3. 「カスタム型」 を含むます。 ●

5.1.2. hibernate-mapping

この要素にはいくつかオプション属性があります。schema 属性と catalog 属性は、 このマッピングが参照するテーブルが、この属性によって指定されたスキーマと(または)カタログに属することを指定します。 この属性が指定されると、テーブル名は与えられたスキーマ名とカタログ名で修飾されます。これらの属性が指定されていなければ、 テーブル名は修飾されません。default-cascade 属性は、 cascade 属性を指定していないプロパティやコレクションに、 どのカスケードスタイルを割り当てるかを指定します。 auto-import 属性は、 クエリ言語内で修飾されていないクラス名を、デフォルトで使えるようにします。

<hibernate-mapping
         schema="schemaName"                          (1)
         catalog="catalogName"                        (2)
         default-cascade="cascade_style"              (3)
         default-access="field|property|ClassName"    (4)
         default-lazy="true|false"                    (5)
         auto-import="true|false"                     (6)
         package="package.name"                       (7)
 />
(1)

schema(オプション):データベーススキーマの名前。

(2)

catalog (オプション):データベースカタログの名前。

(3)

default-cascade (オプション - デフォルトは none): デフォルトのカスケードスタイル。

(4)

default-access (オプション - デフォルトは property ): Hibernateがプロパティにアクセスする際に採るべき戦略。 PropertyAccessor を実装することでカスタマイズ可能。

(5)

default-lazy (オプション - デフォルトは true ): lazy 属性が指定されていないクラスやコレクションマッピングに対するデフォルト値。

(6)

auto-import (オプション - デフォルトは true): クエリ言語内で、(このマッピング内のクラスの)修飾されていないクラス名を使えるかどうかを指定します。

(7)

package (オプション): マッピングドキュメント内で修飾されていないクラス名に対して割り当てる、 パッケージの接頭辞(prefix)を指定します。

(修飾されていない)同じ名前の永続クラスが2つあるなら、 auto-import="false" を設定すべきです。 2つのクラスに”インポートされた”同じ名前を割り当てようとすると、Hibernateは例外を投げます。

hibernate-mapping 要素は、最初の例で示したようにいくつかの永続 <class> マッピングをネストできます。 しかし、1つのマッピングファイルではただひとつの永続クラス(またはひとつのクラス階層)にマッピングするようにし、 さらに永続スーパークラスの後で指定するべきでしょう(いくつかのツールはこのようなマッピングファイルを想定しています)。 例えば次のようになります。: Cat.hbm.xml , Dog.hbm.xml , または継承を使うなら Animal.hbm.xml

5.1.3. class

class 要素を使って、永続クラスを宣言できます。

<class
        name="ClassName"                              (1)
        table="tableName"                             (2)
        discriminator-value="discriminator_value"     (3)
        mutable="true|false"                          (4)
        schema="owner"                                (5)
        catalog="catalog"                             (6)
        proxy="ProxyInterface"                        (7)
        dynamic-update="true|false"                   (8)
        dynamic-insert="true|false"                   (9)
        select-before-update="true|false"             (10)
        polymorphism="implicit|explicit"              (11)
        where="arbitrary sql where condition"         (12)
        persister="PersisterClass"                    (13)
        batch-size="N"                                (14)
        optimistic-lock="none|version|dirty|all"      (15)
        lazy="true|false"                             (16)
        entity-name="EntityName"                      (17)
        check="arbitrary sql check condition"         (18)
        rowid="rowid"                                 (19)
        subselect="SQL expression"                    (20)
        abstract="true|false"                         (21)
        entity-name="EntityName"
        node="element-name"
/>
(1)

name (オプション):永続クラス(またはインターフェイス)の完全修飾Javaクラス名。 もしこの属性を指定しなければ、POJOではないエンティティに対するマッピングとして扱われます。

(2)

table (オプション - デフォルトは修飾されていないクラス名):データベーステーブルの名前

(3)

discriminator-value (オプション - デフォルトはクラス名): ポリモーフィックな振る舞いに使われる個々のサブクラスを識別するための値。 値は nullnot null のいずれかを取ります。

(4)

mutable (オプション、 デフォルトは true ): そのクラスのインスタンスが更新可能(または不可能)であることを指定します。

(5)

schema (オプション): ルートの <hibernate-mapping> 要素で指定されたスキーマ名をオーバーライドします。

(6)

catalog (オプション):ルートの <hibernate-mapping> 要素で指定されたカタログ名をオーバーライドします。

(7)

proxy (オプション):遅延初期化プロキシに使うインターフェイスを指定します。 永続化するクラス名そのものを指定することも可能です。

(8)

dynamic-update (オプション、 デフォルトは false ): 値が変更されたカラムだけを含むSQLの UPDATE 文を、実行時に生成することを指定します。

(9)

dynamic-insert (オプション, デフォルトは false ): 値がnullではないカラムだけを含むSQLの INSERT 文を、実行時に生成することを指定します。

(10)

select-before-update (オプション, デフォルトは false): オブジェクトが変更されたのが確実でないならば、HibernateがSQLの UPDATE決して実行しない ことを指定します。 ある特定の場合(実際的には、一時オブジェクトが update() を使い、 新しいセッションと関連付けられた時だけ)、UPDATE が実際に必要かどうかを決定するために、 Hibernateが余分なSQLの SELECT 文を実行することを意味します。

(11)

(optional, デフォルトでは implicit ): implicit(暗黙)かexplicit(明示)の、 どちらのクエリポリモーフィズムを使うか決定します。

(12)

where (オプション): このクラスのオブジェクトを検索するときに使用する、任意のSQLの WHERE 条件を指定します。

(13)

persister (オプション):カスタム ClassPersister を指定します。

(14)

batch-size (オプション, デフォルトは 1 ): 識別子でこのクラスのインスタンスを復元するときの「バッチサイズ」を指定します。

(15)

optimistic-lock (オプション,デフォルトは version ): 楽観ロック戦略を決定します。

(16)

lazy (オプション): lazy="false" と設定することで、 遅延フェッチができなくなります。

(17)

entity-name (オプション、デフォルトはクラス名): Hibernate3ではクラスが複数回マッピングでき(場合によっては違うテーブルに対しても)、 JavaレベルでMapやXMLで表現されるエンティティマッピングが可能です。 これらの場合、エンティティに対して任意の名前を、明示的に付けなくてはなりません。 詳しくは 項4.4. 「動的モデル」章 18. XMLマッピング を参照してください。

(18)

check (オプション):自動的にスキーマを生成するために、 複数行の check 制約を生成するSQL式。

(19)

rowid (オプション):Hibernateは、それをサポートしているデータベースでROWIDと 呼ばれるものを使うことができます。 例えばOracleを使っているとき、このオプションに rowid を設定すれば、 Hiberanteはupdateを高速化するために rowid という特別なカラムを使うことができます。 ROWIDは詳細な実装であり、保存されたタプルの物理的な位置を表しています。

(20)

subselect (オプション):不変かつ読み取り専用であるエンティティを データベースの副問合せ(subselect)にマッピングします。 もし元のテーブルの代わりにビューを持ちたければ有用ですが、 そうでないのなら有用ではありません。より詳しい情報は下記を参照してください。

(21)

abstract (オプション): <union-subclass> 階層内の抽象スーパークラスにマークするために使います。

永続クラスの名前にインターフェイスを指定してもまったく問題ありません。 そのときは <subclass> 要素を使って、 そのインターフェイスを実装するクラスを定義してください。 static な内部クラスでも永続化できます。 そのときは標準形式、例えば eg.Foo$Bar を使ってクラス名を指定してください。

mutable="false" 指定をした不変クラスは、 アプリケーションによる更新や削除が出来ないことがあります。 これにより、Hibernateがパフォーマンスを少し改善します。

オプションの proxy 属性により、クラスの永続インスタンスの遅延初期化が可能になります。 Hibernateは最初に、指定したインターフェイスを実装したCGLIBプロキシを返します。 実際の永続オブジェクトはプロキシのメソッドを呼び出すときにロードします。 以下の「遅延初期化のためのプロキシ」を参照してください。

暗黙的 ポリモーフィズムとは、次の二つを意味しています。 一つはクラスのインスタンスが、スーパークラスや実装したインターフェイス、またそのクラスを指定するクエリによって返されることで、 もう一つはそのクラスのサブクラスのインスタンスが、そのクラス自身を指定したクエリによって返されることです。 また、明示的 ポリモーフィズムとは、次の二つを意味しています。 一つはクラスのインスタンスが、そのクラスを明示的に指定したクエリによってのみ返されることで、 もう一つはクラスを指定したクエリが、<class> 要素の中で <subclass><joined-subclass> とマッピングされているサブクラスのインスタンスだけを返すことです。 ほとんどの用途ではデフォルトの polymorphism="implicit" が適切です。 明示的なポリモーフィズムは、2つの違ったクラスが同じテーブルにマッピングされているときに有用です (これによってテーブルカラムのサブセットを含む、「軽量な」クラスが可能になります)。

persister 属性を指定することで、クラスの永続化戦略をカスタマイズできます。 例えば org.hibernate.persister.EntityPersister 自身のサブクラスを指定したり、 また例えばストアドプロシージャコール、フラットファイルへシリアライズ、 LDAPなどを通した永続性を実装する org.hibernate.persister.ClassPersister インターフェイスの完全に新しい実装を提供できます。簡単な例として org.hibernate.test.CustomPersister を見てください(これは Hashtable の「永続化」です)。

dynamic-updatedynamic-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> は属性とネストしたマッピング属性のどちらでも利用できます。

5.1.4. id

マップされたクラスはデータベーステーブルの主キーカラムを定義 しなければなりません 。 ほとんどのクラスにはインスタンスのユニークな識別子を保持するJavaBeansスタイルのプロパティもあります。 <id> 要素は、そのプロパティから主キーカラムへのマッピングを定義します。

<id
        name="propertyName"                                          (1)
        type="typename"                                              (2)
        column="column_name"                                         (3)
        unsaved-value="null|any|none|undefined|id_value"             (4)
        access="field|property|ClassName">                           (5)
        node="element-name|@attribute-name|element/@attribute|."

        <generator class="generatorClass"/>
</id>
(1)

name(オプション):識別子プロパティの名前。

(2)

type(オプション):Hibernateの型を示す名前。

(3)

column(オプション - デフォルトはプロパティ名): 主キーカラムの名前。

(4)

unsaved-value(オプション - デフォルトの値はsensible): インスタンスが新しくインスタンス化された (セーブされていない)ことを示す、識別子プロパティの値。 以前のSessionでセーブまたはロードされた一時的インスタンスと区別するために 使います。

(5)

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

name 属性がなければ、クラスには識別子プロパティがないものとみなされます。

unsaved-value 属性はHibernate3ではほとんどの場合、必要ではありません。

複合キーを持つレガシーデータにアクセスできるように、 <composite-id> という代替のマッピング定義があります。 しかし他の用途への使用は全くおすすめできません。

5.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は組み込みの実装をいくつも用意しています。 組み込みのジェネレータには以下のショートカット名があります:

increment

long , short , int 型の識別子を生成します。 これらは他のプロセスが同じテーブルにデータを挿入しないときだけユニークです。 クラスタ内では使わないでください

identity

DB2, MySQL, MS SQL Server, Sybase, HypersonicSQLの識別子カラムを サポートします。 返される識別子の型は long , short , int のいずれかです。

sequence

DB2, PostgreSQL, Oracle, SAP DB, McKoiのシーケンスや、Interbaseのジェネレータを使用します。 返される識別子の型は long , short , int のいずれかです。

hilo

long , short , int 型の識別子を効率的に生成するhi/loアルゴリズムを使います。 hi値のソースとして、テーブルとカラムを与えます(デフォルトではそれぞれ hibernate_unique_keynext_hi )。 hi/loアルゴリズムは特定のデータベースに対してのみユニークな識別子を生成します。

seqhilo

long , short , int 型の識別子を効率的に生成するhi/loアルゴリズムを使います。 指定されたデータベースシーケンスを与えます。

uuid

(IPアドレスが使用される)ネットワーク内でユニークな文字列型の識別子を生成するために、 128ビットのUUIDアルゴリズムを使用します。UUIDは長さ32の16進数字の文字列としてエンコードされます。

guid

MS SQLサーバとMySQLでデータベースが生成するGUID文字列を使用します。

native

使用するデータベースの性能により identity , sequence , hilo のいずれかが選ばれます。

assigned

save() が呼ばれる前に、 アプリケーションがオブジェクトに識別子を代入できるようにします。 <generator> が指定されていなければ、これがデフォルトの戦略になります。

select

あるユニークキーによる行の選択と主キーの値の復元により、 データベーストリガが割り当てた主キーを取得します。

foreign

他の関連オブジェクトの識別子を使います。 普通は、<one-to-one> 主キー関連と組み合わせて使います。

5.1.4.2. Hi/lo アルゴリズム

hiloseqhilo ジェネレータは、 識別子生成の代表的なアプローチである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 を適切に設定しなければなりません。

5.1.4.3. UUID アルゴリズム

UUIDには以下のものが含まれます: IPアドレス、JVMのスタートアップタイム(4分の1秒の正確さ)、 システム時間、(JVMに対してユニークな)カウンタ値。 JavaコードからMACアドレスやメモリアドレスを取得することはできないので、 JNIが使えないときの最良の方法です。

5.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つを選択しますが、 これは使用しているデータベースの能力に依存します。

5.1.4.5. 識別子の割り当て

アプリケーションに識別子を割り当てさせたいのであれば(Hibernateが生成するものではなく)、 assigned ジェネレータを使うことができます。 この特別なジェネレータは、すでにオブジェクトの識別子プロパティに代入された値を 識別子に使います。このジェネレータは主キーが代理キーの代わりに自然キーである場合に使用します。 <generator> 要素を指定しない場合のデフォルトの動作になります。

assigned ジェネレータを選択すると、 Hibernateは unsaved-value="undefined" を使用します。 そして、バージョンやタイムスタンプのプロパティがない場合や Interceptor.isUnsaved() を定義しなかった場合には、インスタンスが一時的(transient)なものであるのか、 またはセッションから分離(detached)したものかどうかを決めるために、データベースを調べます。

5.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 という名前の代理キーがあります。

5.1.5. 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 )とエンティティクラス自身の両方が、 medicareNumberdependent という名前のプロパティを持ちます。 識別子クラスは、equals()hashCode() をオーバライドし、 Serializable を実装しなくてはなりません。 この方法には、明らかにコードが重複するという不都合があります。

次の属性はマッピングした複合識別子を指定するために使用します。

  • mapped (オプション、デフォルトは false ): マッピングした複合識別子が使用されることと、包含されたプロパティのマッピングが、 エンティティクラスと複合識別子クラスの両方を参照することを示します。

  • class (オプション,ただしマッピングした複合識別子には必須): 複合識別子として使用するクラス。

3つ目のさらに便利な方法は、複合識別子を項8.4. 「複合識別子としてのコンポーネント」内の コンポーネントクラスとして実装することです。 下で記述している属性は、この代替方法にのみ適用されます。

  • name (オプション, このアプローチでは必須): 複合識別子を保持するコンポーネントタイプのプロパティ(9章を参照してください).

  • access (オプション - デフォルトは property ): Hibernateがプロパティの値にアクセスするために使用すべき戦略。

  • class (オプション - デフォルトはリフレクションにより決定されるプロパティの型): 複合識別子として使われるコンポーネントのクラス(次の節を見てください)。

この3つ目の方法は 識別子コンポーネント と呼び、 ほとんどすべてのアプリケーションに対して推奨する方法です。

5.1.6. discriminator

<discriminator> 要素は、 table-per-class-hierarchyマッピング戦略を使うポリモーフィックな永続化に必要であり、 テーブルの識別カラムを定義します。 識別カラムは、ある行に対して永続層がどのサブクラスをインスタンス化するかを 伝えるマーカー値を含んでいます。 以下のような型に制限されます: string , character , integer, byte , short , boolean , yes_no , true_false.

<discriminator
        column="discriminator_column"                      (1)
        type="discriminator_type"                          (2)
        force="true|false"                                 (3)
        insert="true|false"                                (4)
        formula="arbitrary sql expression"                 (5)
/>
(1)

column(オプション - デフォルトは class ): 識別カラムの名前。

(2)

type (オプション - デフォルトは string ):Hibernateの型を示す名前。

(3)

force (オプション - デフォルトは false ): ルートクラスのすべてのインスタンスを検索する場合であっても、 Hibernateが使用できる識別カラムの指定を「強制」します。

(4)

insert (オプション - デフォルトは true ): もし識別カラムがマッピングする複合識別子の一部ならば、false と設定してください。 (HibernateにSQLの INSERT には含まれないことを知らせる)

(5)

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"/>

5.1.7. version(オプション)

<version> 要素はオプションであり、 テーブルがバージョンデータを含むことを示します。 これは ロングトランザクション を使うつもりなら、特に役立ちます(以下を見てください)。

<version
        column="version_column"                                      (1)
        name="propertyName"                                          (2)
        type="typename"                                              (3)
        access="field|property|ClassName"                            (4)
        unsaved-value="null|negative|undefined"                      (5)
        generated="never|always"                                     (6)
        insert="true|false"                                          (7)
        node="element-name|@attribute-name|element/@attribute|."
/>
(1)

column(オプション - デフォルトはプロパティ名): バージョン番号を保持するカラムの名前。

(2)

name :永続クラスのプロパティの名前。

(3)

type (オプション - デフォルトは integer ):バージョン番号の型。

(4)

access (オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(5)

unsaved-value (オプション - デフォルトは undefined ): インスタンスが新しくインスタンス化されたことを示す (セーブされていないことを示す)バージョンプロパティの値。 以前のSessionでセーブまたはロードされた一時的なインスタンスと区別するために 使います。 ( undefined は識別子プロパティの値が使われることを指定します。)

(6)

generated (オプション - デフォルトは never ): このバージョンのプロパティの値が、データベースによって生成されたことを指定します。 項5.6. 「生成プロパティ」 の議論を見てください。

(7)

insert (オプション - デフォルトは true ): SQLのinsert文にバージョン・カラムを含めるべきかどうかを指定します。 もしデータベース・カラムのデフォルト値が 0 と定義されるときには、 false に設定すると良いでしょう。

バージョン番号は Hibernateの long , integer , short , timestamp , calendar 型のいずれかです。

バージョンやタイムスタンプのプロパティは、分離されたインスタンスに対してnullであってはなりません。 そのためどのような unsaved-value 戦略が指定されても、 Hibernateはnullのバージョンやタイムスタンプを持ったすべてのインスタンスを、 一時的なものであると判断します。 nullを許容するバージョンやタイムスタンプのプロパティを定義することは、 過渡的に一時オブジェクトとすることを防ぐ簡単な方法です。 特に識別子の割り当てや複合キーを使用しているときには特に有用です。

5.1.8. timestamp(オプション)

オプションの <timestamp> 要素は、 テーブルがタイムスタンプデータを含むことを示します。 これはバージョン付けの代わりの方法として用意されています。 タイムスタンプはもともと楽観的ロックにおける安全性の低い実装です。 しかしアプリケーションは異なる用途で使うこともあるかもしれません。

<timestamp
        column="timestamp_column"                                    (1)
        name="propertyName"                                          (2)
        access="field|property|ClassName"                            (3)
        unsaved-value="null|undefined"                               (4)
        source="vm|db"                                               (5)
        generated="never|always"                                     (6)
        node="element-name|@attribute-name|element/@attribute|."
/>
(1)

column(オプション - デフォルトはプロパティ名): タイムスタンプを保持するカラムの名前。

(2)

name : 永続クラスであるJava の Date型 または Timestamp 型 の、JavaBeansスタイルプロパティの名前。

(3)

access (オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(4)

unsaved-value (オプション - デフォルトは null ): インスタンスが新しくインスタンス化された (セーブされていない)ことを示すバージョンプロパティの値。 以前のSessionでセーブまたはロードされた一時的なインスタンスと 区別するために使われます。 ( undefined と指定すると、 識別子プロパティの値が使われます。)

(5)

source (オプション - デフォルトは vm ): Hibernateはどこからタイムスタンプの値を取得するべきでしょうか? データベースからでしょうか、現在のJVMからでしょうか? データベースによるタイムスタンプは、Hibernateが"次の値"を決定するために データベースをヒットしなければならないため、オーバヘッドを招きます。 しかしクラスタ環境ではJVMから取得するより安全です。 データベースの現在のタイムスタンプの取得をサポートする すべての データベース方言 が知られているわけではないことに 注意してください。また一方で、精密さを欠くために、 ロックで使用するには安全でないものもあります(例えばOracle 8)。

(6)

generated (オプション - デフォルトは never ): このタイムスタンプ・プロパティの値が、データベースによって生成されることを指定します。 項5.6. 「生成プロパティ」 を参照してください。

<timestamp><version type="timestamp"> と等価であることに注意してください。 <timestamp source="db"><version type="dbtimestamp"> と等価であることに注意してください。

5.1.9. property

<property> 要素は、クラスの永続的なJavaBeanスタイルのプロパティを定義します。

<property
        name="propertyName"                                          (1)
        column="column_name"                                         (2)
        type="typename"                                              (3)
        update="true|false"                                          (4)
        insert="true|false"                                          (4)
        formula="arbitrary SQL expression"                           (5)
        access="field|property|ClassName"                            (6)
        lazy="true|false"                                            (7)
        unique="true|false"                                          (8)
        not-null="true|false"                                        (9)
        optimistic-lock="true|false"                                 (10)
        generated="never|insert|always"                              (11)
        node="element-name|@attribute-name|element/@attribute|."
        index="index_name"
        unique_key="unique_key_id"
        length="L"
        precision="P"
        scale="S"
/>
(1)

name:小文字で始まるプロパティ名。

(2)

column(オプション - デフォルトはプロパティ名): マッピングされたデータベーステーブルのカラムの名前。 ネストした <column> 要素でも指定できます。

(3)

type(オプション):Hibernateの型を示す名前。

(4)

update, insert (オプション - デフォルトは true ): マッピングされたカラムがSQLの UPDATEINSERT に含まれることを指定します。 両方とも false に設定すると、 同じカラムにマッピングされた他のプロパティやトリガや 他のアプリケーションによって初期化された純粋な「導出」プロパティが可能になります。

(5)

formula(オプション): 計算 プロパティのための値を定義するSQL式。 計算されたプロパティは自身のカラムへのマッピングがありません。

(6)

access(オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(7)

lazy (optional - デフォルトは false ): インスタンス変数に最初にアクセスしたときに、プロパティを遅延して取得するよう指定します。 (バイトコード実装を作成する時間が必要になります)。

(8)

unique (オプション):カラムにユニーク制約をつけるDDLの生成を可能にします。 また、property-ref のターゲットとすることもできます。

(9)

not-null (オプション):カラムにnull値を許可するDDLの生成を可能にします。

(10)

optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要求するかどうかを指定します。 言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。

(11)

generated (オプション - デフォルトは never ): プロパティの値が、データベースによって生成されたことを指定します。 項5.6. 「生成プロパティ」 を参照してください。

typename には以下の値が可能です:

  1. Hibernateの基本型の名前(例 integer, string, character, date, timestamp, float, binary, serializable, object, blob )。

  2. デフォルトの基本型のJavaクラス名 (例 int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob )。

  3. シリアライズ可能なJavaクラスの名前。

  4. カスタム型のクラス名(例 com.illflow.type.MyCustomType )。

型を指定しなければ、Hibernateは正しいHibernateの型を推測するために、 指定されたプロパティに対してリフレクションを使います。 Hibernateはルール2, 3, 4をその順序に使い、 getterプロパティの返り値のクラスの名前を解釈しようとします。 しかしこれで常に十分であるとは限りません。 場合によっては、type 属性が必要な場合があります。 (例えば Hibernate.DATEHibernate.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> マッピング要素を使えることにも注意してください。

5.1.10. many-to-one

他の永続クラスへの通常の関連は many-to-one 要素を使って定義します。 リレーショナルモデルは多対一関連です。 つまりあるテーブルの外部キーは、ターゲットとなるテーブルの主キーカラムを参照しています。

<many-to-one
        name="propertyName"                                          (1)
        column="column_name"                                         (2)
        class="ClassName"                                            (3)
        cascade="cascade_style"                                      (4)
        fetch="join|select"                                          (5)
        update="true|false"                                          (6)
        insert="true|false"                                          (6)
        property-ref="propertyNameFromAssociatedClass"               (7)
        access="field|property|ClassName"                            (8)
        unique="true|false"                                          (9)
        not-null="true|false"                                        (10)
        optimistic-lock="true|false"                                 (11)
        lazy="proxy|no-proxy|false"                                  (12)
        not-found="ignore|exception"                                 (13)
        entity-name="EntityName"                                     (14)
        formula="arbitrary SQL expression"                           (15)
        node="element-name|@attribute-name|element/@attribute|."
        embed-xml="true|false"
        index="index_name"
        unique_key="unique_key_id"
        foreign-key="foreign_key_name"
/>
(1)

name:プロパティ名。

(2)

column (オプション):外部キーカラムの名前。 ネストした <column> 要素でも指定できます。

(3)

class(オプション - デフォルトは、 リフレクションにより決定されるプロパティの型):関連クラスの名前。

(4)

cascade(オプション): どの操作を、親オブジェクトから関連オブジェクトへとカスケードさせるかを指定します。

(5)

fetch (オプション - デフォルトは select ): 外部結合フェッチか順次選択フェッチ(sequential select fetch)を選択します。

(6)

update, insert(オプション - デフォルトは true ): マッピングされたカラムがSQLの UPDATE または INSERT 文に含まれることを指定します。 両方とも false に設定すると、 その値が同じカラムにマッピングされた他のプロパティやトリガや 他のアプリケーションによって初期化された純粋な「導出」プロパティが可能になります。

(7)

property-ref(オプション): この外部キーに結合された関連クラスのプロパティ名。 何も指定しなければ、関連クラスの主キーが使われます。

(8)

access(オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(9)

unique(オプション): 外部キーカラムに対してユニーク制約をつけたDDLの生成を可能にします。 また、property-ref のターゲットにすることもできます。 これにより関連の多重度を効果的に一対一にします。

(10)

not-null (オプション):外部キーカラムに対して、 null値を許可するDDLの生成を可能にします

(11)

optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観的ロックの取得を要求するかどうかを指定します。 言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。

(12)

lazy (オプション - デフォルトは proxy ): デフォルトでは、多重度1の関連がプロキシとなります。 lazy="no-proxy" は、インスタンス変数に最初にアクセスしたときに、 プロパティを遅延フェッチするよう指定します (ビルド時にバイトコード実装が必要になります)。 lazy="false" は関連を常に即時にフェッチするよう指定します。

(13)

not-found (オプション - デフォルトは exception ): 欠落した行を参照する外部キーをどのように扱うかを指定します。 ignore は欠落した行をnull関連として扱います。

(14)

entity-name (オプション):関連したクラスのエンティティ名。

cascade 属性に none 以外の意味のある値をを設定すると、 関連オブジェクトへある操作が伝播することになります。 意味のある値とはHibernateの基本操作の名前のことで、 delete-orphanall 、操作名をカンマで区切った組み合わせ (例えば cascade="persist,merge,evict"cascade="all,delete-orphan")、 またそれだけでなく persist, merge, delete, save-update, evict, replicate, lock, refresh のことを指します。 詳しい説明は 項10.11. 「連鎖的な永続化」 を見てください。 値が一つの関連(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"/>

5.1.11. one-to-one

他の永続クラスへの一対一関連は、one-to-one 要素で定義します。

<one-to-one
        name="propertyName"                                          (1)
        class="ClassName"                                            (2)
        cascade="cascade_style"                                      (3)
        constrained="true|false"                                     (4)
        fetch="join|select"                                          (5)
        property-ref="propertyNameFromAssociatedClass"               (6)
        access="field|property|ClassName"                            (7)
        formula="any SQL expression"                                 (8)
        lazy="proxy|no-proxy|false"                                  (9)
        entity-name="EntityName"                                     (10)
        node="element-name|@attribute-name|element/@attribute|."
        embed-xml="true|false"
        foreign-key="foreign_key_name"
/>
(1)

name:プロパティ名。

(2)

class(オプション - デフォルトはリフレクションにより決定されるプロパティの型): 関連クラスの名前。

(3)

cascade(オプション): 親オブジェクトから関連オブジェクトへ、どの操作をカスケードするかを指定します。

(4)

constrained(オプション): マッピングされたテーブルの主キーに対する外部キー制約が、 関連クラスのテーブルを参照することを指定します。 このオプションは save()delete() がカスケードされる順序に影響し、 そして関連がプロキシされるかどうかにも影響します (そしてスキーマエクスポートツールにも使われます)。

(5)

fetch(オプション - デフォルトは select ): 外部結合フェッチと順次選択フェッチ(sequential select fetch)のどちらかを選択します。

(6)

property-ref(オプション): このクラスの主キーに結合された関連クラスのプロパティ名。 指定されなければ、関連クラスの主キーが使われます。

(7)

access(オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(8)

formula (オプション): ほとんどすべての一対一関連はオーナーのエンティティの主キーへとマッピングされます。 これ以外の稀な場合は、 他のカラムや、複数のカラム、SQL構文を使った結合するための式を指定できます。 (例は org.hibernate.test.onetooneformula を参照してください。)

(9)

lazy (オプション - デフォルトは proxy ): デフォルトでは、多重度1の関連がプロキシとなります。 lazy="no-proxy" は、インスタンス変数に最初にアクセスしたときに、 プロパティを遅延フェッチするよう指定します (ビルド時にバイトコード実装が必要になります)。 lazy="false" は関連を常に即時にフェッチするよう指定します。 もし constrained="false" ならば、 プロキシは使用不可能となり、関連を即時にフェッチすることに注意してください!

(10)

entity-name (オプション):関連クラスのエンティティ名

一対一関連には2種類あります:

  • 主キー関連

  • ユニーク外部キー関連

主キー関連には、特別なテーブルカラムは必要ありません。 もし2つの行が関連により関係していれば、2つのテーブルは同じ主キーの値を共有します。 そのため2つのオブジェクトを主キー関連によって関連付けたいのであれば、 確実に同じ識別子の値を代入しなければなりません。

主キー関連を行うためには、以下のマッピングを EmployeePerson のそれぞれに追加してください。

<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>

Employee インスタンスが、Personemployee プロパティで参照されるように、 新しくセーブされた Person のインスタンスには同じ主キーの値が代入されます。 新しくセーブする Person インスタンスは、 その Personemployee プロパティが参照する 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"/>

5.1.12. natural-id

<natural-id mutable="true|false"/>
        <property ... />
        <many-to-one ... />
        ......
</natural-id>

主キーとして代理キーの使用を推奨しますが、 すべてのエンティティに対して自然キーを識別するようにすべきです。 自然キーはユニークかつ非nullな一つのプロパティ、またはプロパティの連結です。 不変であればさらに良いです。 <natural-id> 要素内で自然キーのプロパティをマッピングします。 Hibernateは必然的にユニークかつnull値を許可する制約を生成し、 こうしてマッピングはより自己記述的になります。

エンティティの自然キープロパティの比較には、 equals()hashCode() の実装を強くお勧めします。

このマッピングは自然主キーを使ったエンティティでの使用を意図していません。

  • mutable (オプション, デフォルトは false ): デフォルトでは、自然識別子プロパティは不変(定数)と想定されています。

5.1.13. component, dynamic-component

<component> 要素は、 子オブジェクトのプロパティを親クラスのテーブルのカラムへマッピングします。 コンポーネントは自分のプロパティ、コンポーネント、コレクションの順に定義できます。 以下の「コンポーネント」を見てください。

<component 
        name="propertyName"                 (1)
        class="className"                   (2)
        insert="true|false"                 (3)
        update="true|false"                 (4)
        access="field|property|ClassName"   (5)
        lazy="true|false"                   (6)
        optimistic-lock="true|false"        (7)
        unique="true|false"                 (8)
        node="element-name|."
>
        
        <property ...../>
        <many-to-one .... />
        ........
</component>
(1)

name:プロパティ名。

(2)

class (オプション - デフォルトはリフレクションにより決定されるプロパティの型): コンポーネント(子)クラスの名前。

(3)

insert :マッピングされたカラムがSQLの INSERT に現れるようにするどうかを指定します。

(4)

update : マッピングされたカラムがSQL の UPDATE に現れるようにするかどうかを指定します。

(5)

access (オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(6)

lazy (optional - デフォルトは false ): インスタンス変数に最初にアクセスしたときに、 コンポーネントを遅延してフェッチするよう指定します。 (バイトコード実装を作成する時間が必要になります)

(7)

optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に、楽観ロックの取得を要求するかどうかを指定します。 言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。

(8)

unique (オプション - デフォルトは false ): コンポーネントのすべてのマッピングするカラムに、ユニーク制約が存在するかを指定します。

子の <property> タグで、 子のクラスのプロパティをテーブルカラムにマッピングします。

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

<dynamic-component> 要素は、 Map がコンポーネントとしてマッピングされることを可能にします。 プロパティ名はmapのキーを参照します。項8.5. 「動的コンポーネント」 を参照してください。

5.1.14. properties

<properties> 要素はクラスのプロパティの指定された、 論理的なグルーピングを可能にします。 この構造の最も重要な使用方法は、 property-ref のターゲットになるプロパティの結合を許可することです。 それはまた、複数カラムのユニーク制約を定義する簡単な方法でもあります。

<properties 
        name="logicalName"                  (1)
        insert="true|false"                 (2)
        update="true|false"                 (3)
        optimistic-lock="true|false"        (4)
        unique="true|false"                 (5)
>
        
        <property ...../>
        <many-to-one .... />
        ........
</properties>
(1)

name : グルーピングの論理名。 実際のプロパティ名では ありません

(2)

insert:マッピングされたカラムがSQLの INSERT に現れるようにするかどうかを指定します。

(3)

update:マッピングされたカラムがSQLの UPDATE に現れるようにするかどうかを指定します。

(4)

optimistic-lock (オプション - デフォルトは true ): これらのプロパティの更新に楽観的ロックの取得を要求するかどうかを指定します。 言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを決定します。

(5)

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>

しかし、このようなレガシーデータマッピングのコンテキスト外への使用は推奨しません。

5.1.15. subclass

最後にポリモーフィックな永続化には、ルートの永続クラスの各サブクラスの定義が必要です。 table-per-class-hierarchyマッピング戦略では、 <subclass> 定義が使われます。

<subclass
        name="ClassName"                              (1)
        discriminator-value="discriminator_value"     (2)
        proxy="ProxyInterface"                        (3)
        lazy="true|false"                             (4)
        dynamic-update="true|false"
        dynamic-insert="true|false"
        entity-name="EntityName"
        node="element-name"
        extends="SuperclassName">

        <property .... />
        .....
</subclass>
(1)

name:サブクラスの完全修飾されたクラス名。

(2)

discriminator-value(オプション - デフォルトはクラス名): 個々のサブクラスを区別するための値。

(3)

proxy(オプション): 遅延初期化プロキシに使うクラスやインターフェイスを指定します。

(4)

lazy(オプション, デフォルトは true ): lazy="false" と設定すると、遅延フェッチが使用できません。

各サブクラスでは、永続プロパティとサブクラスを定義します。 <version><id> プロパティは、 ルートクラスから継承されると仮定されます。 階層構造におけるサブクラスは、 ユニークな discriminator-value を定義しなければなりません。 noneが指定されると、完全修飾されたJavaクラス名が使われます。

継承のマッピングに関する情報は 章 9. 継承マッピング を見てください。

5.1.16. joined-subclass

もう1つの方法として、各サブクラスを自身のテーブルへマッピングすることができます (table-per-subclass mapping strategy)。 継承した状態はスーパークラスのテーブルを使った結合で検索します。 <joined-subclass> 要素を使用します。

<joined-subclass
        name="ClassName"                    (1)
        table="tablename"                   (2)
        proxy="ProxyInterface"              (3)
        lazy="true|false"                   (4)
        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>
(1)

name:サブクラスの完全修飾されたクラス名。

(2)

table :サブクラステーブルの名前。

(3)

proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェイスを指定します。

(4)

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">
                <!-- ここにDogのマッピングを書きます -->
        </class>

</hibernate-mapping>

継承のマッピングに関する情報は 章 9. 継承マッピング を見てください。

5.1.17. union-subclass

3つ目の選択肢は、継承階層の具象クラスのみをテーブルにマッピングすることです (the table-per-concrete-class戦略)。 それぞれのテーブルは継承の状態を含めすべてのクラスの永続状態を定義します。 Hibernateではその様な継承階層が必ずしも必要ではありません。 単純にそれぞれのクラスを、 別々の <class> 宣言を使ってマッピングすることができます。 しかしポリモーフィックな関連(例えば 階層のスーパークラスへの関連)を使いたいなら、 <union-subclass> マッピングを使う必要があります。

<union-subclass
        name="ClassName"                    (1)
        table="tablename"                   (2)
        proxy="ProxyInterface"              (3)
        lazy="true|false"                   (4)
        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>
(1)

name:サブクラスの完全修飾されたクラス名。

(2)

table :サブクラステーブルの名前。

(3)

proxy (オプション): 遅延初期化プロキシに使用するクラスやインターフェイスを指定します。

(4)

lazy (オプション, デフォルトは true ): lazy="false" とすると、遅延フェッチが使用できません。

このマッピング戦略では識別カラムやキーカラムは必要ありません。

継承のマッピングに関する情報は 章 9. 継承マッピング を見てください。

5.1.18. join

<join> 要素を使うことで、 1つのクラスのプロパティをいくつものテーブルにマッピングすることができます。

<join
        table="tablename"                        (1)
        schema="owner"                           (2)
        catalog="catalog"                        (3)
        fetch="join|select"                      (4)
        inverse="true|false"                     (5)
        optional="true|false">                   (6)
        
        <key ... />
        
        <property ... />
        ...
</join>
(1)

table :結合したテーブルの名前

(2)

schema (オプション): ルートの <hibernate-mapping> 要素で指定したスキーマ名を オーバーライドします。

(3)

catalog (オプション): ルートの <hibernate-mapping> 要素で指定したカタログ名を オーバーライドします。

(4)

fetch (オプション - デフォルトは join ): join を設定した場合、 Hibernateはデフォルトで、クラスやスーパークラスで定義された <join> を検索するのに内部結合を使い、サブクラスで定義された <join> を検索するのに外部結合を使います。 select を設定した場合には、 Hibernateはサブクラスで定義された <join> の選択に順次選択を使います。この場合、 行がサブクラスのインスタンスを代表することがわかった場合にのみ発行されます。 内部結合はクラスやそのスーパークラスで定義された <join> を検索 するために使用します。

(5)

inverse (オプション - デフォルトは false ): もし可能であれば、Hibernateはこの結合で定義されているプロパティに対し 挿入や更新を行いません。

(6)

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つのクラス階層で継承のマッピング戦略を切り替える時には有用です。

5.1.19. key

今まで何度か <key> 要素が出てきました。 この要素は新しいテーブルへの結合を定義したり、 結合テーブルで外部キーを定義したりする親要素のどこにでも現れ、 オリジナルテーブルの主キーを参照します。

<key
        column="columnname"                      (1)
        on-delete="noaction|cascade"             (2)
        property-ref="propertyName"              (3)
        not-null="true|false"                    (4)
        update="true|false"                      (5)
        unique="true|false"                      (6)
/>
(1)

column (オプション):外部キーカラムの名前。 ネストした <column> カラムによっても指定されます。

(2)

on-delete (オプション, デフォルトは noaction): 外部キー制約がデータベースレベルでカスケード削除が可能かどうかを指定します。

(3)

property-ref (オプション): オリジナルテーブルの主キーではないカラムを参照する外部キーを指定します (レガシーデータに対して提供されます)。

(4)

not-null (オプション): 外部キーカラムがnull値を許容しないことを指定します (このことは外部キーが主キーの一部であることを暗黙的に示します)。

(5)

update (オプション): 外部キーを決して更新してはならないことを指定します (このことは外部キーが主キーの一部であることを暗黙的に示します)。

(6)

unique (オプション): 外部キーがユニーク制約を持つべきであることを指定します (このことは外部キーが主キーの一部であることを暗黙的に示します)。

削除のパフォーマンスが重要であるシステムには、 すべてのキーを on-delete="cascade" と定義することを推奨します。 そうすることでHibernateは、DELETE 文を毎回発行する代わりに、 データベースレベルの ON CASCADE DELETE 制約を使用します。 この特徴はバージョン付けられたデータに対するHibernateの通常の楽観的ロック戦略を 無視するということに注意してください。

not-nullupdate 属性は、単方向一対多関連の時には有用です。 単方向一対多関連をnullを許容しない外部キーにマッピングするときは、 <key not-null="true"> を使ってキーカラムを宣言 しなくてはなりません

5.1.20. 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>

同じプロパティや関連のマッピングの中で、 columnformula 属性を組み合わせることができます。 例えば、特殊な結合条件などです。

<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>

5.1.21. import

アプリケーションに同じ名前の2つの永続クラスがあり、 Hibernateクエリで完全修飾された(パッケージの)名前を指定したくないと仮定します。 そのような場合は auto-import="true" に頼らず、 クラスが「インポート」されたものであると明示できます。 明示的にマッピングされていないクラスやインターフェイスでさえもインポートできます。

<import class="java.lang.Object" rename="Universe"/>
<import
        class="ClassName"              (1)
        rename="ShortName"             (2)
/>
(1)

class:Javaクラスの完全修飾されたクラス名。

(2)

rename(オプション - デフォルトは修飾されていないクラス名): クエリ言語で使われる名前。

5.1.22. 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"                      (1)
        id-type="idtypename"                     (2)
        meta-type="metatypename"                 (3)
        cascade="cascade_style"                  (4)
        access="field|property|ClassName"        (5)
        optimistic-lock="true|false"             (6)
>
        <meta-value ... />
        <meta-value ... />
        .....
        <column .... />
        <column .... />
        .....
</any>
(1)

name:プロパティ名。

(2)

id-type:識別子の型。

(3)

meta-type(オプション - デフォルトは string ): ディスクリミネータマッピングで許された型

(4)

cascade(オプション - デフォルトは none ): カスケードのスタイル。

(5)

access(オプション - デフォルトは property ): プロパティの値へのアクセスにHibernateが使う戦略。

(6)

optimistic-lock (オプション - デフォルトは true ): このプロパティの更新に楽観ロックの取得を要求するかどうかを指定します。 言い換えれば、このプロパティがダーティであるときにバージョンを増やすべきかを定義します。

5.2. Hibernateの型

5.2.1. エンティティと値

永続サービスに関わる様々なJava言語レベルのオブジェクトの振る舞いを理解するためには、 オブジェクトを2つのグループに分ける必要があります:

エンティティ はエンティティへの参照を保持する、 他のすべてのオブジェクトから独立して存在します。 参照されないオブジェクトがガベージコレクトされてしまう性質を持つ通常のJavaモデルと、 これを比べてみてください。 (親エンティティから子へ、セーブと削除が カスケード されうることを除いて) エンティティは明示的にセーブまたは削除されなければなりません。 これは到達可能性によるオブジェクト永続化のODMGモデルとは異なっています。 大規模なシステムでアプリケーションオブジェクトが普通どのように使われるかにより密接に対応します。 エンティティは循環と参照の共有をサポートします。 またそれらはバージョン付けすることもできます。

エンティティの永続状態は他のエンティティや 型の インスタンスへの参照から構成されます。 値はプリミティブ、コレクション(コレクションの内部ではなく)、 コンポーネント、不変オブジェクトです。 エンティティとは違い、値は(特にコレクションとコンポーネントにおいて)、 到達可能性による永続化や削除が 行われます 。 値オブジェクト(とプリミティブ)は、包含するエンティティと一緒に永続化や削除が行われるので、 それらを独立にバージョン付けすることはできません。 値には独立したアイデンティティがないので、 複数のエンティティやコレクションがこれを共有することはできません。

これまで「永続クラス」という言葉をエンティティの意味で使ってきました。 これからもそうしていきます。 厳密に言うと、永続状態を持つユーザ定義のクラスのすべてが エンティティというわけではありません。 コンポーネント は値のセマンティクスを持つユーザ定義クラスです。 java.lang.String 型のプロパティもまた値のセマンティクスを持ちます。 定義するなら、JDKで提供されているすべてのJavaの型(クラス)が値のセマンティクスを持つといえます。 一方ユーザ定義型は、エンティティや値型のセマンティクスとともにマッピングできます。 この決定はアプリケーション開発者次第です。 そのクラスの1つのインスタンスへの共有参照は、 ドメインモデル内のエンティティクラスに対する良いヒントになります。 一方合成集約や集約は、通常値型へ変換されます。

本ドキュメントを通して、何度もこの概念を取り上げます。

Java型のシステム(もしくは開発者が定義したエンティティと値型)を SQL/データベース型のシステムにマッピングすることは難しいです。 Hibernateは2つのシステムの架け橋を提供します。 エンティティに対しては <class><subclass> などを使用します。 値型に対しては <property><component> などを、通常 type とともに使います。 この属性の値はHibernateの マッピング型 の名前です。 Hibernateは(標準JDKの値型に対して)多くの自由なマッピングを提供します。 後で見るように、自身のマッピング型を記述し、同様にカスタムの変換戦略を実装することができます。

コレクションを除く組み込みのHibernateの型はすべて、nullセマンティクスをサポートします。

5.2.2. 基本的な型

組み込みの 基本的なマッピング型 は大まかに以下のように分けられます。

integer, long, short, float, double, character, byte, boolean, yes_no, true_false

Javaのプリミティブやラッパークラスから適切な(ベンダー固有の) SQLカラム型への型マッピング。 boolean, yes_notrue_false は、 すべてJavaの boolean または java.lang.Boolean の代替エンコードです。

string

java.lang.String から VARCHAR (またはOracleの VARCHAR2 )への型マッピング。

date, time, timestamp

java.util.Date とそのサブクラスからSQL型の DATE, TIME , TIMESTAMP (またはそれらと等価なもの) への型マッピング。

calendar, calendar_date

java.util.Calendar からSQL型 の「 TIMESTAMP , DATE (またはそれらと等価なもの)への型マッピング。

big_decimal, big_integer

java.math.BigDecimaljava.math.BigInteger から NUMERIC(またはOracleの NUMBER )への型マッピング。

locale, timezone, currency

java.util.Locale , java.util.TimeZone , java.util.Currency から VARCHAR (またはOracleの VARCHAR2 )への型マッピング。 LocaleCurrency のインスタンスは、 それらのISOコードにマッピングされます。 TimeZone のインスタンスは、 それらの ID にマッピングされます。

class

java.lang.Class から VARCHAR (またはOracleの VARCHAR2 )への型マッピング。 Class はその完全修飾された名前にマッピングされます。

binary

バイト配列は、適切なSQLのバイナリ型にマッピングされます。

text

長いJava文字列は、SQLの CLOB または TEXT 型にマッピングされます。

serializable

シリアライズ可能なJava型は、適切なSQLのバイナリ型にマッピングされます。 デフォルトで基本型ではないシリアライズ可能なJavaクラスや インターフェイスの名前を指定することで、 Hibernateの型を serializable とすることもできます。

clob, blob

JDBCクラス java.sql.Clobjava.sql.Blob に対する型マッピング。 blobやclobオブジェクトはトランザクションの外では再利用できないため、 アプリケーションによっては不便かもしれません。 (さらにはドライバサポートが一貫していません。)

imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary

ほとんどの場合に可変であるJavaの型に対する型マッピング。 Hibernateは不変なJavaの型に対しては最適化を行い、 アプリケーションはそれを不変オブジェクトとして扱います。 例えば imm_timestamp としてマップしたインスタンスに対して、 Date.setTime() を呼び出してはなりません。 プロパティの値を変更しその変更を永続化するためには、 アプリケーションはプロパティに対して新しい(同一でない)オブジェクトを割り当てなければなりません。

エンティティとコレクションのユニークな識別子は、binary , blob , clob を除く、どんな基本型でも構いません。 (複合識別子でも構いません。以下を見てください。)

基本的な値型には、org.hibernate.Hibernate で定義された Type 定数がそれぞれあります。 例えば、Hibernate.STRINGstring 型を表現しています。

5.2.3. カスタム型

開発者が独自の値型を作成することは、比較的簡単です。 例えば、java.lang.BigInteger 型のプロパティを VARCHAR カラムに永続化したいかもしれません。 Hibernateはこのための組み込み型を用意していません。 しかしカスタム型は、プロパティ(またはコレクションの要素)を1つのテーブルカラムに マッピングするのに制限はありません。 そのため例えば、java.lang.String 型の getName() / setName() Javaプロパティを FIRST_NAME , INITIAL, SURNAME カラムに永続化できます。

カスタム型を実装するには、org.hibernate.UserType または org.hibernate.CompositeUserType を実装し、 型の完全修飾された名前を使ってプロパティを定義します。 どのような種類のものが可能かを調べるには、 org.hibernate.test.DoubleStringType を確認してください。

<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
    <column name="first_string"/>
    <column name="second_string"/>
</property>

<column> タグで、 プロパティを複数のカラムへマッピングできることに注目してください。

CompositeUserType , EnhancedUserType , UserCollectionType , UserVersionType インターフェイスは、より特殊な使用法に対してのサポートを提供します。

マッピングファイル内で UserType へパラメータを提供できます。 このためには、UserTypeorg.hibernate.usertype.ParameterizedType を実装しなくてはなりません。 カスタム型パラメータを提供するために、 マッピングファイル内で <type> 要素を使用できます。

<property name="priority">
    <type name="com.mycompany.usertypes.DefaultValueIntegerType">
        <param name="default">0</param>
    </type>
</property>

UserType は、 引数として渡された Properties オブジェクトから、 default で指定したパラメータに対する値を検索することができます。

特定の UserType を頻繁に使用するならば、短い名前を定義すると便利になるでしょう。 <typedef> 要素を使ってこのようなことが行えます。 Typedefsはカスタム型に名前を割り当てます。 その型がパラメータを持つならば、 パラメータのデフォルト値のリストを含むこともできます。

<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
    <param name="default">0</param>
</typedef>
<property name="priority" type="default_zero"/>

プロパティのマッピングで型パラメータを使うことで、 typedefで提供されたパラメータをその都度オーバーライドすることが可能です。

Hibernateの幅広い組み込み型とコンポーネントに対するサポートは、 カスタム型をめったに 使わない ということを意味します。 それでもなお、アプリケーションで頻出する(エンティティではない)クラスに対するカスタム型の使用は、 よいやり方であるとみなされます。 例えば MonetaryAmount クラスはコンポーネントとして簡単にマッピングできますが、 CompositeUserType の良い候補です。 カスタム型を使用する動機の1つは抽象化です。 カスタム型を使うことで、通貨をどのように表現しようとも マッピングドキュメントは起こりうる変化に対応できます。

5.3. 1つのクラスに1つ以上のマッピング

ある永続クラスに、一つ以上のマッピングを提供することが出来ます。 この場合、マッピングする2つのエンティティのインスタンスを明確にするために、 エンティティ名 を指定しなければなりません (デフォルトではエンティティ名はクラス名と同じです。)。 永続オブジェクトを扱うとき、クエリを書き込むとき、 指定されたエンティティへの関連をマッピングするときには、 Hibernateではエンティティ名を指定しなければなりません。

<class name="Contract" table="Contracts" 
        entity-name="CurrentContract">
    ...
    <set name="history" inverse="true" 
            order-by="effectiveEndDate desc">
        <key column="currentContractId"/>
        <one-to-many entity-name="HistoricalContract"/>
    </set>
</class>

<class name="Contract" table="ContractHistory" 
        entity-name="HistoricalContract">
    ...
    <many-to-one name="currentContract" 
            column="currentContractId" 
            entity-name="CurrentContract"/>
</class>

関連がclass の代わりに entity-name を使って、 どのように指定されるのかに注目してください。

5.4. バッククォートで囲んだ SQL 識別子

マッピングドキュメントでテーブルやカラムの名前をバッククォートで囲むことで、 Hibernateで生成されたSQL中の識別子を引用させることができます。 HibernateはSQLの Dialect に対応する、正しい引用スタイルを使います (普通はダブルクォートですが、SQL Serverではかぎ括弧、MySQLではバッククォートです)。

<class name="LineItem" table="`Line Item`">
    <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
    <property name="itemNumber" column="`Item #`"/>
    ...
</class>

5.5. メタデータの代替手段

XMLの記述以外に、 HibernateではO/Rマッピングのメタデータを定義する代替方法があります。

5.5.1. XDoclet マークアップの使用

多くのHibernateユーザはXDocletの @hibernate.tags を使って、 ソースコード内に直接マッピング情報を埋め込むことを好みます。 これは厳密に言えばXDocletの分野なので、本ドキュメントではこの方法を対象とはしません。 しかしXDocletを使った以下の Cat マッピングの例を示します。

package eg;
import java.util.Set;
import java.util.Date;

/**
 * @hibernate.class
 *  table="CATS"
 */
public class Cat {
    private Long id; // identifier
    private Date birthdate;
    private Cat mother;
    private Set kittens
    private Color color;
    private char sex;
    private float weight;

    /*
     * @hibernate.id
     *  generator-class="native"
     *  column="CAT_ID"
     */
    public Long getId() {
        return id;
    }
    private void setId(Long id) {
        this.id=id;
    }

    /**
     * @hibernate.many-to-one
     *  column="PARENT_ID"
     */
    public Cat getMother() {
        return mother;
    }
    void setMother(Cat mother) {
        this.mother = mother;
    }

    /**
     * @hibernate.property
     *  column="BIRTH_DATE"
     */
    public Date getBirthdate() {
        return birthdate;
    }
    void setBirthdate(Date date) {
        birthdate = date;
    }
    /**
     * @hibernate.property
     *  column="WEIGHT"
     */
    public float getWeight() {
        return weight;
    }
    void setWeight(float weight) {
        this.weight = weight;
    }

    /**
     * @hibernate.property
     *  column="COLOR"
     *  not-null="true"
     */
    public Color getColor() {
        return color;
    }
    void setColor(Color color) {
        this.color = color;
    }
    /**
     * @hibernate.set
     *  inverse="true"
     *  order-by="BIRTH_DATE"
     * @hibernate.collection-key
     *  column="PARENT_ID"
     * @hibernate.collection-one-to-many
     */
    public Set getKittens() {
        return kittens;
    }
    void setKittens(Set kittens) {
        this.kittens = kittens;
    }
    // addKitten not needed by Hibernate
    public void addKitten(Cat kitten) {
        kittens.add(kitten);
    }

    /**
     * @hibernate.property
     *  column="SEX"
     *  not-null="true"
     *  update="false"
     */
    public char getSex() {
        return sex;
    }
    void setSex(char sex) {
        this.sex=sex;
    }
}

Hibernateのウェブサイトには、XDocletとHibernateに関するサンプルが多数あります。

5.5.2. JDK 5.0 アノテーションの使用

JDK5.0ではタイプセーフかつコンパイル時にチェックできる、 言語レベルのXDocletスタイルのアノテーションを導入しました。 このメカニズムはXDocletのアノテーションよりも強力で、ツールやIDEも多くがサポートしています。 例えばIntelliJ IDEAは、JDK5.0にアノテーションの自動補完と構文の強調表示をサポートしています。 EJB仕様 (JSR-220)の新しいバージョンでは、エンティティビーンに対する主要なメタデータメカニズムとして JDK5.0のアノテーションを使用しています。 Hibernate3ではJSR-220 (永続化API)の EntityManager を実装し、 メタデータマッピングに対するサポートは、 別ダウンロードの Hibernate Annotations パッケージにより利用可能です。 これはEJB3(JSR-220)とHibernate3のメタデータをどちらもサポートしています。

以下はEJBのエンティティビーンとして注釈されたPOJOクラスの例です。

@Entity(access = AccessType.FIELD)
public class Customer implements Serializable {

    @Id;
    Long id;

    String firstName;
    String lastName;
    Date birthday;

    @Transient
    Integer age;

    @Embedded
    private Address homeAddress;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="CUSTOMER_ID")
    Set<Order> orders;

    // Getter/setter and business methods
}

JDK5.0のアノテーション(とJSR-220)のサポートは進行中の作業であり、完全ではないことに注意してください。 さらに詳しい情報はHibernateのアノテーション・モジュールを参照してください。

5.6. 生成プロパティ

生成プロパティとは、データベースによって生成された値を持つプロパティです。 通常、Hibernateアプリケーションは、データベースが値を生成したプロパティを含むオブジェクトを リフレッシュ する必要がありました。 しかし、プロパティが生成されたということをマークすることで、 アプリケーションはリフレッシュの責任をHibernateに委譲します。 基本的に、生成プロパティを持つと定義したエンティティに対して HibernateがINSERTやUPDATEのSQLを発行した後すぐに、 生成された値を読み込むための SELECT SQL が発行されます。

生成プロパティは、挿入不可能かつ更新不可能でなければなりません。 項5.1.7. 「version(オプション)」項5.1.8. 「timestamp(オプション)」項5.1.9. 「property」 だけが生成されたとマークできます。

never (デフォルト) - 与えられたプロパティの値は、 データベースから生成されないことを意味します。

insert - 与えられたプロパティの値は挿入時に生成されるが、 続いて起こる更新時には生成されないこと示します。 作成された日付などは、このカテゴリに分類されます。 項5.1.7. 「version(オプション)」項5.1.8. 「timestamp(オプション)」 の プロパティは生成されたとマークできますが、このオプションは利用できないことに注意してください。

always - 挿入時も更新時もプロパティの値が生成されることを示します。

5.7. 補助的なデータベース・オブジェクト

Hibernateのスキーマ・エボリューションツールと連動することで、 任意のデータベース・オブジェクト(トリガーやストアドプロシージャなど)のCREATEとDROPにより、 Hibernateのマッピングファイル内のユーザ・スキーマをすべて定義することが出来ます。 主にトリガやストアドプロシージャのようなデータベース・オブジェクトを生成や削除することを意図していますが、 実際には java.sql.Statement.execute() メソッドによって実行できる 任意のSQLコマンド(ALTER、INSERTなど)が実行できます。 補助的なデータベース・オブジェクトを定義するための、2つの基本的な方法があります。

1つ目の方法は、CREATEとDROPコマンドをマッピングファイルの外に、明示的に記載することです。

<hibernate-mapping>
    ...
    <database-object>
        <create>CREATE TRIGGER my_trigger ...</create>
        <drop>DROP TRIGGER my_trigger</drop>
    </database-object>
</hibernate-mapping>

2つ目の方法は、CREATEとDROPコマンドの組み立て方を知っているカスタムクラスを提供することです。 このカスタムクラスは org.hibernate.mapping.AuxiliaryDatabaseObject インタフェースを 実装しなければなりません。

<hibernate-mapping>
    ...
    <database-object>
        <definition class="MyTriggerDefinition"/>
    </database-object>
</hibernate-mapping>

さらに、あるデータベース方言が使用される時にだけ適用するといったように、 データベース・オブジェクトが使われるケースを限定できます。

<hibernate-mapping>
    ...
    <database-object>
        <definition class="MyTriggerDefinition"/>
        <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
        <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
    </database-object>
</hibernate-mapping>