TopLinkでは、ディスクリプタを使用して、あるデータ・ソースによって特定クラスのインスタンスがどのように表現されるかを定義した情報を格納します。ディスクリプタには、クラスのインスタンス変数を、データ・ソースおよび、値の格納と取得に使用されるトランスフォーメーション・ルーチンに関連付けるマッピングを含めます。それによって、ディスクリプタは、Javaオブジェクトとそのデータ・ソース表現を接続する役目を果します。
この章の内容は次のとおりです。
表16-1は、オブジェクト・モデルにクラスを定義するために使用するディスクリプタ・タイプを示します。それぞれについて、基本タイプか詳細タイプかの分類も示します。
表16-1 TopLinkのディスクリプタ・タイプ
ディスクリプタ・タイプ | 説明 | Oracle JDeveloper |
TopLink Workbench | Java |
---|---|---|---|---|
リレーショナル(21.1項「リレーショナル・ディスクリプタ」を参照) |
リレーショナル・データベース内の表にマップするJavaオブジェクトを記述します。TopLinkでサポートされるすべてのリレーショナル・データベースに対してのみ適用可能です。 |
|||
オブジェクト・リレーショナル(24.1項「オブジェクト・リレーショナル・データ・タイプ・ディスクリプタ」を参照) |
より近密に各オブジェクト・タイプに対応した専用のデータベース・データ・タイプを提供している、リレーショナル・データベースの表にマップされるJavaオブジェクトを記述します。TopLinkでサポートされるリレーショナル・データベースのうち、これら専用のデータ・タイプを提供するリレーショナル・データベースに対してのみ適用可能です。 |
|
|
|
EIS(74.1項「EISディスクリプタの概念」を参照) |
JCAアダプタを介してEISデータ・ソースにマップするJavaオブジェクトを記述します。 |
|||
XML(50.1項「XMLディスクリプタの概念」を参照) |
XMLスキーマ・ドキュメント(XSD)に定義されているXML文書内の複合型へのインメモリー・マッピングを行うJavaオブジェクトを記述します。 |
詳細は、次を参照してください。
この項では、次の内容を含む、TopLinkに固有のディスクリプタの概念について説明します。
ディスクリプタには、あるデータ・ソースによって特定オブジェクト・クラスのインスタンスがどのように表現されるかを定義したすべての情報を格納します。
TopLinkディスクリプタには次の情報を指定します。
ディスクリプタ自体の中で記述する永続Javaクラス、および対応するデータ・ソース(データベース表、XMLの複合型またはEISインタラクション)。
そのクラスの属性とリレーションシップをデータベースにどのように格納するかを記述したマッピングのコレクション。
データ・ソースの主キー情報(またはそれに相当する情報)。
フィールド名の問合せキー(または別名)のリスト。
順序番号の情報。
ディスクリプタの動作を調整するためのオプション・プロパティのセット。リフレッシュ・オプションのキャッシング、アイデンティティ・マップ、オプティミスティック・ロック、イベント・マネージャおよび問合せマネージャに関するサポートが含まれます。
TopLinkがサポートするデータ・ソース・タイプごとに、対応するディスクリプタ・タイプがあります。同一のデータ・ソース・タイプに対して有効なディスクリプタ・タイプが複数ある場合もあります。使用するディスクリプタのタイプにより、定義できるマッピングのタイプが決まります。
表16-2は、プロジェクト、ディスクリプタ、マッピングの関係をまとめたものです。
表16-2 プロジェクト、ディスクリプタおよびマッピングのサポート
プロジェクト | ディスクリプタ | マッピング |
---|---|---|
リレーショナル(第18章「リレーショナル・プロジェクトの概要」を参照) |
次を決定します。
|
次を決定します。
|
EIS(第71章「EISプロジェクトの概要」を参照) |
EIS(74.1項「EISディスクリプタの概念」を参照) |
EIS(17.7項「EISマッピング」を参照) |
XML(第47章「XMLプロジェクトの概要」を参照) |
XML(50.1項「XMLディスクリプタの概念」を参照) |
XML(17.6項「XMLマッピング」を参照) |
継承とは、導出されたクラス(子)にそのスーパークラス(親)の特性をどのように受け継がせるかを意味します。ディスクリプタを使用すると、リレーショナル・プロジェクト、EISプロジェクトおよびXMLプロジェクトにおいてクラス間の継承リレーションシップを定義できます。
子クラスのディスクリプタでは、親クラスのディスクリプタに指定されているマッピングをオーバーライドしたり、親クラスのディスクリプタにマップされていない属性をマップできます。
詳細は、16.3項「ディスクリプタと継承」を参照してください。
ディスクリプタを使用すると、コンテナ管理の永続性を備えたエンティティBeanまたはBean管理の永続性を備えたエンティティBeanの特性を定義できます。
そのためには、Enterprise Beanのマッピング時に、Beanクラスのディスクリプタを作成します。ただし、ローカル・インタフェース、リモート・インタフェース、ホーム・クラスまたは主キー・クラスのディスクリプタは作成しません。
TopLink Workbenchを使用する場合は、適切なエンティティBeanタイプ(コンテナ管理の永続性またはBean管理の永続性を備えたエンティティBeanなど)を使用してプロジェクトを定義し、Beanのejb-jar.xml
ファイルをTopLink Workbenchプロジェクトにインポートする必要があります。
CMPプロジェクトについては、ejb-jar.xml
ファイルを使用してBeanのマップ対象属性を定義します。コンテナ管理の永続性を備えたBeanのディスクリプタには、CMP固有のオプションを構成するためのCMPポリシーを記述します。
注意: EJB 3.0プロジェクトについては、注釈を使用してBeanのマップ対象属性を定義できます。 |
この項の内容は次のとおりです。
TopLinkのデフォルトでは、すべての変更はコミット時まで保留されます。これは、データ・ソース間のインタラクション数を最小限にとどめるために最も効率的な方法です。
あるいは、エンティティBeanのディスクリプタを構成して非遅延変更が行われるようにすることもできます。すなわち、エンティティBeanの永続フィールドを変更すると、ただちにTopLink CMPによってリレーショナル・スキーマが変更される、ということです。
非遅延変更では、一部のEJBコンテナのネイティブの動作との下位互換性が得られます。また、トランザクションの一時的な状態を使用してトリガーやストアド・プロシージャなどのためにデータベースとエンティティの変更を同期化するアプリケーションや、同一の主キーを使用して行を削除および作成するアプリケーション、またはトランザクション内の一時的な状態を使用してその他の複雑な問合せを行うアプリケーションなど、高度なアプリケーションにも対応可能となります。
非遅延変更の短所は、データ・ソースとのインタラクションの数が最大になるため、方法としては最も効率が悪いことです。
非遅延変更がサポートされるようにTopLink CMPを構成した場合でも、同じ遅延設定を持つ複数のエンティティBean間でマップされたリレーションシップについての制約は、TopLinkによって引き続き処理されます。ただし、非遅延設定のクラスが遅延設定のクラスに関連付けられており、その2つのクラス間に制約がある場合には、非遅延クラスに加えられた変更に起因するエラーは、開発者が処理する必要があります。
詳細は、119.18項「EJB CMPおよびBMP情報によるディスクリプタの構成」を参照してください。
新規のエンティティBeanを作成した場合、デフォルトでは次のようなBeanのライフ・サイクルが想定されます。
ejbCreate
メソッド:
挿入後、EJBコンテナは、作成されたインスタンス用に、データベースによって割り当てられた主キーを取得します。
リレーショナル・プロジェクトの場合:
INSERT INTO ...
SELECT FROM ...
EISプロジェクトの場合:
Write object ...
Find object ...
ejbPostCreate
メソッド:
EJBコンテナにより、コンテナ管理のリレーションシップ(CMR)のフィールドが更新されます。EJBコンテナには、ejbCreate
メソッドで取得された主キーが必要です。
リレーショナル・プロジェクトの場合:
UPDATE SET ...
EISプロジェクトの場合:
Write object ...
なお、データベースに非NULLの外部キー制約がある場合は、ejbCreate
メソッドの実行後にデータ・ソースを変更すると問題が発生することがあります。このような問題を回避するために、OC4Jなどの一部のアプリケーション・サーバーでは、ejbPostCreate
メソッドの実行後に新しいオブジェクトを作成できるようにして、コンテナを使用して外部キー制約を解決するようにしています。
詳細は、119.18項「EJB CMPおよびBMP情報によるディスクリプタの構成」を参照してください。
デフォルトでは、特定のオブジェクト・クラスに対してオブジェクト・レベルの読取り問合せを実行すると、そのオブジェクトのディスクリプタにマップされているすべての永続属性がTopLinkによって返されます。この1回の問合せを実行するだけで、対象オブジェクトのすべての永続属性が定義され、さらに、各属性のget
メソッドをコールすることで、属性値をオブジェクトから直接取得できます。
オブジェクトの属性の一部のみが必要な場合は、そのオブジェクトの属性のサブセットのみが返されるようにした方が効率的です。これを行うには、フェッチ・グループを使用してオブジェクトの属性のサブセットを定義し、そのフェッチ・グループをReadObjectQuery
またはReadAllQuery
問合せと関連付けます。
詳細は、次を参照してください。
2つのオブジェクト、つまり、ソース(親、すなわち所有)オブジェクトとターゲット(子、すなわち被所有)オブジェクトは、両者間に厳密な1対1の関係がある場合、集約によって関連付けられ、ターゲット・オブジェクトのすべての属性は、ソース・オブジェクトと同じデータ・ソース表現から取得できます。つまり、ソース・オブジェクトが存在すればターゲット・オブジェクトも存在する必要があり、ソース・オブジェクトが破棄されればターゲット・オブジェクトも破棄されるということです。
この場合、ソースおよびターゲット・オブジェクトのディスクリプタは、この関係を反映するよう定義する必要があります。
EJB 3.0では、集約は埋込み可能であることが知られています。EJB 3.0仕様では、埋込み可能である集約に、埋込み可能な別の集約を含めることはできません(つまり、EJB 3.0仕様では、ネストされた集約はサポートされていません)。
詳細は、次を参照してください。
ディスクリプタ・カスタマイザを指定すると、実行時にディスクリプタをカスタマイズできます。ディスクリプタ・カスタマイザはJavaクラスの1つで、oracle.toplink.tools.sessionconfiguration.DescriptorCustomizer
インタフェースを実装し、デフォルト(ゼロ引数)コンストラクタを備えています。
ディスクリプタ・カスタマイザを使用することで、コードAPIを介して実行時にディスクリプタがカスタマイズされます。その方法は、修正メソッドを使用してディスクリプタをカスタマイズする方法と類似しています(16.2.7項「修正メソッドとロード後メソッド」を参照)。
詳細は、119.34項「ディスクリプタ・カスタマイザ・クラスの構成」を参照してください。
TopLink Workbenchを使用すると、実行時にディスクリプタがロードされたときにコールされる静的Javaメソッドを関連付けることができます。このメソッドは、ディスクリプタのJavaコードAPIを介して実行時のディスクリプタ・インスタンスを修正できます。このメソッドを使用すると、現在のTopLink Workbenchではサポートされていないような高度な構成オプションを作成できます。
ただし、ディスクリプタはセッションが接続される前にのみ修正できます。これは、セッションの接続後にディスクリプタを修正するのは望ましくないためです。
詳細は、119.35項「修正メソッドの構成」を参照してください。
リレーショナル・プロジェクトおよびEISプロジェクトでは、永続データのライフ・サイクルの間にTopLinkがDescriptorEvent
の様々なインスタンスを発生させます(表119-26および表119-28を参照)。各ディスクリプタには、DescriptorEventManager
のインスタンスがあります。このインスタンスは、イベントを受信し、自身に登録されているディスクリプタ・イベント・ハンドラにディスパッチする働きをします。
ディスクリプタ・イベント・ハンドラを使用すると、構築したアプリケーション固有のロジックをディスクリプタ・イベントの発生時に実行でき、永続データのライフ・サイクルの様々な時点において実行可能なカスタマイズ・アクションをとれるようになります。たとえば、ディスクリプタ・イベント・ハンドラを使用して次の動作を実行できます。
永続オブジェクトを他のシステム、サービスおよびフレームワークと同期化すること。TopLinkで認識されない非永続属性を保持すること。
オブジェクトの永続状態が変化したときにアプリケーションの他のオブジェクトに通知すること。
TopLinkのマッピングで直接サポートされていない複雑なマッピングまたは最適化を実装すること。
詳細は、次を参照してください。
各リレーショナル・ディスクリプタとEISディスクリプタには、次の構成に使用できるDescriptorQueryManager
のインスタンスが用意されています。
名前付き問合せ(119.7項「ディスクリプタ・レベルでの名前付き問合せの構成」を参照)
基本的な永続性操作に関するカスタムのデフォルト問合せ(108.13.2項「デフォルトの問合せ実装の構成方法」を参照)
追加の結合式(108.13.3項「追加の結合式の構成方法」を参照)
問合せマネージャの使用方法の詳細は、108.13項「ディスクリプタ問合せマネージャ問合せ」を参照してください。
オブジェクト・アイデンティティを保持する上で重要なのは、各オブジェクト・インスタンスを区別するために一意の値(特定の順序)の割当てを管理することです。詳細は、15.2.6項「プロジェクトおよび順序付け」を参照してください。
順序付けオプションにプロジェクト・レベルとセッション・レベルのいずれを構成するかにより、TopLinkで使用される順序付けのタイプが決まります。CMPプロジェクトの場合、通常はこの順序タイプをプロジェクト・レベルに構成します(20.3項「プロジェクト・レベルでの順序付けの構成」を参照)。POJOプロジェクトの場合、必要に応じて、セッション・レベルの順序構成を使用して、プロジェクト・レベルの順序構成をセッション単位でオーバーライドすることもできます(98.4項「セッション・レベルでの順序付けの構成」を参照)。
各ディスクリプタの参照クラスに対して順序タイプを構成した後は、属性の1つ(通常は主キーとして使用される属性。119.2項「主キーの構成」を参照)をディスクリプタ固有の順序と関連付けます(23.3項「ディスクリプタ・レベルでの順序付けの構成」を参照)。
次に示すロック・ポリシーのいずれかを使用してディスクリプタを構成し、ドメイン・オブジェクトへの同時アクセスを制御できます。
オプティミスティック: すべてのユーザーにデータへの読取りアクセス権限があります。ユーザーが変更を加えようとすると、アプリケーションにより、ユーザーがデータを読み取ってから、そのデータが変更されていないかが確認されます(16.4.1項「オプティミスティック・バージョン・ロック・ポリシー」および16.4.4項「オプティミスティック・フィールド・ロック・ポリシー」を参照)。
ペシミスティック: データを更新する目的でそのデータにアクセスする最初のユーザーが、更新を完了するまでデータをロックします(16.4.5項「ペシミスティック・ロック・ポリシー」を参照)。
ロックなし: 複数のユーザーが互いの変更内容を上書きする操作は阻止されません。
ほとんどのタイプのアプリケーションでは、ユーザー同士が互いの変更内容を上書きできないようにするために、オプティミスティック・ロックを使用することをお薦めします。
詳細は、次を参照してください。
ディスクリプタに記述したクラスに関連付けられているデータ・ソースのデータ・タイプがTopLinkランタイムに認識されるようにするには、デフォルトのルート要素を使用して、EISルート・ディスクリプタ(76.3項「デフォルトのルート要素の構成」を参照)およびXMLディスクリプタ(52.4項「デフォルトのルート要素の構成」を参照)を構成します。
注意: 参照オブジェクトの未定義のドキュメント・ルート要素は、コレクション・マッピングおよびオブジェクト・マッピングによるマーシャリング中に無視されます。 |
この項では、デフォルトのルート要素の概要と、それがTopLinkでどのように使用されるかについて説明します。
例16-1に示すCustomer
クラスとAddress
クラス、およびそれらのマッピングについて考えてみます。
例16-1 CustomerクラスとAddressクラス
Class:
CustomerDefault Root:
customerAttributes and Mappings:
name:String Drect Mapping to name/text() billingAddress:Address Composite Object Mapping to billing-address shippingAddress:Address Composite Object Mapping to shipping-addressClass:
AddressDefault Root:
addressAttributes and Mappings:
street:String Direct Mapping to street/text() city:String Direct Mapping to city/text()
これらのクラスは、例16-2に示すXMLスキーマに対応しています。
例16-2 CustomerおよびAddressスキーマ
<xsd:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsd:complexType name="address-type"> <xsd:sequence> <element name="street" type="xsd:string"/> <element name="city" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:element name="customer" type="customer-type"/> <xsd:complexType name="customer-type"> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:element name="billing-address" type="address-type"/> <xsd:element name="shipping-address" type="address-type"/> </xsd:sequence> </xsd:complexType> </xsd:schema>
Customer
クラスのインスタンスをXMLに永続化する際には、TopLinkランタイムで次の処理が行われます。
デフォルトのルート要素が取得されます。
Customer
クラス・インスタンスは、XML文書のルートに対応しています。TopLinkランタイムは、ディスクリプタ(customer
)に指定されているデフォルトのルート要素を使用して、XML文書を開きます。次に、ディスクリプタに含まれるマッピングを使用して、そのオブジェクトの属性をマーシャリングします。
<customer> <name>…</name> </customer>
TopLinkランタイムは、たとえば、オブジェクト属性billingAddress
に遭遇すると、この属性に関連付けられているマッピングをチェックし、処理をどの要素(billing-address
)に移すかを判別します。
<customer> <name>…</name> <billing-address/> </customer>
TopLinkランタイムはマッピングの参照ディスクリプタ(Address
)をチェックして、永続化する属性を判別します。
<customer> <name>…</name> <billing-address> <street>…</street> <city>…</city> </billing-address> </customer>
継承とは、導出されたクラスにそのスーパークラスの特性をどのように受け継がせるかを意味します。ディスクリプタを使用すると、リレーショナル・プロジェクト、EISプロジェクトおよびXMLプロジェクトにおいてクラス間の継承リレーションシップを定義できます。
図16-1は、Java継承階層の代表例としてVehicle
というオブジェクト・モデルを示します。ルート・クラスのVehicle
には、2つブランチ・クラスFueledVehicle
およびNonFueledVehicle
が含まれています。各ブランチ・クラスには、それぞれCar
およびBicycle
というリーフ・クラスが含まれます。
TopLinkが継承階層で認識するクラスは、次の3つの種類です。
ルート・クラスには、サブクラス階層内のインスタンス化可能なすべてのクラスに関する情報が格納されます。デフォルトでは、ルート・クラスに対して実行された問合せにより、ルート・クラスおよびインスタンス化可能なサブクラスのインスタンスが返されます。ただし、ルート・クラスの構成方法によっては、ルート・クラスに対する問合せ時にサブクラスのインスタンスを省いて、ルート・クラス自体のインスタンスのみを返すこともできます。
たとえば、図16-1のVehicle
クラスが、ルート・クラスです。
ブランチ・クラスは、永続スーパークラスの他にサブクラスを持つものです。デフォルトでは、ブランチ・クラスに対して実行された問合せにより、ブランチ・クラスおよびそのサブクラスのインスタンスが返されます。ただし、ルート・クラスの場合と同様に、ブランチ・クラスの構成方法によっては、ブランチ・クラスに対する問合せ時にサブクラスのインスタンスを省いて、ブランチ・クラス自体のインスタンスのみを返すこともできます。
たとえば、図16-1のFueledVehicle
クラスが、ブランチ・クラスです。
リーフ・クラスには、階層内の永続スーパークラスがありますが、サブクラスはありません。リーフ・クラスに対して実行された問合せにより、リーフ・クラスのインスタンスのみが返されます。
たとえば、図16-1のCar
クラスが、リーフ・クラスです。
子クラスのディスクリプタでは、親クラスのディスクリプタに指定されているマッピングをオーバーライドしたり、親クラスのディスクリプタにマップされていない属性をマップできます。
この項では、次の内容について説明します。
親(ルート)クラス・ディスクリプタに関する継承の構成方法の詳細は、119.21項「親(ルート)ディスクリプタに関する継承の構成」を参照してください。
子(ブランチまたはリーフ)クラス・ディスクリプタに関する継承の構成方法の詳細は、119.20項「子(ブランチまたはリーフ)クラス・ディスクリプタに関する継承の構成」を参照してください。
継承を構成する場合は、ルート・クラス・ディスクリプタに、インスタンス化先のサブクラスの選択の方法を構成します。
これは、次のいずれかの方法で行えます。
注意: 継承階層内のすべてのリーフ・クラスにはクラス・インジケータが必須で、かつこれらのリーフ・クラスは同じタイプのクラス・インジケータ(フィールドまたはクラス抽出メソッド)を持つ必要があります。 |
クラスの永続属性を使用すると、インスタンス化先のサブクラスを指定できます。たとえば、リレーショナル・ディスクリプタでは、ルート・クラス表のクラス・インジケータ・フィールドを使用できます。ただし、クラス・インジケータ・フィールドには、読取り専用に設定されていないダイレクト・マッピングを関連付けないでください。
注意: クラス・インジケータ・フィールドが主キーの一部である場合は、このフィールドに対して書込み専用トランスフォーメーション・マッピングを定義します(第39章「リレーショナル・トランスフォーメーション・マッピングの構成」を参照)。 |
クラス・インジケータ・フィールドでは、値として文字列または数値を使用できます。
ルート・クラス・ディスクリプタによって、クラス・インジケータ・フィールドの値をインスタンス化されるクラスに変換する方法を指定する必要があります。
そのための方法の1つは、クラス・インジケータ・ディクショナリ(クラス・インジケータ・フィールドに格納される単純なキーを、インスタンス化するクラスに関連付けるキー値のコレクション)を使用して、ルート・クラス・ディスクリプタを構成することです。表16-3に、図16-1で示したVehicle
クラスのサブクラスのクラス・インジケータ・ディクショナリを示します。
もう1つの方法は、単純にクラス名自体をクラス・インジケータ・フィールドへの値として格納することです。こうすると、クラス名の長さの範囲内で通常より長めのキー値を使用してまでクラスごとに一意のインジケータを定義することの必要性から解放されます。
オブジェクトのデータ・ソース・レコード内で利用可能なすべての情報に基づいて、クラス・インジケータを計算するJavaメソッドを定義できます。このようなメソッドをクラス抽出メソッドといいます。
クラス抽出メソッドを使用すると、データ・モデルに明示的なクラス・インジケータ・フィールドを含める必要がなくなり、また、複雑すぎてクラス・インジケータ・フィールドには定義できないリレーションシップを処理できます。
クラス抽出メソッドには次の特性が必要です。
ルート・ディスクリプタのクラスをベースにして定義されていること
静的であること
引数としてRecord
をとること
入出力引数であるRecord
にjava.lang.Class
オブジェクトを出力すること
また、必要に応じて、OnlyInstancesExpressionとWithAllSubclassesExpressionも定義します(16.3.1.2.1項「OnlyInstancesExpressionとWithAllSubclassesExpressionの指定」を参照)。
表16-4は、一例としてEMPLOYEE
という表の行を示したものです。ベース・クラスはEmployee
クラスです。Director
、Manager
、Programmer
およびTechWriter
クラスはいずれもEmployee
クラスから導出されます。ただし、構築するアプリケーションでは、Manager
、Programmer
およびTechWriter
クラスのインスタンスはEmployee
のインスタンスとして表現するものの、Director
のインスタンスはDirector
のインスタンスとして表現する必要があります。クラスとJOB_TYPE
フィールド値は1対1で対応していないため、JOB_TYPE
フィールド単独ではクラス・インジケータ・フィールドとして機能しません(16.3.1.1項「クラス・インジケータ・フィールドの使用」を参照)。この問題を解決するには、例16-3に示すクラス抽出メソッドを使用できます。
表16-4 EMPLOYEE表
ID | NAME | JOB_TYPE | JOB_TITLE |
---|---|---|---|
732 |
Bob Jones |
1 |
Manager |
733 |
Sarah Smith |
3 |
Technical Writer |
734 |
Ben Ng |
2 |
Director |
735 |
Sally Johnson |
3 |
Programmer |
例16-3 クラス抽出メソッド
... // If the JOB_TYPE field value in record equals 2, return the Director class. // Return the Employee class for all other JOB_TYPE field values public static Class getClassFromRecord(Record record) { if (record.get("JOB_TYPE").equals(new Integer(2)) { return Director.class; } else { return Employee.class; } }
クラス抽出メソッドを使用して継承を構成した場合、Oracle TopLinkではルート・クラスに関する問合せのためのSQLは生成されません。
クラス抽出メソッド(16.3.1.2項「クラス抽出メソッドの使用」を参照)を使用する場合は、共通の表を使用するすべてのクラスの兄弟インスタンスを正しくフィルタ処理するための式を、TopLinkに指定する必要があります(119.22項「親(ルート)クラス・ディスクリプタに関する継承式の構成」を参照)。
リレーショナルおよびEISプロジェクトの場合、TopLinkでは、継承階層内のすべてのクラスが、ルート・ディスクリプタに設定されているのと同じ主キーを持っていることが前提とされます。
詳細は、次を参照してください。
リレーショナル・プロジェクトでは、継承階層を単一表(21.3.2.1項「単一表の継承」を参照)にも複数表(21.3.2.2項「複数表の継承」を参照)にもマップできます。
リレーショナル・ディスクリプタは集約ディスクリプタとして、EISディスクリプタはコンポジット・ディスクリプタとして指定できます。XMLディスクリプタは必ずコンポジット・ディスクリプタである必要があります(50.1.1項「XMLディスクリプタと集約」を参照)。
リレーショナル集約ディスクリプタで継承を構成する場合は、継承ツリー内のすべてのディスクリプタが集約である必要があります。つまり、集約クラスと非集約クラスのディスクリプタを同じ継承ツリーに置くことはできません。
同様に、EISコンポジット・ディスクリプタで継承を構成する場合も、継承ツリー内のすべてのディスクリプタはコンポジットである必要があります。つまり、コンポジット・クラスと非コンポジット・クラスのディスクリプタを同じ継承ツリーに置くことはできません。
XMLディスクリプタで継承を構成する場合は、すべてのXMLディスクリプタがコンポジットであるため、継承はディスクリプタ・タイプによる制限を受けません。
この項では、TopLinkでサポートされている次のような様々なタイプのロック・ポリシーについて説明します。
詳細は、119.26項「ロック・ポリシーの構成」を参照してください。
オプティミスティック・ロックを使用した場合、すべてのユーザーにデータへの読取りアクセス権限があります。ユーザーが変更を加えようとすると、アプリケーションにより、ユーザーがデータを読み取ってから、そのデータが変更されていないかが確認されます。
オプティミスティック・バージョン・ロック・ポリシーでは、バージョン・フィールド(書込みロック・フィールド)を使用して、オプティミスティック・ロックが実行されます。バージョン・フィールドは、参照クラス内に作成し、オブジェクト変更がコミットされるたびにTopLinkによって更新されます。
TopLinkは、データ・ソースからオブジェクトを読み取るときにこのバージョン・フィールドの値をキャッシュします。クライアントがオブジェクトに書き込もうとすると、TopLinkはキャッシュしたバージョン値とデータ・ソース内の最新のバージョン値を次のように比較します。
2つの値が一致した場合は、オブジェクト内のバージョン・フィールドを更新し、データ・ソースに対する変更内容をコミットします。
2つの値が一致しなかった場合は、そのクライアントが最初にオブジェクトを読み取った後に別のクライアントがそのオブジェクトを更新したことを意味するため、書込み操作を却下します。
TopLinkには、次のようなバージョン・ベースのオプティミスティック・ロック・ポリシーが用意されています。
VersionLockingPolicy
: numericタイプのバージョン・フィールドが必要です。TopLinkは、値を1ずつ増分することによってこのバージョン・フィールドを更新します。
TimestampLockingPolicy
: timestampタイプのバージョン・フィールドが必要です。TopLinkは、新しいタイムスタンプを挿入することによってこのバージョン・フィールドを更新します(このポリシーは、タイムスタンプをデータ・ソースまたはローカル時間から取得するように構成できます。デフォルトではデータ・ソースから取得されます)。
注意: 次の理由から、通常はnumericタイプのバージョンのロックをお薦めします。
|
オプティミスティック・ロック違反により更新が失敗すると、TopLinkではOptimisticLockException
がスローされます。この例外は、データベースの変更を行っているアプリケーションで処理する必要があります。アプリケーションは、ロックの競合をクライアントに通知し、オブジェクトをリフレッシュして、クライアントに変更内容の再適用を依頼する必要があります。
バージョン値は、オブジェクト内にマップ済属性として格納するか、キャッシュに格納するかを選択できます。3層アプリケーションでは、オブジェクトの更新時にバージョン値が確実にクライアントに渡されるようにするため、オブジェクト内にバージョン値を格納しておく方法が一般的です(16.4.6項「3層アプリケーションでのロック」を参照)。
バージョン値をキャッシュに格納する場合、バージョン値のマッピングは必要ありません。バージョン・フィールドをマッピングする場合は、マッピングを読取り専用として構成する必要があります(121.2項「読取り専用マッピングの構成」を参照)。
私有の子オブジェクトの変更時にその親オブジェクトのバージョン・フィールドが確実に更新されるようにするには、16.4.2項「オプティミスティック・バージョン・ロック・ポリシーとカスケード」を使用することを検討します。
作業ユニットによるオプティミスティック・バージョン・ロックを使用する場合は、115.11項「forceUpdateToVersionFieldメソッドによるオプティミスティック読取りロックの使用」を検討します。
ストアド・プロシージャを使用してオブジェクトを更新または削除すると、データベースは、オプティミスティック・ロックの失敗を検出するために必要な行カウントを返さない場合があります。そのため、オプティミスティック・ロックのバージョンのチェックおよび一致しない場合のエラーのスローにもストアド・プロシージャが使用されます。StoredProcedureCall
で直接サポートされているのはバージョンのロックのみです。タイムスタンプおよびフィールド・ロックでは、コールに対して同じフィールドの2つのバージョンを渡す必要があるため、##
パラメータを使用して変換行にアクセスするSQLコールは他のロック・ポリシーにも使用できます。詳細は、109.5項「StoredProcedureCallの使用」および109.6項「StoredFunctionCallの使用」を参照してください。
使用するデータベース・スキーマが、親オブジェクトおよびその私有の子オブジェクトの両方を同じ表内に格納する設計になっている場合には、その子オブジェクトを更新すると、親オブジェクトのバージョン・フィールドが更新されます。
一方、親オブジェクトおよびその私有の子オブジェクトを異なる表に格納する場合は、デフォルトでは、子を変更しても、親のバージョン・フィールドは更新されません。
この場合に親オブジェクトのバージョン・フィールドを確実に更新するには、親オブジェクトのバージョン・フィールドを手動で更新します(115.11項「forceUpdateToVersionFieldメソッドによるオプティミスティック読取りロックの使用」を参照)。または、VersionLockingPolicy
ポリシーを使用しているのであれば、子オブジェクトのバージョン・フィールドの更新が親オブジェクトに自動的にカスケードされるようにTopLinkを構成する方法もあります(119.26.2.2項「オプティミスティック・ロック・ポリシーのカスケードの構成」を参照)。
オプティミスティック・バージョン・ロックのカスケード機能を有効化した場合に、私有の子オブジェクトを変更すると、TopLinkはその私有されている外部参照マッピングを走査し、ルートに向かってすべての親オブジェクトを更新します。
オプティミスティック・バージョン・ロックのカスケードは、子オブジェクトが作業ユニット内に登録されている場合にのみ適用されます。
TopLinkでは、次の場合にオプティミスティック・バージョン・ロックのカスケードがサポートされます。
私有の1対1および1対多マッピングでのオブジェクト変更
次のコレクション・マッピング(私有かどうかは問わない)でのリレーションシップ変更(追加または削除)
ダイレクト・コレクション
1対多
多対多
集約コレクション
図16-2に例示したオブジェクトの図について考えてみます。
この例では、ObjectA
がObjectB
を私有し、ObjectB
はObjectC
を私有し、ObjectC
はObjectD
を私有します。
ObjectB
を作業ユニットに登録し、ObjectB
を変更して、作業ユニットをコミットしたとします。この場合、ObjectB
はObjectA
のキャッシュをチェックし、値が存在しない場合にはデータベースに対してObjectA
を問い合せます。次に、ObjectB
は自身の変更をObjectA
に通知します。ObjectA
は、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。
ObjectA
を作業ユニットに登録して、ObjectA
→ObjectB
→ObjectC
→ObjectD
の順にアクセスし、ObjectD
のフィールドを変更して、作業ユニットをコミットしたとします。この場合は、ObjectD
が自身の変更をObjectC
に通知します。ObjectC
は、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。次に、ObjectC
はObjectD
の変更をObjectB
に通知します。次に、ObjectB
がObjectD
の変更をObjectA
に通知します。ObjectA
は、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。
オプティミスティック・ロックで、オプティミスティック・ロックのバージョンをキャッシュに格納する場合、ロックされたオブジェクトの値をロールバックするにはUnitOfWork
メソッドcommitAndResumeOnFailure
(115.6項「コミット後の作業ユニットの再開」を参照)を使用します。
ロックされた複数のバージョンを1つのオブジェクトに格納する場合は、更新失敗時に各オブジェクト(すなわち、オブジェクトのバージョン)をリフレッシュする必要があります。あるいは、失敗時に新しい作業ユニットを取得して、そのユニットに変更を再適用することもできます。
オプティミスティック・フィールド・ロック・ポリシーでは、表内に現存する1つ以上のフィールドを使用して、クライアントが対象オブジェクトを読み取った以後にそのオブジェクトが変更されたかどうかを判別することによって、オプティミスティック・ロックが実行されます。
ユーザーがオブジェクトを最初に読み取ったとき、またはオブジェクトを作業ユニットに登録したときに、作業ユニットにより、そのオブジェクトの元の状態がキャッシュされます。コミット時には、ロック・フィールドの元の値と更新中のデータ・ソースの現在の値が、作業ユニットにより比較されます。ロック・フィールドのいずれかの値が変更されていた場合には、オプティミスティック・ロック例外がスローされます。
TopLinkには、次のようなオプティミスティック・フィールド・ロック・ポリシーが用意されています。
AllFieldsLockingPolicy
: 更新または削除操作を行うと、TopLinkは対象オブジェクトのすべてのフィールドをデータ・ソース内のすべてのフィールドと比較します。いずれかのフィールドの元の値がデータ・ソース内の値と異なる場合、その書込み操作は却下されます。
たとえば、ある顧客の姓を変更した場合、TopLinkにより次のようなSQLが生成されます。
UPDATE CUSTOMER SET LNAME='new last name' WHERE ID=7 AND LNAME='old last name' AND FNAME='Donald' AND B_DAY='1972' AND CREDIT_RATING='A+' AND EYE_COLOR='Blue'
このフィールド・ロック・ポリシーの主な短所は、特に、多くの属性を持つオブジェクトの変更時に、最適なパフォーマンスが得られない点です。
注意: この比較は、表単位でのみ行われます。複数表にマップされているオブジェクト(複数表の継承)に対して更新操作を行うと、変更された各表の変更されたフィールドのみがwhere 句に表示されます。 |
ChangedFieldsLockingPolicy
: 更新操作を行うと、TopLinkは対象オブジェクトの変更されたフィールドのみをデータ・ソース内の対応フィールドと比較します。いずれかのフィールドの元の値がデータ・ソース内の値と異なる場合、その書込み操作は却下されます。削除に対しては、TopLinkではフィールド比較は実行されません。
このフィールド・ロック・ポリシーの主な長所は、複数のフィールドを同時に更新できる点です。たとえば、ChangedFieldsLockingPolicy
を使用してCustomer
ディスクリプタを構成した場合に、あるスレッドで顧客の姓が更新され、別のスレッドではその顧客の信用格付けが更新されると、TopLinkにより次のようなSQLが生成されます。
// Unit of work 1 UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name' // Unit of work 2 UPDATE CUSTOMER SET CREDIT_RATING='B' WHERE ID=7 AND CREDIT_RATING='A+'
SelectedFieldsLockingPolicy
: 更新または削除操作を行うと、TopLinkは対象オブジェクトの選択されたフィールドのみを、データ・ソース内の対応フィールドと比較します。いずれかのフィールドのキャッシュされた値がデータ・ソース内の値と異なる場合、その書込み操作は却下されます。
たとえば、Customer
属性のLNAME
とCREDIT_RATING
を選択した場合、TopLinkは実行時に次のようなSQLを生成します。
UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name' AND CREDIT_RATING='A+'
オプティミスティック・ロック違反により更新が失敗すると、TopLinkではOptimisticLockException
がスローされます。この例外は、データベースの変更を行っているアプリケーションで処理する必要があります。アプリケーションは、ロックの競合をクライアントに通知し、オブジェクトをリフレッシュして、クライアントに変更内容の再適用を依頼する必要があります。
フィールド・ロック・ポリシーを使用する場合は、データ・ソースを更新するために作業ユニットを使用する必要があります。
注意: AttributeChangeTrackingPolicy (113.2.3.3項「属性変更追跡ポリシー」を参照)を使用している場合、FieldsLockingPolicy のインスタンスは使用できません。 |
ペシミスティック・ロックを使用した場合は、データを更新する目的でそのデータにアクセスする最初のユーザーが、更新を完了するまでデータをロックします。
ペシミスティック・ロック・ポリシーを使用する場合は、更新をただちに失敗させるか、読取りロックが取得されるまで待機させるようにポリシーを構成できます。
ペシミスティック・ロック・ポリシーを使用できるのは、コンテナ管理の永続性タイプ(117.5項「永続性タイプの構成」を参照)を指定し、EJB情報を持つディスクリプタ(119.18項「EJB CMPおよびBMP情報によるディスクリプタの構成」を参照)を使用したプロジェクトのみです。
また、問合せレベルでペシミスティック・ロック機能(ペシミスティック・ロック・ポリシーではない)を使用することもできます(119.7.1.9項「名前付き問合せのオプションの構成」を参照)。
コンテナ管理の永続性を備えたエンティティBeanでペシミスティック・ロックが使用される場合、TopLinkではこのロックが最適化されます。問合せをペシミスティック・ロックに設定し、その問合せを独自の新規トランザクション(ファインダの実行後に終了するトランザクション)内で実行すると、TopLinkによりロック設定が上書きされ、SQLにFOR UPDATE
は追加されません。ただし、ペシミスティック・ロック問合せがFOR UPDATEを含むSQL文字列でユーザーによりカスタマイズされている場合、この最適化の使用により不適切な結果が生じることがあります。この場合、最適化の条件が存在すると、問合せはペシミスティック・ロックを使用しないようリセットされますが、SQLはそのままの状態で残るため、問合せのロック設定が問合せのSQL文字列と競合することになります。この問題を回避するには、次のいずれかの方法を使用します。
選択基準としてEJB QLまたはTopLinkの式(第110章「TopLinkの式の概要」を参照)を使用します。これにより、TopLinkでSQLの生成を制御します。
最適化の条件をなくすため、ファインダをトランザクション内に配置します。
3層アプリケーションを構築する場合は、オブジェクトが正しくロックされるようにするため、そのオブジェクトが編集用にクライアントに送信される前にロックを取得できるようにする必要があります。
オプティミスティック・ロックを使用する場合、オブジェクトを正しくロックするには次の2つの選択肢があります。
オブジェクト内のオプティミスティック・ロック・フィールドを読取り専用以外にマップし、読取り時にバージョンをクライアントに渡し、更新時にサーバーに戻します。
バージョン・フィールドに対して読取り専用以外のマッピングを定義し、バージョン値がキャッシュではなくオブジェクト内に格納されるようにオプティミスティック・ロック・ポリシーを設定する必要があります。これは、TopLink Workbenchで「ロック」タブの「キャッシュにバージョンを保存」の選択を解除することで設定できます(119.26.1項「TopLink Workbenchを使用したロック・ポリシーの構成方法」を参照)。
クライアントがオブジェクトを更新目的で読み取るときに、元のバージョン値がクライアントに確実に送信されるようにします。次に、クライアントが、元のバージョン値を更新情報とともに戻す必要があります。また、このバージョンは、サーバー上の新しい作業ユニット内に登録されたかまたは読み込まれた後に、更新対象のオブジェクト内に設定される必要があります。
クライアントとの対話が完了するまで作業ユニットを保持します。
ステートフル・セッションBeanを介して、またはHTTPセッションにおいて、更新対象オブジェクトの読取りに使用する作業ユニットを、クライアントとの対話が完了するまで保管します。
オブジェクトをクライアントに更新用に渡す前に、この作業ユニットからオブジェクトを読み取っておく必要があります。これにより、作業ユニットのキャッシュまたは作業ユニットのクローンに元のバージョン値を保管しておけます。
この同じ作業ユニットを更新でも必ず使用します。
1番目の選択肢の方が一般的に使用され、ステートレス・アプリケーションを開発する場合には必須となります。
ペシミスティック・ロックを使用する場合は、作業ユニットを使用して、オブジェクトが読み取られる前にデータベース・トランザクションを開始する必要があります。この作業ユニットとデータベース・トランザクションは、クライアントがオブジェクトを編集して更新するまでの間、保持する必要があります。この同じ作業ユニットをオブジェクトの更新でも必ず使用します。(通常クライアントとの対話中はデータベース・トランザクションをオープンにしておくことは望ましくないとされている)3層Webアプリケーションを構築する場合は、一般的にはペシミスティック・ロックよりもオプティミスティック・ロックの方が適しています(16.4.6.1項「3層アプリケーションでのオプティミスティック・ロック」を参照)。
ディスクリプタAPIを使用すると、Javaコードを介してTopLinkディスクリプタを定義または修正できます。ディスクリプタAPIのクラスは、主としてoracle.toplink.descriptors
パッケージに含まれています。このようなクラスには次のものがあります。
ClassDescriptor
(抽象汎用ディスクリプタAPI)
RelationalDescriptor
(リレーショナル・プロジェクト固有API)
DescriptorEventManager
(イベントAPI)
DescriptorQueryManager
(問合せAPI)
InheritancePolicy
InterfacePolicy
ReturningPolicy
ロック・ポリシー(各種のオプティミスティック・ロック・ポリシー)
オブジェクト・リレーショナル・データ・タイプ、EISおよびXMLプロジェクト用のディスクリプタ・クラスは、それぞれoracle.toplink.objectrelational
、oracle.toplink.eis
およびoracle.toplink.ox
パッケージに含まれています。
この項では、「ディスクリプタの継承階層」などのOracle TopLink Foundation Libraryの重要なディスクリプタ・クラスについて説明します。
例16-4は、クラスoracle.toplink.descriptors.ClassDescriptor
から導出されたディスクリプタ・タイプを示します。