5 ディスクリプタの理解
EclipseLinkでは、ディスクリプタを使用して、あるデータ・ソースによって特定クラスのインスタンスがどのように表現されるかを定義した情報を格納します。
ディスクリプタには、クラスのインスタンス変数を、データ・ソースおよび、値の格納と取得に使用されるトランスフォーメーション・ルーチンに関連付けるマッピングを含めます。それによって、ディスクリプタは、Javaオブジェクトとそのデータ・ソース表現を接続する役目を果します。
共通のディスクリプタの概念
多くの概念は、オブジェクト・リレーショナル・ディスクリプタとMOXyディスクリプタに共通です。
ディスクリプタのアーキテクチャ
ディスクリプタには、あるデータ・ソースによって特定オブジェクト・クラスのインスタンスがどのように表現されるかを定義したすべての情報を格納します。
ディスクリプタAPIを使用すると、Javaコードを介してEclipseLinkディスクリプタを定義または修正できます。ディスクリプタAPIのクラスは、主としてorg.eclipse.persistence.descriptorsパッケージに含まれています。
EclipseLinkディスクリプタには次の情報を格納できます。
-
ディスクリプタ自体の中で記述する永続Javaクラス、および対応するデータ・ソース(データベース表またはXMLの複合型インタラクション)。
-
そのクラスの属性とリレーションシップをデータ・ソースでどのように表現するかを記述したマッピングのコレクション。
-
データ・ソースの主キー情報(またはそれに相当する情報)。
-
フィールド名の問合せキー(または別名)のリスト。
-
順序番号の情報。
-
ディスクリプタの動作を調整するためのオプション・プロパティのセット。リフレッシュ・オプションのキャッシング、アイデンティティ・マップ、オプティミスティック・ロック、イベント・マネージャおよび問合せマネージャに関するサポートが含まれます。
EclipseLinkがサポートするデータ・ソース・タイプごとに、対応するディスクリプタ・タイプがあります。同一のデータ・ソース・タイプに対して有効なディスクリプタ・タイプが複数ある場合もあります。使用するディスクリプタのタイプにより、定義できるマッピングのタイプが決まります。
ディスクリプタと継承
継承とは、導出されたクラス(子)にそのスーパークラス(親)の特性をどのように受け継がせるかを意味します。
ディスクリプタを使用すると、リレーショナル・プロジェクトおよびXMLプロジェクトにおいてクラス間の継承リレーションシップを定義できます。
子クラスのディスクリプタでは、親クラスのディスクリプタに指定されているマッピングをオーバーライドしたり、親クラスのディスクリプタにマップされていない属性をマップできます。
次の図は、Java継承階層の代表例としてVehicleというオブジェクト・モデルを示します。ルート・クラスのVehicleには、2つブランチ・クラスFueledVehicleおよびNonFueledVehicleが含まれています。各ブランチ・クラスには、それぞれCarおよびBicycleというリーフ・クラスが含まれます。
EclipseLinkが継承階層で認識するクラスは、次の3つの種類です。
-
ルート・クラスは、サブクラス階層内のすべてのインスタンス化可能クラスの情報を格納します。デフォルトでは、ルート・クラスに対して実行された問合せにより、ルート・クラスおよびインスタンス化可能なサブクラスのインスタンスが返されます。ただし、ルート・クラスの構成方法によっては、ルート・クラスに対する問合せ時にサブクラスのインスタンスを省いて、ルート・クラス自体のインスタンスのみを返すこともできます。
たとえば、この図のVehicleクラスが、ルート・クラスです。
-
ブランチ・クラスは、永続スーパークラスの他にサブクラスを持つものです。デフォルトでは、ブランチ・クラスに対して実行された問合せにより、ブランチ・クラスおよびそのサブクラスのインスタンスが返されます。ただし、ルート・クラスの場合と同様に、ブランチ・クラスの構成方法によっては、ブランチ・クラスに対する問合せ時にサブクラスのインスタンスを省いて、ブランチ・クラス自体のインスタンスのみを返すこともできます。
たとえば、この図のFueledVehicleクラスが、ブランチ・クラスです。
-
リーフ・クラスには、階層内の永続スーパークラスがありますが、サブクラスはありません。リーフ・クラスに対して実行された問合せにより、リーフ・クラスのインスタンスのみが返されます。
たとえば、この図のCarクラスが、リーフ・クラスです。
子クラスのディスクリプタでは、親クラスのディスクリプタに指定されているマッピングをオーバーライドしたり、親クラスのディスクリプタにマップされていない属性をマップできます。
クラス・インジケータの指定
継承を構成する場合は、ルート・クラス・ディスクリプタに、インスタンス化先のサブクラスの選択の方法を構成します。
これを行うには、次のいずれかの方法を実行します。
-
クラス・インジケータ・フィールドの使用
-
クラス抽出メソッドの使用
注意:
継承階層内のすべてのリーフ・クラスにはクラス・インジケータが必須で、かつこれらのリーフ・クラスは同じタイプのクラス・インジケータ(フィールドまたはクラス抽出メソッド)を持つ必要があります。
注意:
インジケータ・フィールドが主キーの一部である場合は、インジケータ・フィールドに対する書込み専用トランスフォーメーション・マッピングを定義します。
クラス・インジケータ・フィールドでは、値として文字列または数値を使用できます。
ルート・クラス・ディスクリプタによって、クラス・インジケータ・フィールドの値をインスタンス化されるクラスに変換する方法を指定する必要があります。
クラス抽出メソッドの使用
オブジェクトのデータ・ソース・レコード内で利用可能なすべての情報に基づいて、クラス・インジケータを計算するJavaメソッドを定義することができます。このようなメソッドをクラス抽出メソッドといいます。
クラス抽出メソッドを使用すると、データ・モデルに明示的なクラス・インジケータ・フィールドを含める必要がなくなり、また、複雑すぎてクラス・インジケータ・フィールドには定義できないリレーションシップを処理できます。
クラス抽出メソッドには次の特性が必要です。
-
ルート・ディスクリプタのクラスをベースにして定義されていること
-
静的であること
-
引数としてRecordをとること
-
入出力引数であるRecordにjava.lang.Classオブジェクトを出力すること
状況に応じて、only-instancesおよびwith-all-subclasses式も定義する必要があります。クラス抽出メソッドを使用する場合は、共通の表を使用するすべてのクラスの兄弟インスタンスを正しくフィルタ処理するための式を、EclipseLinkに指定する必要があります。
クラス抽出メソッドを使用して継承を構成した場合、EclipseLinkではルート・クラスに関する問合せのためのSQLは生成されません。
継承と主キー
リレーショナル・プロジェクトの場合、EclipseLinkでは、継承階層内のすべてのクラスが、ルート・ディスクリプタに設定されているのと同じ主キーを持っていることが前提とされます。
単一表と複数表の継承
リレーショナル・プロジェクトでは、継承階層を単一の表または複数の表にマップできます。
集約ディスクリプタ、コンポジット・ディスクリプタおよび継承
リレーショナル・ディスクリプタを集約ディスクリプタとして指定できます。XMLディスクリプタは常にコンポジット・ディスクリプタです(「ディスクリプタと集約」を参照)。
リレーショナル集約ディスクリプタで継承を構成する場合は、継承ツリー内のすべてのディスクリプタが集約である必要があります。つまり、集約クラスと非集約クラスのディスクリプタを同じ継承ツリーに置くことはできません。
XMLディスクリプタで継承を構成する場合は、すべてのXMLディスクリプタがコンポジットであるため、継承はディスクリプタ・タイプによる制限を受けません。
ディスクリプタと集約
2つのオブジェクト、つまり、ソース(親、すなわち所有)オブジェクトとターゲット(子、すなわち被所有)オブジェクトは、両者間に厳密な1対1の関係がある場合、集約によって関連付けられ、ターゲット・オブジェクトのすべての属性は、ソース・オブジェクトと同じデータ・ソース表現から取得できます。つまり、ソース・オブジェクトが存在すればターゲット・オブジェクトも存在する必要があり、ソース・オブジェクトが破棄されればターゲット・オブジェクトも破棄されるということです。
この場合、ソースおよびターゲット・オブジェクトのディスクリプタは、この関係を反映するよう定義する必要があります。
EJB 3.0仕様はネストされた集約をサポートしていません。
ディスクリプタのカスタマイズ
ディスクリプタは、ディスクリプタ・カスタマイザを指定して実行時にカスタマイズできます。
ディスクリプタ・カスタマイザは、org.eclipse.persistence.config.DescriptorCustomizerインタフェースを実装するJavaクラスで、デフォルト(引数ゼロ)のコンストラクタを提供します。
ディスクリプタ・カスタマイザを使用することで、コードAPIを介して実行時にディスクリプタがカスタマイズされますが、その方法は、修正メソッドを使用してディスクリプタをカスタマイズする方法と類似しています。「修正メソッド」を参照してください。
修正メソッド
実行時にディスクリプタがロードされるときにコールするstatic Javaメソッドを関連付けることができます。
このメソッドは、ディスクリプタのJavaコードAPIを介して実行時のディスクリプタ・インスタンスを修正できます。このメソッドは、public staticであり、タイプorg.persistence.descriptors.structures.ClassDescriptorの属性を1つとるものである必要があります。このメソッドの実装では、publicの任意のディスクリプタとマッピングAPIを使用して、ディスクリプタの高度な機能を構成できます。
ただし、ディスクリプタはセッションが接続される前にのみ修正できます。これは、セッションの接続後にディスクリプタを修正するのは望ましくないためです。
修正メソッドは、有理数型ディスクリプタ、オブジェクト・リレーショナル・データ・タイプのディスクリプタ、およびXMLディスクリプタとともに使用できます。
ディスクリプタ・イベント・マネージャ
リレーショナル・プロジェクトでは、EclipseLinkによって、永続性のライフ・サイクル中にDescriptorEventの様々なインスタンスが起動されます。
各ディスクリプタは、これらのイベントを受信してそれを登録済のディスクリプタ・イベント・ハンドラにディスパッチする役割を持つDescriptorEventManagerのインスタンスを所有します。
ディスクリプタ・イベント・ハンドラを使用すると、構築したアプリケーション固有のロジックをディスクリプタ・イベントの発生時に実行でき、永続データのライフ・サイクルの様々な時点において実行可能なカスタマイズ・アクションをとれるようになります。たとえば、ディスクリプタ・イベント・ハンドラを使用して次の動作を実行できます。
-
永続オブジェクトと他のシステム、サービスおよびフレームワークとの同期化
-
EclipseLinkで対応していない非永続属性の管理
-
オブジェクトの永続状態が変化したときにアプリケーションの他のオブジェクトに通知すること
-
EclipseLinkのマッピングで直接サポートされていない複雑なマッピングまたは最適化を実装すること
オブジェクト・リレーショナル・ディスクリプタの概念
この項では、オブジェクト・リレーショナル・ディスクリプタに固有の概念について説明します。
フェッチ・グループ
フェッチ・グループを使用して、オブジェクトの属性のサブセットを定義し、そのフェッチ・グループをReadObjectQueryまたはReadAllQuery問合せと関連付けることができます。
デフォルトでは、特定のオブジェクト・クラスに対してオブジェクト・レベルの読取り問合せを実行すると、そのオブジェクトのディスクリプタにマップされているすべての永続属性がEclipseLinkによって返されます。この1回の問合せを実行するだけで、対象オブジェクトのすべての永続属性が定義され、さらに、各属性のgetメソッドをコールすることで、属性値をオブジェクトから直接取得できます。
オブジェクトの属性の一部のみが必要な場合は、フェッチ・グループを使用して、そのオブジェクトの属性のサブセットのみが返されるようにした方が効率的です。
フェッチ・グループを使用して、オブジェクトの属性のサブセットを定義し、そのフェッチ・グループをReadObjectQueryまたはReadAllQuery問合せと関連付けることができます。問合せを実行すると、EclipseLinkによりフェッチ・グループ内の属性のみが取得されます。除外された属性のいずれかに関するgetメソッドをコールすると、EclipseLinkでは、このサブセットから除外されたすべての属性をフェッチする問合せが自動的に実行されます。
1つのクラスに対して複数のフェッチ・グループを定義できます。オプションで、デフォルトのフェッチ・グループとして最大1つのフェッチ・グループを指定できます。フェッチ・グループを指定しないでReadObjectQueryまたはReadAllQuery問合せを実行する場合、問合せを別の方法で構成しないかぎり、EclipseLinkではデフォルトのフェッチ・グループが使用されます。
フェッチ・グループを使用する前に、システムの使用状況を綿密に分析しておくことをお薦めします。多くの事例では、フェッチ・グループに含まれない属性をロードするために必要な追加の問合せにより、部分的な属性のロードによって得られるメリットがかなり相殺されているためです。
フェッチ・グループは、FetchType.LAZY(部分オブジェクト問合せ)で構成された基本マッピングとの組合せでのみ使用できます。
EclipseLinkでは、フェッチ、ロード、コピーおよびマージ操作で部分的エンティティの使用を構成するために使用できるAttributeGroupを使用します。
-
フェッチ: データベースから取得される属性とその関連列を制御します
-
ロード: 問合せから返されたエンティティで移入されるリレーションシップを制御します
-
コピー: 新しいエンティティ・インスタンスにコピーされる属性を制御します
-
マージ: エンティティにフェッチ、ロードまたはコピーされた属性のみをマージします
次の項では、使用可能なAttributeGroupタイプおよび操作について説明します。
FetchGroup
FetchGroupは、問合せ実行の結果としてエンティティが取得されるときにフェッチする(データベースから選択する)必要のある属性を定義します。FetchGroupにリレーションシップ属性を含めることで決定されるのは、属性の必須列をフェッチして移入するかどうかのみです。遅延フェッチ・タイプの場合、属性を含めることで、そのプロキシが作成されてアクセス時に遅延ロードが有効になります。問合せでのFetchGroupの使用時にリレーションシップ・マッピングの移入を強制するには、属性をグループに含め、FetchType.EAGERに設定するか、問合せで関連するLoadGroupに含める必要があります。
デフォルトFetchGroup
FetchGroupには、FetchGroupManagerによって管理される名前付きのデフォルトFetchGroupの概念も含まれます。デフォルトFetchGroupは、1つ以上の基本マッピングが遅延するように構成され、エンティティ・クラスがFetchGroupTrackerを実装している場合に、メタデータ処理中に定義されます(通常はウィービングを通じて導入されます)。デフォルトFetchGroupは、明示的FetchGroupや名前付きFetchGroupが構成されていないこのエンティティ・タイプのすべての問合せで使用されます。
名前付きFetchGroup
名前付きFetchGroupは、@FetchGroup注釈を使用して、またはeclipselink-orm.xml
ファイル内で、エンティティに対して定義できます。
完全なFetchGroup
FetchGroupは、最初に作成されたときは空であると想定されます。ユーザーは、FetchGroupに属性を追加する必要があります。すべての属性を含むFetchGroupが必要な場合、FetchGroupManager.createFullFetchGroup()を使用する必要があります。
Load/LoadAllとFetchGroup
FetchGroupは、リレーションシップ・マッピングやネストしたリレーションシップ・マッピングのロード操作を実行するように構成することもできます。
LoadGroup
LoadGroupを使用して、リレーションシップ属性の指定したセットに問合せ結果での移入を強制します。
CopyGroup
CopyGroupによって、エンティティのコピー方法を定義するために使用される、非推奨のObjectCopyPolicyが置換されます。ソース・エンティティ・グラフからターゲット・コピーにコピーする内容を定義する属性を指定することに加え、CopyGroupでは次の定義も可能です。
-
shouldResetPrimaryKey: 識別子属性をデフォルト値にリセットします。これは、コピー操作でソースと同様の状態の新しいエンティティを作成するためにエンティティをクローニングする場合に使用されます。デフォルトはfalseです。
-
shouldResetVersion: オプティミスティック・バージョンのロック属性をコピーでデフォルト値にリセットします。デフォルトはfalseです。
-
depth: リレーションシップを処理するためのカスケード・モードを定義します。デフォルトでは、CASCADE_PRIVATE_PARTSが使用されますが、NO_CASCADEやCASCADE_ALL_PARTSにも構成できます。
マージ
使用可能な属性を定義して、部分エンティティをAttributeGroupが関連付けられた永続性コンテキストにマージすると、それらの属性のみがマージされます。エンティティ内のリレーションシップ・マッピングも、それらのカスケード・マージ設定に従ってマージされます。
ディスクリプタ問合せマネージャ
特定のクラスに適用できる名前付き問合せを保存できるのみでなく、一般的なデータ・ソース操作用にEclipseLinkで定義されているデフォルトの操作をDescriptorQueryManagerによってオーバーライドすることもできます。
ディスクリプタと順序付け
順序付けオプションにプロジェクト・レベルとセッション・レベルのいずれを構成するかにより、EclipseLinkで使用される順序付けのタイプが決まります。
オブジェクト・アイデンティティを保持するうえで重要なのは、各オブジェクト・インスタンスを区別するために一意の値(特定の順序)の割当てを管理することです。
POJOプロジェクトの場合、セッション・レベルの順序構成を使用して、プロジェクト・レベルの順序構成をセッション単位でオーバーライドすることもできます。
順序タイプの構成後、ディスクリプタの参照クラスごとに、1つの属性(通常は主キーとして使用する属性)を独自の順序に関連付ける必要があります。
ディスクリプタとロック
EclipseLinkでは、複数のロック・ポリシーがサポートされます。
オブジェクト・リレーショナル・マッピングでは、次に示すロック・ポリシーのいずれかを使用してディスクリプタを構成し、ドメイン・オブジェクトへの同時アクセスを制御できます。
-
オプティミスティック: すべてのユーザーにデータへの読取りアクセス権限があります。ユーザーが変更を加えようとした場合、そのユーザーがデータを読み取った後にデータが変更されていないか、アプリケーションによってチェックされます。
-
ペシミスティック: 更新目的でデータにアクセスした最初のユーザーによって、更新処理が完了するまでデータがロックされます。
-
ロックなし: 複数のユーザーが互いの変更内容を上書きする操作は阻止されません。
ほとんどのタイプのアプリケーションでは、ユーザー同士が互いの変更内容を上書きできないようにするために、オプティミスティック・ロックを使用することをお薦めします。
この項では、EclipseLinkでサポートされている次のような様々なタイプのロック・ポリシーについて説明します。
オプティミスティック・バージョン・ロック・ポリシー
オプティミスティック・ロックを使用した場合、すべてのユーザーにデータへの読取りアクセス権限があります。ユーザーが変更を加えようとした場合、そのユーザーがデータを読み取った後にデータが変更されていないか、アプリケーションによってチェックされます。
オプティミスティック・バージョン・ロック・ポリシーでは、バージョン・フィールド(書込みロック・フィールド)を使用して、オプティミスティック・ロックが実行されます(バージョン・フィールドは、参照クラス内に作成し、オブジェクト変更がコミットされるたびにEclipseLinkによって更新されます)。
EclipseLinkは、データ・ソースからオブジェクトを読み取るときにこのバージョン・フィールドの値をキャッシュします。クライアントがオブジェクトに書き込もうとすると、EclipseLinkはキャッシュしたバージョン値とデータ・ソース内の最新のバージョン値を次のように比較します。
-
2つの値が一致した場合は、EclipseLinkはオブジェクト内のバージョン・フィールドを更新し、データ・ソースに対する変更内容をコミットします。
-
2つの値が一致しなかった場合は、そのクライアントが最初にオブジェクトを読み取った後に別のクライアントがそのオブジェクトを更新したことを意味するため、書込み操作を却下します。
EclipseLinkには、次のようなバージョン・ベースのオプティミスティック・ロック・ポリシーが用意されています。
-
VersionLockingPolicy
-
TimestampLockingPolicy
注意:
-
データ・ソースからのタイムスタンプ取得により、パフォーマンスが低下する場合があること
-
timestampタイプのバージョンのロックでは、データベースが格納するタイムスタンプによって精度に限界があること
オプティミスティック・ロック違反により更新が失敗すると、EclipseLinkではOptimisticLockExceptionがスローされます。この例外は、データベースの変更を行っているアプリケーションで処理する必要があります。アプリケーションは、ロックの競合をクライアントに通知し、オブジェクトをリフレッシュして、クライアントに変更内容の再適用を依頼する必要があります。
バージョン値は、オブジェクト内にマップ済属性として格納するか、キャッシュに格納するかを選択できます。3層アプリケーションでは、オブジェクトの更新時にバージョン値が確実にクライアントに渡されるようにするため、オブジェクト内にバージョン値を格納しておく方法が一般的です(次の「アプリケーションでのロックの適用」を参照)。
バージョン値をキャッシュに格納する場合、バージョン値のマッピングは必要ありません。バージョン・フィールドをマップする場合、マッピングを読取り専用として構成する必要があります。
私有の子オブジェクトの変更時にその親オブジェクトのバージョン・フィールドが確実に更新されるようにするには、オプティミスティック・バージョン・ロック・ポリシーとカスケードを使用することを検討します。
ストアド・プロシージャを使用してオブジェクトを更新または削除する場合、オプティミスティック・ロックの障害を検出するために必要な行数をデータベースが返さないことがあるため、ストアド・プロシージャでオプティミスティック・ロックのバージョンを確認し、一致しない場合はエラーをスローする必要があります。StoredProcedureCallでは、バージョン・ロックのみが直接サポートされます。タイムスタンプおよびフィールド・ロックでは、同じフィールドの2つのバージョンをコールに渡す必要があるため、##パラメータを使用してトランザクション行にアクセスするSQLコールは、他のロック・ポリシーに使用できます。
使用するデータベース・スキーマが、親オブジェクトおよびその私有の子オブジェクトの両方を同じ表内に格納する設計になっている場合には、その子オブジェクトを更新すると、親オブジェクトのバージョン・フィールドが更新されます。
一方、親オブジェクトおよびその私有の子オブジェクトを異なる表に格納する場合は、デフォルトでは、子を変更しても、親のバージョン・フィールドは更新されません。
この場合に親オブジェクトのバージョン・フィールドを確実に更新するには、親オブジェクトのバージョン・フィールドを手動で更新するか、またはVersionLockingPolicyを使用している場合は、子オブジェクトのバージョン・フィールドの更新が親に自動的にカスケードされるようにEclipseLinkを構成することができます。
オプティミスティック・バージョン・ロックのカスケード機能を有効化した場合に、私有の子オブジェクトを変更すると、EclipseLinkはその私有されている外部参照マッピングを走査し、ルートに向かってすべての親オブジェクトを更新します。
EclipseLinkでは、次の場合にオプティミスティック・バージョン・ロックのカスケードがサポートされます。
-
私有の1対1および1対多マッピングでのオブジェクト変更
-
次のコレクション・マッピング(私有かどうかは問わない)でのリレーションシップ変更(追加または削除)
-
ダイレクト・コレクション
-
1対多
-
多対多
-
集約コレクション
-
次の図に例示したオブジェクトの図について考えてみます。
この例では、ObjectAがObjectBを私有し、ObjectBはObjectCを私有し、ObjectCはObjectDを私有します。
ObjectBを作業ユニットに登録し、ObjectBを変更して、作業ユニットをコミットしたとします。この場合、ObjectBはObjectAのキャッシュをチェックし、値が存在しない場合にはデータベースに対してObjectAを問い合せます。 次に、ObjectBは自身の変更をObjectAに通知します。 ObjectAは、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。
ObjectAを作業ユニットに登録し、そのObjectB、ObjectC、ObjectDにこの順序でアクセスして、ObjectDのフィールドを変更し、作業ユニットをコミットするとします。この場合は、ObjectDが自身の変更をObjectCに通知します。 ObjectCは、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。次に、 ObjectCはObjectDの変更をObjectBに通知します。 次に、ObjectBがObjectDの変更をObjectAに通知します。 ObjectAは、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。
オプティミスティック・ロックで、オプティミスティック・ロックのバージョンをキャッシュに格納する場合、ロックされたオブジェクトの値をロールバックするにはUnitOfWorkメソッドcommitAndResumeOnFailureを使用します。
ロックされた複数のバージョンを1つのオブジェクトに格納する場合は、更新失敗時に各オブジェクト(すなわち、オブジェクトのバージョン)をリフレッシュする必要があります。あるいは、失敗時に新しい作業ユニットを取得して、そのユニットに変更を再適用することもできます。
オプティミスティック・フィールド・ロック・ポリシーでは、表内に現存する1つ以上のフィールドを使用して、クライアントが対象オブジェクトを読み取った以後にそのオブジェクトが変更されたかどうかを判別することによって、オプティミスティック・ロックが実行されます。
ユーザーがオブジェクトを最初に読み取ったとき、またはオブジェクトを作業ユニットに登録したときに、作業ユニットにより、そのオブジェクトの元の状態がキャッシュされます。コミット時には、ロック・フィールドの元の値と更新中のデータ・ソースの現在の値が、作業ユニットにより比較されます。ロック・フィールドのいずれかの値が変更されていた場合には、オプティミスティック・ロック例外がスローされます。
EclipseLinkには、次のようなオプティミスティック・フィールド・ロック・ポリシーが用意されています。
-
AllFieldsLockingPolicy
-
ChangedFieldsLockingPolicy
-
SelectedFieldsLockingPolicy
-
VersionLockingPolicy
-
TimestampLockingPolicy
これらのロック・ポリシーの詳細は、『Solutions Guide for EclipseLink』の「Setting Optimistic Locking」を参照してください。
ペシミスティック・ロック・ポリシー
ペシミスティック・ロックを使用した場合は、データを更新する目的でそのデータにアクセスする最初のユーザーが、更新を完了するまでデータをロックします。
ペシミスティック・ロック・ポリシーを使用する場合は、更新をただちに失敗させるか、読取りロックが取得されるまで待機させるようにポリシーを構成できます。
ペシミスティック・ロック・ポリシーは、コンテナ管理の永続性タイプと、EJB情報を含むディスクリプタを保持するプロジェクトでのみ使用できます。
(ペシミスティック・ロック・ポリシーではなく)ペシミスティック・ロックは、問合せレベルでも使用できます。
EclipseLinkでは、ペシミスティック・ロックをコンテナ管理の永続性を持つエンティティとともに使用する場合にこのロックの最適化が行われ、問合せをペシミスティック・ロックに設定し、(ファインダの実行後に終了する)独自の新しいトランザクションで問合せを実行すると、EclipseLinkによってそのロック設定はオーバーライドされ、SQLにFOR UPDATEは追加されません。ただし、ユーザーがFOR UPDATEを含むSQL文字列でペシミスティック・ロック問合をカスタマイズしている場合、この最適化の使用によって不都合な結果が発生する可能性があります。この場合、最適化が必要な状況が存在すると、問合せは非ペシミスティック・ロックにリセットされますが、SQLは同じままのため、問合せのロック設定が問合せのSQL文字列と競合します。この問題を回避するには、次の2つのアプローチのいずれかを使用します。
-
選択基準として式を使用します(「EclipseLinkの式の理解」を参照してください)。これによって、EclipseLinkでSQLの生成を制御します。
-
最適化の条件をなくすため、ファインダをトランザクション内に配置します。
アプリケーションでのロックの適用
アプリケーションで適切にオブジェクトをロックするには、オブジェクトが編集のためにクライアントに送信される前に、ロックを取得する必要があります。
オプティミスティック・ロックを使用する場合、オブジェクトを正しくロックするには次の2つの選択肢があります。
-
オブジェクト内のオプティミスティック・ロック・フィールドを読取り専用以外にマップし、読取り時にバージョンをクライアントに渡し、更新時にサーバーに戻します。
更新のためにオブジェクトが読み取られるときに、元のバージョン値がクライアントに送信されることを確認します。クライアントは、その元のバージョン値を更新情報とともに戻す必要がありますが、このバージョンは、サーバー上の新しい作業ユニットで登録または読取りが行われた後に、更新対象のオブジェクトに設定される必要があります。
-
クライアントとの対話が完了するまで作業ユニットを保持します。
ステートフル・セッションBeanを介して、またはHTTPセッションにおいて、更新対象オブジェクトの読取りに使用する作業ユニットを、クライアントとの対話が完了するまで保管します。
オブジェクトをクライアントに更新用に渡す前に、この作業ユニットからオブジェクトを読み取っておく必要があります。これにより、作業ユニットのキャッシュまたは作業ユニットのクローンに元のバージョン値を保管しておけます。
この同じ作業ユニットを更新でも必ず使用します。
1番目の選択肢の方が一般的に使用され、ステートレス・アプリケーションを開発する場合には必須となります。
ペシミスティック・ロックを使用する場合は、作業ユニットを使用して、オブジェクトが読み取られる前にデータベース・トランザクションを開始する必要があります。この作業ユニットとデータベース・トランザクションは、クライアントがオブジェクトを編集して更新するまでの間、保持する必要があります。この同じ作業ユニットをオブジェクトの更新でも必ず使用します。
ディスクリプタ・ファイル
EclipseLinkには、オブジェクト・リレーショナルとMOXyのマッピングに使用可能なディスクリプタ・ファイルが含まれています。
オブジェクト・リレーショナル・マッピングへのorm.xmlの使用
orm.xmlファイルを使用して、メタデータを永続性ユニットに適用します。
このメタデータは、すべてのマッピング・ファイルと注釈の集合です(xml-mapping-metadata-complete要素が存在しない場合)。メタデータに対して1つのマッピングorm.xmlファイルを使用し、そのファイルをクラスパスのMETA-INFディレクトリに配置する場合、それを明示的にリストする必要はありません。このファイル(orm.xml)は、永続性プロバイダによって自動的に検索され、使用されます。
JPA 2.2 orm.xmlのスキーマはorm_2_2.xsd (http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/orm_2_2.xsd)です。
マッピング・ファイルを異なる名前で使用するか、異なる場所に配置する場合、それらをpersistence.xml
ファイルのmapping-file要素にリストする必要があります。
EclipseLinkのオブジェクト・リレーショナル・マッピングへのeclipselink-orm.xmlの使用
EclipseLinkは、eclipselink-orm.xml
という拡張JPA orm.xmlマッピング構成ファイルをサポートしています。
このマッピング・ファイルは、JPAの標準マッピング・ファイルのかわりとして、またJPAのマッピング・ファイルをオーバーライドするためにも使用できます。すべての標準JPAマッピング機能を使用できるだけでなく、高度なマッピング・タイプとオプションも使用できます。
eclipselink-orm.xml
ファイルの詳細は、『Java Persistence API (JPA) Extensions Reference for EclipseLink』の「eclipselink-orm.xml Schema Reference」を参照してください。
注意:
このマッピング・ファイルを使用すると、EclipseLinkの多くの高度な機能が有効になりますが、他のJPA実装に対する永続性ユニットの移植性がなくなる場合があります。
オーバーライド値の詳細は、次を参照してください。
-
JPA仕様の「XML Overriding Rules」
-
eclipselink-orm.xmlのスキーマはeclipselink_orm_2_2.xsdです。
http://www.eclipse.org/eclipselink/xsds/eclipselink_orm_2_2.xsd
マッピング情報のオーバーライドおよびマージ
orm.xml
ファイルのマッピングをオーバーライドするには、プロジェクトでMETA-INF/eclipselink-orm.xml
ファイルを定義する必要があります。eclipselink-orm.xml
の内容が、orm.xml
および永続性ユニットで指定されたその他のJPAマッピング・ファイルをオーバーライドします。複数のORMファイルの指定が重複している場合、競合エンティティが存在しなければそれらのファイルはマージされます。
詳細は、『Java Persistence API (JPA) Extensions Reference for EclipseLink』の「Overriding and Merging」を参照してください。
XMLスキーマの検証
デフォルトでは、.orm
XMLファイルの内容は、JPAの.orm
XMLスキーマに対して検証されません。
開発時に、.orm
XMLファイルをスキーマに対して検証し、その妥当性を確認することをお薦めします。EclipseLinkで.orm
XMLスキーマの検証を有効にするには、persistence.xml
ファイルの永続性ユニット・プロパティeclipselink.orm.validate.schemaを使用します。
XML使用の長所と短所
注釈のかわりにXMLを使用する場合の利点は、次のとおりです。
-
メタデータとソース・コード間の結合がありません。
-
既存のEJB 3.0以前の開発プロセスに準拠しています。
-
IDEおよびソース・コントロール・システムでサポートされます。
XMLによるマッピングの主な短所は、次のとおりです。
-
注釈と比較して、本質的に複雑です。
-
コード・コンテキストのレプリケーションが必要です(XMLとソース・コードの両方で構造を定義する必要があります)。
詳細は、JPA仕様の第10章「Metadata Annotations」を参照してください。
EclipseLink MOXyマッピングへのeclipselink-oxm.xmlの使用
Java注釈を使用して、プロジェクトにJAXBの機能を指定できます。
eclipselink-oxm.xml
というXMLマッピング構成ファイルが用意されています。このマッピング・ファイルには、標準のJAXBマッピングと、高度なマッピング・タイプ用の構成オプションが含まれています。eclipselink-oxm.xml
ファイルを、ソース・コードのJAXB注釈のかわりに使用するか、JAXB注釈をオーバーライドするために使用できます。
注意:
このマッピング・ファイルを使用すると、多くの高度な機能が有効になりますが、他のJAXB実装に対するモデルの移植性がなくなる場合があります。