ヘッダーをスキップ
Oracle TopLink開発者ガイド
10g(10.1.3.1.0)
B31861-01
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

23 ディスクリプタの概要

TopLinkでは、ディスクリプタを使用して、あるデータ・ソースによって特定クラスのインスタンスがどのように表現されるかを定義した情報を格納します。ディスクリプタには、クラスのインスタンス変数を、データ・ソースおよび、値の格納と取得に使用されるトランスフォーメーション・ルーチンに関連付けるマッピングを含めます。それによって、ディスクリプタは、Javaオブジェクトとそのデータ・ソース表現を接続する役目を果します。

この章の内容は次のとおりです。

ディスクリプタ・タイプ

表23-1は、オブジェクト・モデルにクラスを定義するために使用するディスクリプタ・タイプをまとめたものです。それぞれについて、基本タイプか詳細タイプかの分類も示します。

表23-1 TopLinkのディスクリプタ・タイプ

ディスクリプタ・タイプ 説明 タイプ TopLink Workbench Java

「リレーショナル・ディスクリプタ」


リレーショナル・データベース内の表にマップするJavaオブジェクトを記述します。TopLinkでサポートされるすべてのリレーショナル・データベースに対してのみ適用可能です。

基本

サポートされている
サポートされている

「オブジェクト・リレーショナル・ディスクリプタ」


より近密に各オブジェクト・タイプに対応した専用のデータベース・データ・タイプを提供している、リレーショナル・データベースの表にマップされるJavaオブジェクトを記述します。TopLinkでサポートされるリレーショナル・データベースのうち、これら専用のデータ・タイプを提供するリレーショナル・データベースに対してのみ適用可能です。

詳細

サポートされていない
サポートされている

「EISディスクリプタ」


J2Cアダプタを介してEISデータ・ソースにマップするJavaオブジェクトを記述します。

基本

サポートされている
サポートされている

「XMLディスクリプタ」


XMLスキーマ・ドキュメント(XSD)に定義されているXML文書内の複合型へのインメモリー・マッピングを行うJavaオブジェクトを記述します。

基本

サポートされている
サポートされている

詳細は、次を参照してください。

ディスクリプタの概念

この項では、次の内容を含む、TopLinkに固有のディスクリプタの概念について説明します。

ディスクリプタのアーキテクチャ

ディスクリプタには、あるデータ・ソースによって特定オブジェクト・クラスのインスタンスがどのように表現されるかを定義したすべての情報を格納します。

TopLinkディスクリプタには次の情報を指定します。

  • ディスクリプタ自体の中で記述する永続Javaクラス、および対応するデータ・ソース(データベース表、XMLの複合型またはEISインタラクション)。

  • そのクラスの属性とリレーションシップをデータベースにどのように格納するかを記述したマッピングのコレクション。

  • データ・ソースの主キー情報(またはそれに相当する情報)。

  • フィールド名の問合せキー(または別名)のリスト。

  • 順序番号の情報。

  • ディスクリプタの動作を調整するためのオプション・プロパティのセット。リフレッシュ・オプションのキャッシング、アイデンティティ・マップ、オプティミスティック・ロック、イベント・マネージャおよび問合せマネージャに関するサポートが含まれます。

TopLinkがサポートするデータ・ソース・タイプごとに、対応するディスクリプタ・タイプがあります。同一のデータ・ソース・タイプに対して有効なディスクリプタ・タイプが複数ある場合もあります。使用するディスクリプタのタイプにより、定義できるマッピングのタイプが決まります。

表23-2は、プロジェクト、ディスクリプタ、マッピングの関係をまとめたものです。

ディスクリプタと継承

継承とは、導出されたクラス(子)にそのスーパークラス(親)の特性をどのように受け継がせるかを意味します。ディスクリプタを使用すると、リレーショナル・プロジェクト、EISプロジェクトおよびXMLプロジェクトにおいてクラス間の継承リレーションシップを定義できます。

子クラスのディスクリプタでは、親クラスのディスクリプタに指定されているマッピングをオーバーライドしたり、親クラスのディスクリプタにマップされていない属性をマップできます。

詳細は、「ディスクリプタと継承の概要」を参照してください。

ディスクリプタとEJB

ディスクリプタを使用すると、コンテナ管理の永続性を備えたエンティティ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コンテナ(OC4Jなど)のネイティブの動作との下位互換性が得られます。また、トランザクションの一時的な状態を使用してトリガーやストアド・プロシージャなどのためにデータベースとエンティティの変更を同期化するアプリケーションや、同一の主キーを使用して行を削除および作成するアプリケーション、またはトランザクション内の一時的な状態を使用してその他の複雑な問合せを行うアプリケーションなど、高度なアプリケーションにも対応可能となります。

非遅延変更の短所は、データ・ソースとのインタラクションの数が最大になるため、方法としては最も効率が悪いことです。

非遅延変更がサポートされるようにTopLink CMPを構成した場合でも、同じ遅延設定を持つ複数のエンティティBean間でマップされたリレーションシップについての制約は、TopLinkによって引き続き処理されます。ただし、非遅延設定のクラスが遅延設定のクラスに関連付けられており、その2つのクラス間に制約がある場合には、非遅延クラスに加えられた変更に起因するエラーは、開発者が処理する必要があります。


注意:

非遅延変更を行うようにディスクリプタを構成した場合でも、TopLink CMPでは、依存オブジェクトに対しては非遅延変更が適用されません。依存オブジェクトにはデフォルトである遅延変更が適用されるためです。したがって、リレーショナル・スキーマはコミット時まで変更されません。

詳細は、「EJB情報によるディスクリプタの構成」を参照してください。

新規エンティティBeanおよびejbCreate/ejbPostCreateメソッドの作成

新規のエンティティBeanを作成した場合、デフォルトでは次のようなBeanのライフ・サイクルが想定されます。

  1. ejbCreateメソッド:

    挿入後、EJBコンテナは、作成されたインスタンス用に、データベースによって割り当てられた主キーを取得します。

    リレーショナル・プロジェクトの場合:

    1. INSERT INTO ...

    2. SELECT FROM ...

    EISプロジェクトの場合:

    1. Write object ...

    2. Find object ...

  2. ejbPostCreateメソッド:

    EJBコンテナにより、コンテナ管理のリレーションシップ(CMR)のフィールドが更新されます。EJBコンテナには、ejbCreateメソッドで取得された主キーが必要です。

    リレーショナル・プロジェクトの場合:

    1. UPDATE SET ...

    EISプロジェクトの場合:

    1. Write object ...

なお、データベースに非NULLの外部キー制約がある場合は、ejbCreateメソッドの実行後にデータ・ソースを変更すると問題が発生することがあります。このような問題を回避するために、一部のアプリケーション・サーバー(OC4Jなど)では、ejbPostCreateメソッドの実行後に新しいオブジェクトを作成できるようにして、コンテナを使用して外部キー制約を解決するようにしています。

詳細は、「EJB情報によるディスクリプタの構成」を参照してください。

継承

TopLinkでは、いくつか制約はあるものの、CMPディスクリプタで継承を構成できます。

詳細は、「継承とEJB」を参照してください。

フェッチ・グループ

デフォルトでは、特定のオブジェクト・クラスに対してオブジェクト・レベルの読取り問合せを実行すると、そのオブジェクトのディスクリプタにマップされているすべての永続属性がTopLinkによって返されます。この1回の問合せを実行するだけで、対象オブジェクトのすべての永続属性が定義され、さらに、各属性のgetメソッドをコールすることで、属性値をオブジェクトから直接取得できます。

オブジェクトの属性の一部のみが必要な場合は、そのオブジェクトの属性のサブセットのみが返されるようにした方が効率的です。これを行うには、フェッチ・グループを使用してオブジェクトの属性のサブセットを定義し、そのフェッチ・グループをReadObjectQueryまたはReadAllQuery問合せと関連付けます。

詳細は、次を参照してください。

修正メソッドとロード後メソッド

TopLink Workbenchを使用すると、実行時にディスクリプタがロードされたときにコールされる静的Javaメソッドを関連付けることができます。このメソッドは、ディスクリプタのJavaコードAPIを介して実行時のディスクリプタ・インスタンスを修正できます。このメソッドを使用すると、現在のTopLink Workbenchではサポートされていないような高度な構成オプションを作成できます。ただし、ディスクリプタはセッションが接続される前にのみ修正できます。これは、セッションの接続後にディスクリプタを修正するのは望ましくないためです。

詳細は、「修正メソッドの構成」を参照してください。

ディスクリプタと集約

2つのオブジェクト、つまり、ソース(親、すなわち所有)オブジェクトとターゲット(子、すなわち被所有)オブジェクトは、両者間に厳密な1対1の関係がある場合、集約によって関連付けられ、ターゲット・オブジェクトのすべての属性は、ソース・オブジェクトと同じデータ・ソース表現から取得できます。つまり、ソース・オブジェクトが存在すればターゲット・オブジェクトも存在する必要があり、ソース・オブジェクトが破棄されればターゲット・オブジェクトも破棄されるということです。

この場合、ソースおよびターゲット・オブジェクトのディスクリプタは、この関係を反映するよう、次の手順で定義する必要があります。

リレーショナル・プロジェクトでの集約ディスクリプタとコンポジット・ディスクリプタ

リレーショナル・プロジェクトでは、集約ディスクリプタを定義します(「リレーショナル集約ディスクリプタ」を参照)。

これにより、集約マッピングを構成して(第43章「リレーショナル集約オブジェクト・マッピングの構成」を参照)、ターゲット・オブジェクト内のデータ・メンバーを、ソース・オブジェクトの基礎となるデータベース表のフィールドと関連付けることができます。

リレーショナル・ディスクリプタを集約ディスクリプタとして定義する場合、TopLinkではターゲット・クラスのフィールドごとに1つのマッピング・タイプを指定できますが、フィールドとデータベース表の関連付けは、ソース・ディスクリプタで集約オブジェクト・マッピングを構成した後で可能になります。つまり、ターゲット・クラス・ディスクリプタは各ターゲット・クラス・フィールドがマップされる方法を定義し、ソース・クラス・ディスクリプタは各ターゲット・クラス・フィールドがマップされる場所を定義する、ということです。この仕組みにより、異なる表にマップされた多数の親ディスクリプタ間で同一の集約オブジェクトを共有することができます。

リレーショナル・ディスクリプタを集約ディスクリプタとして定義する場合は、そのクラスが集約オブジェクト・マッピングのターゲットになることをTopLinkに指示します。こうすることで、TopLinkランタイムにより、ターゲット・クラスが次のように処理されます。

  • ターゲット・クラスの挿入、更新、削除が、ソース・クラスの挿入、更新、削除とパラレルで実行されます。

  • ターゲット・クラスは単独ではキャッシュされず、ソース・クラスの一部としてキャッシュされます。

  • 作業ユニット内でのターゲット・クラスの読取り、書込み、削除、登録は許可されません。

集約リレーショナル・ディスクリプタを使用する場合は、次の事項に留意する必要があります。

詳細は、「クラスまたは集約タイプとしてのリレーショナル・ディスクリプタの構成」を参照してください。

リレーショナル集約とネスト

TopLinkでは、ネストされた集約はサポートされません。図23-1のソース・クラスHockeyPlayerは、通常の、非集約であるクラス・ディスクリプタです。このクラスは、集約として定義されたターゲット・クラスInfoを持っています。Infoクラス自体は、ターゲット・クラスPersonalInfoTeamInfoを持っており、この2つはいずれも集約として定義されています。

図23-1 ネストされた集約

図23-1の説明が続きます
「図23-1 ネストされた集約」の説明

EJB 3.0では、集約は埋込み可能であることが知られています。EJB 3.0仕様では、埋込み可能である集約に、埋込み可能な別の集約を含めることはできません(つまり、EJB 3.0仕様では、ネストされた集約はサポートされていません)。

ただし、TopLink対応の永続性を備えたEJB 3.0アプリケーションをOC4Jにデプロイすることで、TopLinkによる拡張EJB 3.0仕様を利用して、ネストされた埋込み可能集約を構成できます。ただし、この方法をとった場合、そのアプリケーションは厳密なEJB 3.0仕様準拠ではなくなりますので注意してください。例23-1に、図23-1の各クラスのコード例を示します。ここでは、EJB 3.0の注釈とともにTopLinkによる拡張EJB 3.0仕様を利用し、埋込み可能なInfoに、同じく埋込み可能なTeamInfoPersonalInfoを埋め込んでいます。

例23-1 ネストされた埋込み可能集約

public class HockeyPlayer implements Serializable {
    private int playerId;
    private Info Info;
    private String lastName;
    private String firstName;
    ...
    @Embedded
    public Info getInfo() {
        return Info;
    }
}

@Embeddable
public class Info implements Serializable {
    TeamInfo teamInfo; // TopLink extension of EJB 3.0 allows Embeddable with Embeddable
    PersonalInfo personalInfo;

    public Info() {}

    @Embedded
    public PersonalInfo getPersonalInfo() {
        return personalInfo;
    }

    public void setPersonalInfo(PersonalInfo personalInfo) {
        this.personalInfo = personalInfo;
    }

    @Embedded
    public TeamInfo getTeamInfo() {
        return teamInfo;
    }

    public void setTeamInfo(TeamInfo teamInfo) {
        this.teamInfo = teamInfo;
    }
}

@Embeddable
public class PersonalInfo implements Serializable {
    private int age;
    private double weight;
    private double height;
    ...
}

@Embeddable
public class TeamInfo implements Serializable {
    private String position;
    private int jerseyNumber;
    private HockeyTeam hockeyTeam;
    ...
}

リレーショナル集約と継承

集約として定義されたリレーショナル・ディスクリプタ(「ディスクリプタと継承」を参照)で継承を構成できますが、その場合は継承ツリー内のすべてのディスクリプタが集約である必要があります。集約ディスクリプタとクラス・ディスクリプタは、同じ継承ツリーに置くことはできません。

リレーショナル集約とEJB

EJBプロジェクトではリレーショナル集約ディスクリプタを使用できますが、集約として定義されたリレーショナル・ディスクリプタ用にEJB情報を構成することはできません(「ディスクリプタとEJB」を参照)。

リレーショナル集約とEJB 3.0の使用方法の詳細は、「リレーショナル集約とネスト」を参照してください。

EISプロジェクトでのルート・ディスクリプタとコンポジット・ディスクリプタ

EISプロジェクトでは、コンポジット・ディスクリプタを定義できます(「EISコンポジット・ディスクリプタ」を参照)。

作成するEISマッピングのタイプに応じて、EISディスクリプタをコンポジットとして構成するか、ルートとして構成するかが決まります(「コンポジットおよび参照EISマッピング」を参照)。

詳細は、「EISディスクリプタのタイプ(ルートまたはコンポジット)の構成」を参照してください。

コンポジット・ディスクリプタとして定義されたEISディスクリプタ用にEJB情報を構成することはできません(「ディスクリプタとEJB」を参照)。

コンポジットとして定義されたEISディスクリプタ(「ディスクリプタと継承」を参照)で継承を構成できますが、その場合は継承ツリー内のすべてのディスクリプタがコンポジットである必要があります。コンポジットとルートは、同じ継承ツリーに置くことはできません。

XMLプロジェクトでのコンポジット・ディスクリプタ

XMLプロジェクトでは、ディスクリプタは常にコンポジット・ディスクリプタです。

XMLディスクリプタは常にコンポジットであるため、XMLディスクリプタのタイプを考慮せずにXMLディスクリプタで継承を構成できます(「ディスクリプタと継承」を参照)。

ディスクリプタ・イベント・マネージャ

リレーショナル・プロジェクトおよびEISプロジェクトでは、永続データのライフ・サイクルの間にTopLinkがDescriptorEventの様々なインスタンスを発生させます(表25-26および表25-28を参照)。各ディスクリプタには、DescriptorEventManagerのインスタンスがあります。このインスタンスは、イベントを受信し、自身に登録されているディスクリプタ・イベント・ハンドラにディスパッチする働きをします。

ディスクリプタ・イベント・ハンドラを使用すると、構築したアプリケーション固有のロジックをディスクリプタ・イベントの発生時に実行でき、永続データのライフ・サイクルの様々な時点において実行可能なカスタマイズ・アクションをとれるようになります。たとえば、ディスクリプタ・イベント・ハンドラを使用して次の動作を実行できます。

  • 永続オブジェクトを他のシステム、サービスおよびフレームワークと同期化すること。TopLinkで認識されない非永続属性を保持すること。

  • オブジェクトの永続状態が変化したときにアプリケーションの他のオブジェクトに通知すること。

  • TopLinkのマッピングで直接サポートされていない複雑なマッピングまたは最適化を実装すること。

詳細は、次を参照してください。

ディスクリプタ問合せマネージャ

各リレーショナル・ディスクリプタとEISディスクリプタには、次の構成に使用できるDescriptorQueryManagerのインスタンスが用意されています。

問合せマネージャの使用方法の詳細は、「ディスクリプタ問合せマネージャ問合せ」を参照してください。

ディスクリプタと順序付け

オブジェクト・アイデンティティを保持する上で重要なのは、各オブジェクト・インスタンスを区別するために一意の値(特定の順序)の割当てを管理することです。詳細は、「プロジェクトおよび順序付け」を参照してください。

順序付けオプションにプロジェクト・レベルとセッション・レベルのいずれを構成するかにより、TopLinkで使用される順序付けのタイプが決まります。CMPプロジェクトの場合、通常はこの順序タイプをプロジェクト・レベルに構成します(「プロジェクト・レベルでの順序付けの構成」を参照)。CMP以外のプロジェクトの場合、セッション・レベルの順序構成を使用して、プロジェクト・レベルの順序構成をセッション単位でオーバーライドすることもできます(「セッション・レベルでの順序付けの構成」を参照)。

各ディスクリプタの参照クラスに対して順序タイプを構成した後は、属性の1つ(通常は主キーとして使用される属性。「主キーの構成」を参照)をディスクリプタ固有の順序と関連付けます(「ディスクリプタ・レベルでの順序付けの構成」を参照)。

ディスクリプタとロック

次に示すロック・ポリシーのいずれかを使用してディスクリプタを構成し、ドメイン・オブジェクトへの同時アクセスを制御できます。

ほとんどのタイプのアプリケーションでは、ユーザー同士が互いの変更内容を上書きできないようにするために、オプティミスティック・ロックを使用することをお薦めします。

詳細は、次を参照してください。

デフォルトのルート要素

ディスクリプタに記述したクラスに関連付けられているデータ・ソースのデータ・タイプがTopLinkランタイムに認識されるようにするには、デフォルトのルート要素を使用して、EISルート・ディスクリプタ(「デフォルトのルート要素の構成」を参照)およびXMLディスクリプタ(「デフォルトのルート要素の構成」を参照)を構成します。


注意:

参照オブジェクトの未定義のドキュメント・ルート要素は、コレクション・マッピングおよびオブジェクト・マッピングによるマーシャリング中に無視されます。

この項では、デフォルトのルート要素の概要と、それがTopLinkでどのように使用されるかについて説明します。

例23-2に示すCustomerクラスとAddressクラス、およびそれらのマッピングについて考えてみます。

例23-2 CustomerクラスとAddressクラス

Class: Customer
Default Root: customer
Attributes and Mappings:
    name:String                Drect Mapping to                name/text()
    billingAddress:Address     Composite Object Mapping to     billing-address
    shippingAddress:Address    Composite Object Mapping to     shipping-address

Class: Address
Default Root: address
Attributes and Mappings:
    street:String              Direct Mapping to               street/text()
    city:String                Direct Mapping to               city/text()

これらのクラスは、例23-3に示すXMLスキーマに対応しています。

例23-3 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ランタイムで次の処理が行われます。

  1. デフォルトのルート要素が取得されます。

    Customerクラス・インスタンスは、XML文書のルートに対応しています。TopLinkランタイムは、ディスクリプタ(customer)に指定されているデフォルトのルート要素を使用して、XML文書を開きます。次に、ディスクリプタに含まれるマッピングを使用して、そのオブジェクトの属性をマーシャリングします。

    <customer>
        <name>…</name>
    </customer>
    
    
  2. TopLinkランタイムは、たとえば、オブジェクト属性billingAddressに遭遇すると、この属性に関連付けられているマッピングをチェックし、処理をどの要素(billing-address)に移すかを判別します。

    <customer>
        <name>…</name>
        <billing-address/>
    </customer>
    
    

    TopLinkランタイムはマッピングの参照ディスクリプタ(Address)をチェックして、永続化する属性を判別します。

    <customer>
        <name>…</name>
        <billing-address>
            <street>…</street>
            <city>…</city>
        </billing-address>
    </customer>
    

リレーショナル・ディスクリプタ

リレーショナル・ディスクリプタには、リレーショナル・データベース内の表にマップするJavaオブジェクトを記述します。リレーショナル・ディスクリプタはリレーショナル・プロジェクトで使用します(「リレーショナル・プロジェクト」を参照)。

リレーショナル・ディスクリプタをリレーショナル・プロジェクトで使用すると、リレーショナル・マッピングを構成できます(「リレーショナル・マッピングのタイプ」を参照)。

詳細は、次を参照してください。

オブジェクト・リレーショナル・ディスクリプタ

オブジェクト・リレーショナル・パラダイムは、従来のリレーショナル・データベースを拡張してオブジェクト指向の機能を組み込むためのものです。Oracle、IBM DB2、Informixおよびその他のDBMSデータベースでは、ユーザーは、複雑なデータの格納、アクセス、使用をより高度な方法で行うことができます。オブジェクト・リレーショナル標準は、主にデータベースのデータ構造とSQL(SQL 3)の拡張を目的として改良が重ねられている標準です。

オブジェクト・リレーショナル・ディスクリプタには、より近密に各オブジェクト・タイプに対応している専用のリレーショナル・データベース・タイプにマップされるJavaオブジェクトを記述します。これら専用のオブジェクト・リレーショナル・データベース・タイプを使用すると、リレーショナル・データベース表へのオブジェクトのマッピングが容易になります。ただし、これら専用のオブジェクト・リレーショナル・データベース・タイプは一部のリレーショナル・データベースではサポートされていません。

リレーショナル・プロジェクトでオブジェクト・リレーショナル・ディスクリプタを使用すると、それらの専用オブジェクト・リレーショナル・データベース・データ・タイプへのオブジェクト・リレーショナル・マッピングを構成することができます(「オブジェクト・リレーショナル・マッピング・タイプ」を参照)。

詳細は、次を参照してください。

EISディスクリプタ

EISディスクリプタには、J2Cアダプタを介してEISデータ・ソースにマップするJavaオブジェクトを記述します。

TopLink Workbenchで作成したEISプロジェクトでEISディスクリプタを使用すると、XMLレコードへのEISのマッピング(「EISマッピングのタイプ」を参照)を構成できます。

Javaで作成したEISプロジェクトでEISディスクリプタを使用すると、サポートされている任意のEISレコード・タイプ(XML、マップ済、索引付き)へのEISのマッピングを構成できます。

詳細は、次を参照してください。

XMLディスクリプタ

XMLディスクリプタには、XMLスキーマ・ドキュメント(XSD)に定義されている単純および複合型に対してマップするJavaオブジェクトを記述します。

XMLディスクリプタをXMLプロジェクトで使用すると、XSDに定義されているXML要素へのXMLのマッピング(「XMLマッピングのタイプ」を参照)をメモリー内に構成できます。

詳細は、次を参照してください。

ディスクリプタと継承の概要

継承とは、導出されたクラスにそのスーパークラスの特性をどのように受け継がせるかを意味します。ディスクリプタを使用すると、リレーショナル・プロジェクト、EISプロジェクトおよびXMLプロジェクトにおいてクラス間の継承リレーションシップを定義できます。

図23-2に、Java継承階層の代表例としてVehicleというオブジェクト・モデルを示します。ルート・クラスのVehicleには、2つブランチ・クラスFueledVehicleおよびNonFueledVehicleが含まれています。各ブランチ・クラスには、それぞれCarおよびBicycleというリーフ・クラスが含まれます。

図23-2 継承階層の例

図23-2の説明が続きます
「図23-2 継承階層の例」の説明

TopLinkが継承階層で認識するクラスは、次の3つの種類です。

  1. ルート・クラスには、サブクラス階層内のインスタンス化可能なすべてのクラスに関する情報が格納されます。デフォルトでは、ルート・クラスに対して実行された問合せにより、ルート・クラスおよびインスタンス化可能なサブクラスのインスタンスが返されます。ただし、ルート・クラスの構成方法によっては、ルート・クラスに対する問合せ時にサブクラスのインスタンスを省いて、ルート・クラス自体のインスタンスのみを返すこともできます。

    たとえば、図23-2Vehicleクラスが、ルート・クラスです。

  2. ブランチ・クラスは、永続スーパークラスの他にサブクラスを持つものです。デフォルトでは、ブランチ・クラスに対して実行された問合せにより、ブランチ・クラスおよびそのサブクラスのインスタンスが返されます。ただし、ルート・クラスの場合と同様に、ブランチ・クラスの構成方法によっては、ブランチ・クラスに対する問合せ時にサブクラスのインスタンスを省いて、ブランチ・クラス自体のインスタンスのみを返すこともできます。

    たとえば、図23-2FueledVehicleクラスが、ブランチ・クラスです。

  3. リーフ・クラスには、階層内の永続スーパークラスがありますが、サブクラスはありません。リーフ・クラスに対して実行された問合せにより、リーフ・クラスのインスタンスのみが返されます。

    たとえば、図23-2Carクラスが、リーフ・クラスです。

子クラスのディスクリプタでは、親クラスのディスクリプタに指定されているマッピングをオーバーライドしたり、親クラスのディスクリプタにマップされていない属性をマップできます。

この項では、次の内容について説明します。

親(ルート)クラス・ディスクリプタに対する継承の構成方法の詳細は、「親(ルート)ディスクリプタに対する継承の構成」を参照してください。

子(ブランチまたはリーフ)クラス・ディスクリプタに対する継承の構成方法の詳細は、「子(ブランチまたはリーフ)クラス・ディスクリプタに対する継承の構成」を参照してください。

クラス・インジケータの指定

継承を構成する場合は、ルート・クラス・ディスクリプタに、インスタンス化先のサブクラスの選択の方法を構成します。

これは、次のいずれかの方法で行えます。


注意:

継承階層内のすべてのリーフ・クラスにはクラス・インジケータが必須で、かつこれらのリーフ・クラスは同じタイプのクラス・インジケータ(フィールドまたはクラス抽出メソッド)を持つ必要があります。

クラス・インジケータ・フィールドの使用

クラスの永続属性を使用すると、インスタンス化先のサブクラスを指定できます。たとえば、リレーショナル・ディスクリプタでは、ルート・クラス表のクラス・インジケータ・フィールドを使用できます。ただし、クラス・インジケータ・フィールドには、読取り専用に設定されていないダイレクト・マッピングを関連付けないでください。


注意:

クラス・インジケータ・フィールドが主キーの一部である場合は、このフィールドに対して書込み専用トランスフォーメーション・マッピングを定義します(第45章「リレーショナル・トランスフォーメーション・マッピングの構成」を参照)。

クラス・インジケータ・フィールドでは、値として文字列または数値を使用できます。

ルート・クラス・ディスクリプタによって、クラス・インジケータ・フィールドの値をインスタンス化されるクラスに変換する方法を指定する必要があります。

そのための方法の1つは、クラス・インジケータ・ディクショナリ(クラス・インジケータ・フィールドに格納される単純なキーを、インスタンス化するクラスに関連付けるキー値のコレクション)を使用して、ルート・クラス・ディスクリプタを構成することです。表23-3に、図23-2で示したVehicleクラスのサブクラスのクラス・インジケータ・ディクショナリを示します。

表23-3 Vehicleクラスのクラス・インジケータ・ディクショナリ

キー

F

FueledVehicle

N

NonFueledVehicle

C

Car

B

Bicycle


もう1つの方法は、単純にクラス名自体をクラス・インジケータ・フィールドへの値として格納することです。こうすると、クラス名の長さの範囲内で通常より長めのキー値を使用してまでクラスごとに一意のインジケータを定義することの必要性から解放されます。

クラス抽出メソッドの使用

オブジェクトのデータ・ソース・レコード内で利用可能なすべての情報に基づいて、クラス・インジケータを計算するJavaメソッドを定義することができます。このようなメソッドをクラス抽出メソッドといいます。

クラス抽出メソッドを使用すると、データ・モデルに明示的なクラス・インジケータ・フィールドを含める必要がなくなり、また、複雑すぎてクラス・インジケータ・フィールドには定義できないリレーションシップを処理できます。

クラス抽出メソッドには次の特性が必要です。

  • ルート・ディスクリプタのクラスをベースにして定義されていること

  • 静的であること

  • 引数としてRecordをとること

  • 入出力引数であるRecordjava.lang.Classオブジェクトを出力すること

また、必要に応じて、OnlyInstancesExpressionとWithAllSubclassesExpressionも定義します(「OnlyInstancesExpressionとWithAllSubclassesExpressionの指定」を参照)。

表23-4は、一例としてEMPLOYEEという表の行を示したものです。ベース・クラスはEmployeeクラスです。DirectorManagerProgrammerおよびTechWriterクラスはいずれもEmployeeクラスから導出されます。ただし、構築するアプリケーションでは、ManagerProgrammerおよびTechWriterクラスのインスタンスはEmployeeのインスタンスとして表現するものの、DirectorのインスタンスはDirectorのインスタンスとして表現する必要があります。クラスとJOB_TYPEフィールド値は1対1で対応していないため、JOB_TYPEフィールド単独ではクラス・インジケータ・フィールドとして機能しません(「クラス・インジケータ・フィールドの使用」を参照)。この問題を解決するには、例23-4に示すクラス抽出メソッドを使用できます。

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


例23-4 クラス抽出メソッド

...
// 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は生成されません。

OnlyInstancesExpressionとWithAllSubclassesExpressionの指定

クラス抽出メソッド(「クラス抽出メソッドの使用」を参照)を使用する場合は、共通の表を使用するすべてのクラスの兄弟インスタンスを正しくフィルタ処理するための式を、TopLinkに指定する必要があります(「親(ルート)クラス・ディスクリプタに関する継承式の構成」を参照)。

継承と主キー(リレーショナルおよびEISのみ)

リレーショナルおよびEISプロジェクトの場合、TopLinkでは、継承階層内のすべてのクラスが、ルート・ディスクリプタに設定されているのと同じ主キーを持っていることが前提とされます。複数の主キーを持つデータ・ソース表現に関連付けられた子ディスクリプタには、ルートの主キーとローカルの主キーとの間のマッピングを定義する必要があります。

単一表と複数表の継承(リレーショナルのみ)

リレーショナル・プロジェクトでは、継承階層を単一表(「単一表の継承」を参照)にも複数表(「複数表の継承」を参照)にもマッピングできます。この選択肢を使用することで、アプリケーションに適した形で、格納効率とアクセス効率の間でバランスをとることができます。

単一表の継承

この例では、複数レベルの継承を持つクラスを単一表に格納して、データベースのアクセス速度を最適化します。

図23-3に示すとおり、図23-2の継承階層全体で同一の表を共有できます。FueledVehicleの属性がNonFueledVehicleにない場合でも、FueledVehicleおよびNonFueledVehicleサブクラスは同じ表を共有できます。NonFueledVehicleのインスタンスはデータベース・リソースを消費します。これは、データベースがNonFueledVehicleの使用されない部分の行に対しても領域を割り当ててしまうためです。とはいえ、この方法を使用すると、その他のFueledVehicle情報を取得する際に別の表に結合する必要がなくなるため、アクセス時間が短縮されます。

図23-3に示したとおり、この方法ではクラス・インジケータ・フィールドを使用します。詳細は、「クラス・インジケータの指定」を参照してください。

図23-3 スーパークラス表とオプション・フィールドを使用した継承

図23-3の説明が続きます
「図23-3 スーパークラス表とオプション・フィールドを使用した継承」の説明

複数表の継承

この例では、複数レベルの継承を持つクラスを複数表に格納して、データベースのアクセス速度を最適化します。

図23-2に示した継承階層において、追加の属性を必要とするサブクラスについては、スーパークラスの単一表でなく複数表を使用します。これにより、データベース内で、実際には使用されないフィールドが存在することがなくなり、記憶域が最適化されます。ただし、TopLinkがオブジェクトをインスタンス化する場合には、複数表から読取りを行う必要があるため、パフォーマンスが低下します。TopLinkはまずクラス・インジケータ・フィールド(「クラス・インジケータの指定」を参照)を調べて、作成するオブジェクトのクラスを判別してから、そのクラスのディスクリプタを使用してサブクラスの表から属性を読み取ります。

図23-4は、TopLinkでのFUELEDVHCLCARおよびBICYCLE表の実装を示したものです。すべてのオブジェクトはVEHICLE表に格納されています。また、FueledVehicleCarBicycleの情報はセカンダリ表に格納されています。なお、NonFueledVehicleクラスには、属性もリレーションシップもないため、セカンダリ表は不要です。

図23-4 サブクラスごとに別個の表を使用した継承

図23-4の説明が続きます
「図23-4 サブクラスごとに別個の表を使用した継承」の説明


注意:

一般的に、複数表の継承の使用は、結合の頻度と複数表からのフェッチの頻度が高くなるため、非効率です。

継承ビュー

ルートまたはブランチの継承ディスクリプタに、複数表にわたるサブクラスがある場合は、データベース・ビューを構成してすべてのサブクラス表を外部結合することにより、親ディスクリプタに対する問合せのパフォーマンスを最適化することができます。これにより、TopLinkはすべてのサブクラス・インスタンスを複数回ではなく1回の問合せでフェッチできるようになります。また、親クラスに対してカーソルまたは順序付けを使用した問合せも実行できます。

データベースのビューは、すべてのサブクラス表を外部結合した1つのデータベース・ビューとして定義する必要があります。詳細は、「問合せでのサブクラス読取り機能の構成」を参照してください。

集約ディスクリプタ、コンポジット・ディスクリプタおよび継承

リレーショナル・ディスクリプタは集約ディスクリプタとして、EISディスクリプタはコンポジット・ディスクリプタとして指定できます。XMLディスクリプタは必ずコンポジット・ディスクリプタである必要があります(「ディスクリプタと集約」を参照)。

リレーショナル集約ディスクリプタで継承を構成する場合は、継承ツリー内のすべてのディスクリプタが集約である必要があります。つまり、集約クラスと非集約クラスのディスクリプタを同じ継承ツリーに置くことはできません。

同様に、EISコンポジット・ディスクリプタで継承を構成する場合も、継承ツリー内のすべてのディスクリプタはコンポジットである必要があります。つまり、コンポジット・クラスと非コンポジット・クラスのディスクリプタを同じ継承ツリーに置くことはできません。

XMLディスクリプタで継承を構成する場合は、すべてのXMLディスクリプタがコンポジットであるため、継承はディスクリプタ・タイプによる制限を受けません。

継承とEJB

継承はオブジェクト指向モデリングにおける標準的な手法ですが、3.0より前のEJB仕様には継承に関する一般的な情報しか規定されていません。この情報は、EJB継承を実装する前に十分に理解しておく必要があります。また、将来のEJB仕様では、すべてのアプリケーション・サーバーによってサポートされるとはかぎらない継承ガイドラインが規定される可能性もあることに留意してください。

ディスクリプタとロックの概要

この項では、TopLinkでサポートされている次のような様々なタイプのロック・ポリシーについて説明します。

詳細は、「ロック・ポリシーの構成」を参照してください。

オプティミスティック・バージョン・ロック・ポリシー

オプティミスティック・ロックを使用した場合、すべてのユーザーにデータへの読取りアクセス権限があります。ユーザーが変更を加えようとすると、アプリケーションにより、ユーザーがデータを読み取ってから、そのデータが変更されていないかが確認されます。

オプティミスティック・バージョン・ロック・ポリシーでは、バージョン・フィールド(書込みロック・フィールド)を使用して、オプティミスティック・ロックが実行されます。バージョン・フィールドは、参照クラス内に作成し、オブジェクト変更がコミットされるたびにTopLinkによって更新されます。

TopLinkは、データ・ソースからオブジェクトを読み取るときにこのバージョン・フィールドの値をキャッシュします。クライアントがオブジェクトに書き込もうとすると、TopLinkはキャッシュしたバージョン値とデータ・ソース内の最新のバージョン値を次のように比較します。

  • 2つの値が一致した場合は、オブジェクト内のバージョン・フィールドを更新し、データ・ソースに対する変更内容をコミットします。

  • 2つの値が一致しなかった場合は、そのクライアントが最初にオブジェクトを読み取った後に別のクライアントがそのオブジェクトを更新したことを意味するため、書込み操作を却下します。

TopLinkには、次のようなバージョン・ベースのオプティミスティック・ロック・ポリシーが用意されています。

  • VersionLockingPolicy: numericタイプのバージョン・フィールドが必要です。TopLinkは、値を1ずつ増分することによってこのバージョン・フィールドを更新します。

  • TimestampLockingPolicy: timestampタイプのバージョン・フィールドが必要です。TopLinkは、新しいタイムスタンプを挿入することによってこのバージョン・フィールドを更新します(このポリシーは、タイムスタンプをデータ・ソースまたはローカル時間から取得するように構成できます。デフォルトではデータ・ソースから取得されます)。


注意:

次の理由から、通常はnumericタイプのバージョンのロックをお薦めします。
  • データ・ソースからのタイムスタンプ取得により、パフォーマンスに問題が生じる場合があること

  • timestampタイプのバージョンのロックでは、データベースが格納するタイムスタンプによって精度に限界があること


オプティミスティック・ロック違反により更新が失敗すると、TopLinkではOptimisticLockExceptionがスローされます。この例外は、データベースの変更を行っているアプリケーションで処理する必要があります。アプリケーションは、ロックの競合をクライアントに通知し、オブジェクトをリフレッシュして、クライアントに変更内容の再適用を依頼する必要があります。

バージョン値は、オブジェクト内にマップ済属性として格納するか、キャッシュに格納するかを選択できます。3層アプリケーションでは、オブジェクトの更新時にバージョン値が確実にクライアントに渡されるようにするため、オブジェクト内にバージョン値を格納しておく方法が一般的です(「3層アプリケーションでのロック」を参照)。

バージョン値をキャッシュに格納する場合、バージョン値のマッピングは必要ありません。バージョン・フィールドをマッピングする場合は、マッピングを読取り専用として構成する必要があります(「読取り専用マッピングの構成」を参照)。

私有の子オブジェクトの変更時にその親オブジェクトのバージョン・フィールドが確実に更新されるようにするには、「オプティミスティック・バージョン・ロック・ポリシーとカスケード」を使用することを検討します。

作業ユニットによるオプティミスティック・バージョン・ロックを使用する場合は、「forceUpdateToVersionFieldによるオプティミスティック読取りロックの使用」を検討します。

オプティミスティック・バージョン・ロック・ポリシーとカスケード

使用するデータベース・スキーマが、親オブジェクトおよびその私有の子オブジェクトの両方を同じ表内に格納する設計になっている場合には、その子オブジェクトを更新すると、親オブジェクトのバージョン・フィールドが更新されます。

一方、親オブジェクトおよびその私有の子オブジェクトを異なる表に格納する場合は、デフォルトでは、子を変更しても、親のバージョン・フィールドは更新されません。

この場合に親オブジェクトのバージョン・フィールドを確実に更新するには、親オブジェクトのバージョン・フィールドを手動で更新します(「forceUpdateToVersionFieldによるオプティミスティック読取りロックの使用」を参照)。または、TimestampLockingPolicyポリシーを使用しているのであれば、子オブジェクトのバージョン・フィールドの更新が親に自動的にカスケードされるようにTopLinkを構成する方法もあります(「オプティミスティック・ロック・ポリシーのカスケードの構成」を参照)。

オプティミスティック・バージョン・ロックのカスケード機能を有効化した場合に、私有の子オブジェクトを変更すると、TopLinkはその私有されている外部参照マッピングを走査し、ルートに向かってすべての親オブジェクトを更新します。

オプティミスティック・バージョン・ロックのカスケードは、子オブジェクトが作業ユニット内に登録されている場合にのみ適用されます。

TopLinkでは、次の場合にオプティミスティック・バージョン・ロックのカスケードがサポートされます。

  • 私有の1対1および1対多マッピングでのオブジェクト変更

  • 次のコレクション・マッピング(私有かどうかは問わない)でのリレーションシップ変更(追加または削除)

    • ダイレクト・コレクション

    • 1対多

    • 多対多

    • 集約コレクション

図23-5に例示したオブジェクトの図について考えてみます。

図23-5 オプティミスティック・バージョン・ロック・ポリシーとカスケードの例

図23-5の説明が続きます
「図23-5 オプティミスティック・バージョン・ロック・ポリシーとカスケードの例」の説明

この例では、ObjectAObjectBを私有し、ObjectBObjectCを私有し、ObjectCObjectDを私有します。

ObjectBを作業ユニットに登録し、ObjectBを変更して、作業ユニットをコミットしたとします。この場合、ObjectBObjectAのキャッシュをチェックし、値が存在しない場合にはデータベースに対してObjectAを問い合せます。次に、ObjectBは自身の変更をObjectAに通知します。ObjectAは、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。

ObjectAを作業ユニットに登録して、ObjectAObjectBObjectCObjectDの順にアクセスし、ObjectDのフィールドを変更して、作業ユニットをコミットしたとします。この場合は、ObjectDが自身の変更をObjectCに通知します。ObjectCは、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。次に、ObjectCObjectDの変更をObjectBに通知します。次に、ObjectBObjectDの変更をObjectAに通知します。ObjectAは、自身に対応する表に変更がない場合でも、自身のバージョン・オプティミスティック・ロック・フィールドを更新します。

オプティミスティック・ロックとロールバック

オプティミスティック・ロックで、オプティミスティック・ロックのバージョンをキャッシュに格納する場合、ロックされたオブジェクトの値をロールバックするにはUnitOfWorkメソッドcommitAndResumeOnFailure「コミット後の作業ユニットの再開」を参照)を使用します。

ロックされた複数のバージョンを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属性のLNAMECREDIT_RATINGを選択した場合、TopLinkは実行時に次のようなSQLを生成します。

    UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name' AND CREDIT_RATING='A+'
    
    

オプティミスティック・ロック違反により更新が失敗すると、TopLinkではOptimisticLockExceptionがスローされます。この例外は、データベースの変更を行っているアプリケーションで処理する必要があります。アプリケーションは、ロックの競合をクライアントに通知し、オブジェクトをリフレッシュして、クライアントに変更内容の再適用を依頼する必要があります。

フィールド・ロック・ポリシーを使用する場合は、データ・ソースを更新するために作業ユニットを使用する必要があります。


注意:

AttributeChangeTrackingPolicy「属性変更追跡ポリシー」を参照)を使用している場合、FieldsLockingPolicyのインスタンスは使用できません。

ペシミスティック・ロック・ポリシー

ペシミスティック・ロックを使用した場合は、データを更新する目的でそのデータにアクセスする最初のユーザーが、更新を完了するまでデータをロックします。

ペシミスティック・ロック・ポリシーを使用する場合は、更新をただちに失敗させるか、読取りロックが取得されるまで待機させるようにポリシーを構成できます。

ペシミスティック・ロック・ポリシーを使用できるのは、コンテナ管理の永続性タイプ(「永続性タイプの構成」を参照)を指定し、EJB情報を持つディスクリプタ(「EJB情報によるディスクリプタの構成」を参照)を使用したプロジェクトのみです。

また、問合せレベルでペシミスティック・ロック機能(ペシミスティック・ロック・ポリシーではない)を使用することもできます(「名前付き問合せのオプションの構成」を参照)。

コンテナ管理の永続性を備えたエンティティBeanでペシミスティック・ロックが使用される場合、TopLinkではこのロックが最適化されます。問合せをペシミスティック・ロックに設定し、その問合せを独自の新規トランザクション(ファインダの実行後に終了するトランザクション)内で実行すると、TopLinkによりロック設定が上書きされ、SQLにFOR UPDATEは追加されません。ただし、ペシミスティック・ロック問合せがFOR UPDATEを含むSQL文字列でユーザーによりカスタマイズされている場合、この最適化の使用により不適切な結果が生じることがあります。この場合、最適化の条件が存在すると、問合せはペシミスティック・ロックを使用しないようリセットされますが、SQLはそのままの状態で残るため、問合せのロック設定が問合せのSQL文字列と競合することになります。この問題を回避するには、次のいずれかの方法を使用します。

  • 選択基準としてEJB QLまたはTopLinkの式(第95章「TopLinkの式の概要」を参照)を使用します。これにより、TopLinkでSQLの生成を制御します。

  • 最適化の条件をなくすため、ファインダをトランザクション内に配置します。

3層アプリケーションでのロック

3層アプリケーションを構築する場合は、オブジェクトが正しくロックされるようにするため、そのオブジェクトが編集用にクライアントに送信される前にロックを取得できるようにする必要があります。

3層アプリケーションでのオプティミスティック・ロック

オプティミスティック・ロックを使用する場合、オブジェクトを正しくロックするには次の2つの選択肢があります。

  1. オブジェクト内のオプティミスティック・ロック・フィールドを読取り専用以外にマップし、読取り時にバージョンをクライアントに渡し、更新時にサーバーに戻します。

    バージョン・フィールドに対して読取り専用以外のマッピングを定義し、バージョン値がキャッシュではなくオブジェクト内に格納されるようにオプティミスティック・ロック・ポリシーを設定する必要があります。これは、TopLink Workbenchで「ロック」タブの「キャッシュにバージョンを保存」の選択を解除することで設定できます(「TopLink Workbenchの使用」を参照)。

    クライアントがオブジェクトを更新目的で読み取るときに、元のバージョン値がクライアントに確実に送信されるようにします。次に、クライアントが、元のバージョン値を更新情報とともに戻す必要があります。また、このバージョンは、サーバー上の新しい作業ユニット内に登録されたかまたは読み込まれた後に、更新対象のオブジェクト内に設定される必要があります。

  2. クライアントとの対話が完了するまで作業ユニットを保持します。

    ステートフル・セッションBeanを介して、またはHTTPセッションにおいて、更新対象オブジェクトの読取りに使用する作業ユニットを、クライアントとの対話が完了するまで保管します。

    オブジェクトをクライアントに更新用に渡す前に、この作業ユニットからオブジェクトを読み取っておく必要があります。これにより、作業ユニットのキャッシュまたは作業ユニットのクローンに元のバージョン値を保管しておけます。

    この同じ作業ユニットを更新でも必ず使用します。

1番目の選択肢の方が一般的に使用され、ステートレス・アプリケーションを開発する場合には必須となります。

3層アプリケーションでのペシミスティック・ロック

ペシミスティック・ロックを使用する場合は、作業ユニットを使用して、オブジェクトが読み取られる前にデータベース・トランザクションを開始する必要があります。この作業ユニットとデータベース・トランザクションは、クライアントがオブジェクトを編集して更新するまでの間、保持する必要があります。この同じ作業ユニットをオブジェクトの更新でも必ず使用します。(通常クライアントとの対話中はデータベース・トランザクションをオープンにしておくことは望ましくないとされている)3層Webアプリケーションを構築する場合は、一般的にはペシミスティック・ロックよりもオプティミスティック・ロックの方が適しています(「3層アプリケーションでのオプティミスティック・ロック」を参照)。

ディスクリプタAPIの概要

ディスクリプタAPIを使用すると、Javaコードを介してTopLinkディスクリプタを定義または修正することができます。ディスクリプタAPIのクラスは、主としてoracle.toplink.descriptorsパッケージに含まれています。このようなクラスには次のものがあります。

オブジェクト・リレーショナル、EISおよびXMLプロジェクト用のディスクリプタ・クラスは、それぞれoracle.toplink.objectrelationaloracle.toplink.eisおよびoracle.toplink.oxパッケージに含まれています。

この項では、Oracle TopLink Foundation Libraryの重要なディスクリプタ・クラスについて説明します。次の項にそのようなクラスが記載されています。

ディスクリプタの継承階層

例23-5は、クラスoracle.toplink.descriptors.ClassDescriptorから導出されたディスクリプタ・タイプを示したものです。

例23-5 ディスクリプタの継承階層

class oracle.toplink.descriptors.ClassDescriptor
    class oracle.toplink.descriptors.RelationalDescriptor
        class oracle.toplink.objectrelational.ObjectRelationalDescriptor
    class oracle.toplink.eis.EISDescriptor
    class oracle.toplink.ox.XMLDescriptor