Oracle® Fusion Middleware Oracle Application Development FrameworkによるFusion Webアプリケーションの開発 12c (12.2.1.2.0) E82918-03 |
|
前 |
次 |
この章の内容は次のとおりです。
ADFエンティティ・オブジェクトを使用してデータソースの行を表します。ADFエンティティ・オブジェクトを使用すると、行の属性の変更、ドメイン・ビジネス・ロジックのカプセル化、ビジネス・ポリシーおよびルールの実装ができます。
エンティティ・オブジェクトは、指定したデータソース内の行を表し(一般に単一のデータベース表、ビューまたはシノニム)、関連付けられた属性の変更を簡略化するためのADFビジネス・コンポーネントです。重要なことは、エンティティ・オブジェクトを使用すると、ドメイン・ビジネス・ロジックをカプセル化できるため、ビジネス・ポリシーおよびビジネス・ルールを一貫性のある方法で検証できるということです。
エンティティ・オブジェクトは、データの有効性を強化するために様々な宣言的ビジネス・ロジック機能をサポートしています。通常、追加のカスタム・アプリケーション・ロジックおよびビジネス・ルールで宣言的な検証に補完することにより、各エンティティ・オブジェクトに最大の量のドメイン・ビジネス・ロジックを効率的にカプセル化します。関連付けられた一連のエンティティ・オブジェクトによって、複数のアプリケーションで活用できる再利用可能なビジネス・ドメインが形成されます。
エンティティ・オブジェクト(図4-1に示します)の主要な概念は次のとおりです。
エンティティ・オブジェクトを定義するには、エンティティ・オブジェクトが示す行が含まれるデータベース表を指定します。
エンティティ・オブジェクト間の関連を反映するアソシエーションを作成できます。
実行時には、エンティティ行はエンティティ定義オブジェクトによって管理されます。
各エンティティ行は、関連する行キーによって識別されます。
エンティティ行は、データベース・トランザクションを提供するアプリケーション・モジュールのコンテキストで取得および変更します。
図4-1 エンティティ・オブジェクトによる表のビジネス・ロジックのカプセル化
エンティティ・オブジェクトで作業を開始する前に、その他のOracle ADF機能を理解しておくと役に立つ場合があります。次に、関連する他の機能へのリンクを示します。
エンティティ・オブジェクトでの宣言的な検証を使用する方法の詳細は、「検証とビジネス・ルールの宣言的な定義」を参照してください。
oracle.jbo
パッケージに関連するAPIのドキュメントについては、次のJavadocリファレンス・ドキュメントを参照してください。
Oracle ADFモデルJava APIリファレンス
既存の表からADFエンティティ・オブジェクトおよびアソシエーションを作成します。また、最初からADFエンティティ・オブジェクトを作成して表を生成することもできます。
すでに使用しているデータベース・スキーマがある場合、エンティティ・オブジェクトおよびアソシエーションを作成する最も簡単な方法は、これらを既存の表からリバース・エンジニアリングする方法です。必要な場合には、エンティティ・オブジェクトを最初から作成し、後でこのエンティティ・オブジェクト用の表を生成することもできます。
1つ以上のエンティティ・オブジェクトを作成するには、「新規ギャラリ」で使用できる「表からのビジネス・コンポーネント」ウィザードを使用します。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
既存の表から1つ以上のエンティティ・オブジェクトおよびアソシエーションを作成するには:
「アプリケーション」ウィンドウに指定したパッケージのエンティティ・オブジェクトが表示されます。
ベスト・プラクティス:
アソシエーションを作成したら、エンティティ・オブジェクトとは別に表示および管理できるように、すべてのアソシエーションを別のパッケージに移動します。図4-3では、アソシエーションはサブパッケージ(assoc
)に移動されているため、「アプリケーション」ウィンドウのentities
パッケージには表示されません。詳細は、「アソシエーションの名前変更および別のパッケージへの移動方法」を参照してください。
図4-3 「アプリケーション」ウィンドウの新規エンティティ・オブジェクト
単一のエンティティ・オブジェクトを作成するには、「新規ギャラリ」から使用可能なエンティティ・オブジェクト作成ウィザードを使用します。
注意:
エンティティ・オブジェクト作成ウィザードを使用すると、アソシエーションは生成されません。ただし、「表からのビジネス・コンポーネント」ウィザードを使用すると、アソシエーションは生成されますエンティティ・オブジェクトの作成ウィザードを使用して、エンティティ・オブジェクトを作成する場合、対応するアソシエーションを手動で作成する必要があります。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
単一のエンティティ・オブジェクトを作成するには:
既存の表からエンティティ・オブジェクトを作成する場合、最初に、次の情報を推測するためにデータ・ディクショナリからデータが取得されます。
表の列名に基づくJavaに適したエンティティ属性名(USER_ID
→UserId
など)
基礎となる列のデータ型に基づく各属性のSQLおよびJavaデータ型
各属性の長さと精度
主キーと一意キーの属性
NOT NULL
制約に基づく属性の必須フラグ
外部キーの制約に基づく新規エンティティ・オブジェクトとその他のエンティティ間の関連
注意:
エンティティ・オブジェクトはデータベース行を表しているため、エンティティ行と呼ぶのが自然です。または、エンティティ行は実行時にそのデータベース行のビジネス・ロジックをカプセル化するJavaオブジェクト・インスタンスであるため、エンティティ・インスタンスという、よりオブジェクト指向の呼び方も適しています。このため、これらの2つの用語は同義です。
JDeveloperは、宣言的設定を表すXMLドキュメント・ファイルを作成し、パッケージ名と対応するディレクトリに保存します。たとえば、Order
という名前のエンティティがgenericbcmodel.entities
パッケージに含まれる場合、JDeveloperによりそのプロジェクトのソース・パスにgenericbcmodel/entities/Order.xml
というXMLファイルが作成されます。このXMLファイルには、表の名前、各エンティティ属性の名前とデータ型、および各属性の列名が含まれます。
概要エディタでオブジェクトを開き、「ソース」タブをクリックすると、エンティティ・オブジェクトのXML記述内容を検証できます。
注意:
IDEレベルのビジネス・コンポーネントのJava生成設定で指定されている場合、オプションのカスタム・エンティティ・オブジェクト・クラス(OrderImpl.java
など)もウィザードで生成される場合があります。
エンティティ・オブジェクト以外に、エンティティ・オブジェクト間の関連に関する情報を取得する名前付きアソシエーション・コンポーネントも「表からのビジネス・コンポーネント」ウィザードにより生成されます。たとえば、図4-4のデータベース・ダイアグラムを参照すると、外部キー制約名をJavaに適した名前に変換してAssoc
接尾辞を付けることにより、SItemOrdIdFkAssoc
などのデフォルトのアソシエーション名がJDeveloperにより導出されることを確認できます。作成されたアソシエーションごとに、適切なXMLドキュメント・ファイルが作成され、パッケージ名と対応するディレクトリに保存されます。
注意:
「表からのビジネス・コンポーネント」ウィザードを使用すると、アソシエーションは生成されます。ただし、エンティティ・オブジェクト作成ウィザードでは、アソシエーションは生成されません。エンティティ・オブジェクトの作成ウィザードを使用して、エンティティ・オブジェクトを作成する場合、対応するアソシエーションを手動で作成する必要があります。
デフォルトでは、外部キーからリバース・エンジニアリングされたアソシエーションは、エンティティと同じパッケージ内に作成されます。たとえば、summit.model.entities
パッケージ内にあるエンティティへのアソシエーションSItemOrdIdFkAssoc
に対しては、JDeveloperにより../summit/model/entities/SItemOrdIdFkAssoc.xml
という名前のアソシエーションXMLファイルが作成されます。
図4-4 外部キーによって関連付けられたS_ITEM表およびS_ORD表
表に主キー制約がない場合、JDeveloperは、エンティティ・オブジェクトの主キーを推測できません。すべてのエンティティ・オブジェクトには少なくとも1つの属性が主キーとしてマークされている必要があるため、ウィザードでは、エンティティの主キーの一部としてすべての列がマークされています。必要な場合、後でエンティティ・オブジェクトを編集し、別の属性を主キーとしてマークし、その他の属性の設定を削除できます。エンティティ・オブジェクト作成ウィザードを使用する場合、その他の属性を主キーとして設定していないと、すべての列を主キーとして使用するよう求められます。
「表からのビジネス・コンポーネント」ウィザードまたはエンティティ・オブジェクトの作成ウィザードを使用してエンティティ・オブジェクトを作成する場合、オブジェクトは基礎となる表、シノニム、またはビューを表すことができます。フレームワークは、データ・ディクショナリ内のデータベースの主キーおよび外部キー制約を調べることにより、主キーおよび関連するアソシエーションを推測できます。
ただし、選択したスキーマ・オブジェクトがデータベース・ビューである場合、データベース・ビューにはデータベース制約がないため、主キーもアソシエーションも推測できません。この場合、「表からのビジネス・コンポーネント」ウィザードを使用すると、主キーはRowID
にデフォルト設定されます。エンティティ・オブジェクト作成ウィザードを使用する場合、少なくとも1つの属性を主キーとしてマークすることにより、主キーを手動で指定する必要があります。詳細は、「表に主キーがないときに生成される行識別子」を参照してください。
選択したスキーマ・オブジェクトがシノニムである場合、結果には2種類あります。シノニムが表のシノニムである場合、ウィザードおよびエディタは、表を指定した場合と同じように動作します。かわりに、シノニムがデータベース・ビューを参照している場合、ウィザードおよびエディタは、ビューを指定した場合と同じように動作します。
新しいエンティティ・オブジェクトまたはアソシエーションを作成した後、概要エディタでその設定を編集できます。エディタを起動するには、「アプリケーション」のエンティティ・オブジェクトまたはアソシエーションのポップアップ・メニューで「開く」を選択するか、オブジェクトをダブルクリックします。エディタの別のタブをクリックすることにより、オブジェクトを定義する設定や、その実行時の動作を制御する設定を調整できます。
エンティティ・オブジェクトに基づいてデータベース表を作成するには、エンティティ・オブジェクトが含まれる「アプリケーション」ウィンドウでパッケージを右クリックし、ポップアップ・メニューから「データベース・オブジェクトの作成」を選択します。ダイアログが表示され、表の作成元のエンティティを選択できます。このツールを使用して、作成したエンティティ・オブジェクトの表を最初から生成することも、既存の表をドロップして再作成することもできます。
注意:
データベースに対して操作が直接実行され、既存の表がドロップされます。この機能では、後で実行するスクリプトは生成されません。作業に進む前に、この処理を実行するかどうかを確認するダイアログが表示されます。既存の表に基づくエンティティの場合、慎重に作業してください。
アソシエーションの概要エディタの「アソシエーション・プロパティ」ページにある「データベース・キー制約の使用」チェック・ボックスにより、エンティティ・オブジェクトの表の作成時に、関連する外部キー制約を生成するかどうかを制御します。このオプションを選択しても、実行時には影響しません。
すでにエンティティ・オブジェクトを作成した表も、自分で(またはDBAが)変更する必要がある場合があります。基礎となる表に属性が追加されても既存のエンティティに混乱が生じることはありませんが、Java EEアプリケーションで表内の新しい列にアクセスするには、エンティティ・オブジェクトをデータベース表と同期化する必要があります。
たとえば、SQL*Plusコマンド・プロンプトで次を実行し、新しいSECURITY_QUESTION
列をPERSONS
表に追加したとします。
ALTER TABLE PERSONS ADD (security_question VARCHAR2(60));
同期化機能を使用すると、エンティティ・オブジェクトに新規列を属性として追加できます。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
データベース表の変更とエンティティを同期化するには:
同期化機能では、ドロップされた列は処理されません。エンティティ・オブジェクトの作成後に基礎となるデータベースから列がドロップされた場合は、対応する属性をエンティティ・オブジェクトから削除できます。属性がアプリケーションの他の部分で使用される場合は、それらの使用方法も削除する必要があります。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ属性を削除するには:
同期化機能では、変更されたデータ型は処理されません。基礎となる表でのデータ型の変更(精度の向上など)では、属性のすべての使用方法を特定して、必要に応じて手動で変更する必要があります。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ属性のすべての使用方法を特定するには:
特定の時点に関するデータセットを表示する場合は、有効日が指定された表が使用されます。有効日が指定された表は、次のような問合せを処理するHRMSやPayrollなどのアプリケーションで広く使用されています。
2005年8月31日現在の従業員への課税率はどの程度か。
2004年10月現在の従業員の給料はどの程度か。
この2つのいずれかについて、従業員のデータがそれ以降変更されている場合があります。
有効日が指定されたエンティティ・タイプと日付が指定されたエンティティ・タイプの主な違いは、日付が指定されたエンティティ・タイプでは、更新および削除中に行が分割されないことです。
注意:
マスター/ディテール関係の親エンティティ・オブジェクトで有効日を使用しないでください。子オブジェクトは親オブジェクトに対するイベントを呼び出すことができ、有効日が指定されたエンティティ・オブジェクトは更新および削除操作中に行が分割されるため、親と子が同じトランザクション中に更新されると、子オブジェクトは間違った親オブジェクトに対するイベントを呼び出す可能性があります。
有効日が指定されたエンティティ・オブジェクトを作成する場合は、エンティティを有効日指定として識別し、開始日と終了日を表すエンティティの属性を指定します。開始日と終了日の属性はDate型にする必要があります。
また、有効日が指定されたエンティティの順序を表す属性や、順序のフラグを表す属性を指定することもできます。これらの属性では、1日に行われた複数の変更を追跡できます。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
有効日が指定されたエンティティ・オブジェクトを作成するには
注意:
「プロパティ」ウィンドウを使用して、該当の属性に開始日と終了日の属性を指定することもできます。この場合は、概要エディタで該当の属性を選択し、「プロパティ」ウィンドウで「開始日」プロパティまたは「終了日」プロパティを「true」に設定します。
有効日が指定されたエンティティ・オブジェクトを作成すると、SysEffectiveDate
と呼ばれる一時属性が作成され、行の有効日が格納されます。通常は、挿入、更新および削除の操作によって一時属性が変更されますが、ADFビジネス・コンポーネント・フレームワークでは、「有効日開始」と「有効日終了」で適切な値を決定します。
次の例は、有効日が指定されたエンティティを作成したときに生成されるサンプルのXMLエントリの一部を示しています。有効日が指定されたオブジェクトの使用方法の詳細は、「有効日付範囲を使用したビュー・オブジェクト行の制限」を参照してください。
// In the effective dated entity <Entity ... EffectiveDateType="EffectiveDated"> // In the attribute identified as the start date <Attribute ... IsEffectiveStartDate="true"> // In the attribute identified as the end date <Attribute ... IsEffectiveEndDate="true"> // The SysEffectiveDate transient attribute <Attribute Name="SysEffectiveDate" IsQueriable="false" IsPersistent="false" ColumnName="$none$" Type="oracle.jbo.domain.Date" ColumnType="$none$" SQLType="DATE"/>
「表からのビジネス・コンポーネント」ウィザードを使用すると、同時に多くのビジネス・コンポーネントを簡単に作成できます。とはいえ、単にこの処理が可能だという理由でこのウィザードを使用して、データベース・スキーマ内のすべての表についてエンティティ・オブジェクトを即座に作成する必要はありません。アプリケーションですべての表が必要な場合は、この方法が適しています。ただし、このウィザードは必要であればいつでも使用できるため、アプリケーションに必要なことがわかっている表についてエンティティ・オブジェクトを作成することをお薦めします。
「ネストされたアプリケーション・モジュールの定義」では、ビジネス・サービスのユースケース駆動型設計方法について説明されており、アプリケーションのビジネス・ロジックのニーズをサポートするために必要なエンティティ・オブジェクトを理解する上で役立ちます。エンティティ・オブジェクトは、必要に応じて後でいつでも追加できます。
JDeveloperでADFエンティティ・オブジェクト間のアソシエーションを推測できるように、外部キー制約が存在しない場合は、エンティティ・アソシエーションを手動で作成します。
データベース表に外部キー制約が定義されていない場合、作成されたエンティティ・オブジェクト間のアソシエーションはJDeveloperにより推測されません。一部のADFビジネス・コンポーネント・ランタイム機能はエンティティ・アソシエーションが存在しているかどうかに依存するため、外部キーが存在しない場合は、エンティティ・アソシエーションを手動で作成することをお薦めします。
アソシエーションを作成するには、「新規ギャラリ」から使用できる新規アソシエーション作成ウィザードを使用します。
始める前に:
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、「アソシエーションの作成と構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
アソシエーションを作成するには、次のようにします。
アソシエーションを作成すると、適切なXMLドキュメント・ファイルが作成され、パッケージ名と対応するディレクトリに保存されます。たとえば、oracle.summit.model.entities.assoc
サブパッケージ内にSItemOrderIdFkAssoc
という名前のアソシエーションを作成した場合、アソシエーションXMLファイルがSItemOrderIdFkAssoc.xml
という名前で../oracle/summit/model/entities/assoc
ディレクトリに作成されます。実行時には、エンティティ・オブジェクトはこのアソシエーション情報を使用して、一連の関連エンティティの操作を自動化します。
「アソシエーション・プロパティ」ページのアクセッサ名のデフォルト設定について検討し、これらの名前をより直感的なものに変更した方が適切かどうかを決定する必要があります。このデフォルト設定により、実行時に関連の反対側のエンティティにプログラム的にアクセスするときに使用するアクセッサ属性の名前が定義されます。デフォルトでは、これらのアクセッサ名は、反対側のエンティティ・オブジェクトの名前になります。エンティティのアクセッサ名は、エンティティ・オブジェクト属性とその他のアクセッサ間で一意である必要があるため、1つのエンティティが複数の方法で別のエンティティに関連付けられている場合、デフォルトのアクセッサ名は、名前を一意にするために数値接尾辞を使用して変更されます。
既存のアソシエーションでは、「アソシエーション・プロパティ」ダイアログを使用してアクセッサの名前を変更できます。
始める前に:
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、「アソシエーションの作成と構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
アソシエーション内のエンティティ・アクセッサの名前を変更するには:
アソシエーションは、通常はプロジェクトの最初に構成し、それ以降はそれほど頻繁に変更しないコンポーネントであるため、アソシエーションを別のパッケージに移動し、エンティティ・オブジェクトを見やすくできます。
コンポーネントの名前変更と別のパッケージへの移動は両方とも、JDeveloperのリファクタ機能を使用して直接実行します。ただし、参照が失われるとアプリケーションが破壊されることがあるため、ADFビジネス・コンポーネント・オブジェクトの手動での名前の変更や移動はお薦めしません。JDeveloperのリファクタ機能の詳細は、「Fusion Webアプリケーションのリファクタ」を参照してください。
始める前に:
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、「アソシエーションの作成と構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
別のパッケージにビジネス・コンポーネントのセットを移動するには:
コンポーネント名を変更する場合は、名前を変更するコンポーネントを右クリックし、「リファクタ」→「名前の変更」を選択します。ADFビジネス・コンポーネントをリファクタする場合、コンポーネントに関連するXMLファイルおよびJavaファイルがJDeveloperにより移動されるとともに、これらを参照するその他のコンポーネントが更新されます。
図4-7は、すべてのアソシエーションの名前を変更し、これらをoracle.summit.model.entities.assoc
サブパッケージに移動した後の「アプリケーション」ウィンドウの様子を示しています。任意のパッケージ名を選択してアソシエーションをリファクタすることにより、サブパッケージを使用してアソシエーションとエンティティとの論理的な関連を維持しながらアソシエーションのパッケージを閉じることができるため、「アプリケーション」ウィンドウに表示するファイルをより効率的に管理できます。
図4-7 アソシエーションのリファクタ後の「アプリケーション」ウィンドウ
カスタム・ビュー・オブジェクトをエンティティ・アソシエーションのリンク元またはリンク先(あるいは両方)に関連付けることができます。
コード内でエンティティ・アソシエーションをトラバースするときに、エンティティがキャッシュにない場合は、ADFビジネス・コンポーネント・フレームワークによって問合せが実行され、エンティティ(複数可)がキャッシュに取り込まれます。デフォルトでは、エンティティをキャッシュに取り込むために実行される問合せは、主キーによる検索の問合せで、基礎となる表からすべての永続エンティティ属性の値を選択します。アプリケーションでプログラム的なエンティティ・アソシエーションのトラバースが多く実行される場合、すべての属性を取得することは、無理なユースケースになる場合があります。
エンティティ・アソシエーションでは、カスタムのエンティティ・ベースのビュー・オブジェクトをアソシエーション内のソース・エンティティまたは関連先エンティティ(あるいは両方)に関連付けることができます。指定するエンティティ・ベースのビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名は、それを使用するアソシエーションの端のエンティティ・タイプに一致する必要があります。
カスタム・ビュー・オブジェクトの問合せには少ない列を含めることが可能で、ORDER BY
句を含めることができるため、カスタム・ビュー・オブジェクトを使用する方が便利な場合があります。これにより、アソシエーションのトラバースによりエンティティがキャッシュに取り込まれる際に取得されるデータの量だけでなく、関連するエンティティのコレクションの順序も制御できます。
カスタム・ビュー・オブジェクトの作成の詳細は、「プログラムでエンティティ・ベースのビュー・オブジェクトを作成する方法」を参照してください。
アソシエーションは、Order
が参照するCustomer
や、Order
に含まれるItem
などのエンティティ間の関連を表しています。アソシエーションを作成する場合、表現可能な関連の種類や様々なオプションについて理解していると役に立ちます。
エンティティ・オブジェクト間の関連を使用して、次のようなソース・エンティティの状態に応じて2つのスタイルの関連を表現できます。
関連先エンティティを参照している
ネストされた論理的部分として関連先エンティティが含まれる
図4-8は、この2つのスタイルの関連を表すアプリケーション・ビジネス・レイヤーを示しています。たとえば、OrdEO
エントリはCustomerEO
を参照しています。これらの関連は、第一種のアソシエーションを示し、CustomerEO
またはOrdEO
エンティティ・オブジェクトは互いに独立して存在できます。また、Order
を削除しても、これが参照していたCustomer
がカスケード削除されることはありません。
一方、OrdEO
と関連するItemEO
ディテールのコレクション間の関係は、単純な参照よりも強力です。ItemEO
エントリは、OrdEO
全体の論理部分を構成しています。つまり、OrdEO
はItemEO
エントリで構成されています。ItemEO
エンティティ行にとって、OrdEO
から独立して存在することは意味をなさず、(削除が許可されていて)OrdEO
を削除する場合には、その構成部分もすべて削除する必要があります。このタイプの論理関係は、コンポジットと呼ばれる第二種のアソシエーションを示します。図4-8のUMLダイアグラムでは、アソシエーションで他方を構成する側に黒い菱形を使用して、より強力なコンポジット関連を示しています。
図4-8 ItemEOエントリで構成され、CustomerEOとPaymentTypeEOの両方を参照するOrdEO
「表からのビジネス・コンポーネント」ウィザードでは、ON DELETE CASCADE
オプションを持つ外部キーに対してデフォルトでコンポジット・アソシエーションが作成されます。アソシエーション作成ウィザードまたはアソシエーションの概要エディタを使用すると、アソシエーションをコンポジット・アソシエーションとして指定できます。「アソシエーションの作成」ウィザードの「アソシエーション・プロパティ」ページ、または概要エディタの「関連」ページで、「コンポジット・アソシエーション」チェック・ボックスを選択します。
注意:
コンポジット・アソシエーションは、一時属性をベースに作成できません。
コンポジット・アソシエーションがある場合、エンティティ・オブジェクトには実行時に別の動作が追加されます。動作の制御設定については、「コンポジット動作の構成方法」を参照してください。
JDeveloperを使用してADFビジネス・コンポーネントに基づいたビジネス・サービスのUMLダイアグラムを作成できます。UMLダイアグラムは、視覚化、視覚的なナビゲーションおよび編集用のツールとして機能します。
ビジネス・ドメイン・オブジェクトのレイヤーはチームにとって再使用可能な主要資産となるため、多くの場合、UMLモデルを使用して視覚化すると使いやすくなります。JDeveloperでは、自分やチームのメンバーが参照用として使用できるビジネス・ドメイン・レイヤーのダイアグラムを簡単に作成できるようサポートされています。
ビジネス・コンポーネントのUMLダイアグラムは、エンティティ・オブジェクトをダイアグラムにドロップしたときを示す静的な図のみではありません。むしろ、UMLダイアグラムは、現在のコンポーネント定義をUMLベースでレンダリングした図であるため、常に現在の状況を示しています。さらに、UMLダイアグラムは、視覚的なサポートとナビゲーションを提供するツールであるとともに、編集用のツールでもあります。ダイアグラムでエンティティ・オブジェクトについて概要エディタを開くには、目的のオブジェクトを右クリックして「開く」を選択します。また、エンティティ・オブジェクトおよびエンティティ属性の名前変更や属性の追加または削除など、エンティティ・オブジェクトの編集タスクをダイアグラム上で直接実行することもできます。ただし、ビジネス・コンポーネント・ダイアグラムでのエンティティ・オブジェクトへの変更は、基礎となるデータベース・オブジェクトには影響しません。
エンティティ・オブジェクトのダイアグラムを作成するには、「新規ギャラリ」から使用可能な「ビジネス・コンポーネント・ダイアグラムの作成」ダイアログを使用できます。
始める前に:
エンティティ・ダイアグラムがアプリケーションで使用される方法に関する知識が役立つ場合があります。詳細は、「ビジネス・レイヤーのエンティティ・オブジェクトの図の作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
既存のエンティティ・オブジェクトをモデル化するビジネス・コンポーネント・ダイアグラムを作成する手順
ダイアグラムを作成したら、「プロパティ」ウィンドウを使用してダイアグラムのビジュアル・プロパティを調整できます。たとえば、次の操作が可能です。
パッケージ名の表示と非表示の切替え
フォントの変更
グリッドおよび改ページの切替え
アソシエーション名の表示(表示しないと曖昧になる場合)
ダイアグラム上でポップアップ・メニューから「ダイアグラムの公開」を選択すると、ダイアグラムのイメージをPNG
、JPG
、SVG
または圧縮されたSVG
書式で作成することもできます。
図4-9は、ビジネス・ドメイン・レイヤーの様々なエンティティ・オブジェクトをモデル化したサンプル・ダイアグラムを示しています。
図4-9 ビジネス・ドメイン・レイヤーのUMLダイアグラム
ビジネス・コンポーネント・ダイアグラムを作成すると、ダイアグラムが格納されているパッケージ名と一致するプロジェクトのモデル・パスのサブディレクトリに、ダイアグラムを示すXMLファイル*.adfbc_diagram
が作成されます。
デフォルトでは、「アプリケーション」ウィンドウによってプロジェクト・コンテンツ・パスの表示が統一され、ソース・パスのADFコンポーネントおよびJavaファイルがプロジェクト・モデル・パスのUMLモデル・アーティファクトと同じパッケージ・ツリーに表示されます。ただし、図4-10のように、「アプリケーション」ウィンドウの「アプリケーション・ウィンドウのオプション」ボタンを使用すると、必要に応じて、プロジェクト・コンテンツ・パスのルート・ディレクトリを個別に表示できます。
図4-10 個々のコンテンツ・パス・ディレクトリの表示の切替え
エンティティ・オブジェクトなどのビジネス・コンポーネントをUMLダイアグラムに含めると、次の例のように、コンポーネントのXMLコンポーネント・ディスクリプタの<Data>
セクションにメタデータが追加されます。この追加情報が使用されるのは設計時のみです。
<Entity Name="OrderEO" ... > <Data> <Property Name ="COMPLETE_LIBRARY" Value ="FALSE" /> <Property Name ="ID" Value ="ff16fca0-0109-1000-80f2-8d9081ce706f::::EntityObject" /> <Property Name ="IS_ABSTRACT" Value ="FALSE" /> <Property Name ="IS_ACTIVE" Value ="FALSE" /> <Property Name ="IS_LEAF" Value ="FALSE" /> <Property Name ="IS_ROOT" Value ="FALSE" /> <Property Name ="VISIBILITY" Value ="PUBLIC" /> </Data> : </Entity>
ADFプロパティ・セットを定義し、コントロール・ヒントおよびエラー・メッセージとして使用します。
プロパティ・セットとはプロパティの名前付きコレクションで、各プロパティが名前/値のペアとして定義されています。プロパティ・セットは、プロパティをグループ化し、それらを他のADFビジネス・コンポーネント・オブジェクトから参照するための便利なメカニズムです。プロパティ・セットは、エンティティ・オブジェクトとその属性、ビュー・オブジェクトとその属性、およびアプリケーション・モジュールで使用できます。
プロパティ・セットは、コントロール・ヒントやエラー・メッセージなど、様々な機能に使用できます。各プロパティ・セットには、コントロール・ヒントなどのカスタム・プロパティを含めることができ、それらを別のオブジェクトの複数の属性に関連付けることもできます。
プロパティ・セット内で定義されたプロパティは、翻訳可能に構成でき、そのようなケースでは、翻訳されたプロパティはプロパティ・セットが所有するメッセージ・バンドル・ファイルに格納されます。プロパティセットはスキーマ・ドリブンであるため、名前の追加以外に説明も追加できます。
翻訳可能なコンテンツを含むプロパティセットを定義する場合は、異なるコンテンツで共通の用語をオーバーロードしないようにしてください。ソース言語では複数のコンテンツで同じ用語にできる場合でも、コンテンツごとに区別できる用語を使用するようにしてください。たとえば、ある言語で「Name」という用語がオブジェクトと個人の両方に適用できるフィールド・ラベルとして使用されているが、ターゲット言語では2つの異なる用語に翻訳される場合があります。この場合、ソース言語の値が両方の「Name」になるような2つの名前/値ペアを提供します。その後、オブジェクト名はある用語で、個人名は別の用語でターゲット言語に翻訳すると、各用語を適切に翻訳できます。プロパティの説明を使用して用語ごとに個別のコンテキストを提供できます。
プロパティ・セットを定義するには、ダイアログを使用して新規のプロパティ・セットを作成し、「プロパティ」ウィンドウを使用してプロパティを指定します。
始める前に:
プロパティ・セットを使用できる方法に関する知識が役立つ場合があります。詳細は、「プロパティ・セットの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
プロパティ・セットを定義するには:
プロパティ・セットを作成すると、プロパティ・セットをエンティティ・オブジェクトやエンティティ属性に適用できるだけでなく、定義されたプロパティを使用(必要に応じてオーバーライド)することもできます。
始める前に:
プロパティ・セットを使用できる方法に関する知識が役立つ場合があります。詳細は、「プロパティ・セットの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトまたはビュー・オブジェクトにプロパティ・セットを適用するには:
「アプリケーション」ウィンドウで、プロパティ・セットを適用するオブジェクト(エンティティ・オブジェクトまたはビュー・オブジェクト)をダブルクリックします。
概要エディタの「一般」ナビゲーション・タブをクリックし、「プロパティ・セット」の横の「編集」アイコンをクリックします。
「プロパティ・セット」ダイアログで、適切なプロパティ・セットを選択し、「OK」をクリックします。
プロパティ・セットを属性に適用するには:
コントロール・ヒントは、ADFエンティティ・ベースのビュー・オブジェクトによって継承されます。それらを使用して、エンティティ・オブジェクトの属性についてラベル・テキスト、ツールチップおよびフォーマット・マスクのヒントを設定します。
コントロール・ヒントを使用すると、エンティティ・オブジェクト属性のラベル・テキスト、ツールチップおよびフォーマット・マスクのヒントを定義できます。必要に応じて、Groovy式を使用してUIヒントの値を計算できます。ビジネス・ドメイン・レイヤーで定義するUIヒントは、エンティティ・ベースのビュー・オブジェクトによっても継承されます。また、ビュー・オブジェクトやアプリケーション・モジュールでも同様にコントロール・ヒントを追加設定できます。
属性のコントロール・ヒントをエンティティ・オブジェクトに追加するには、概要エディタを使用します。
始める前に:
コントロール・ヒントがエンティティ・オブジェクトで使用される方法に関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトの属性のコントロール・ヒントの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
属性のコントロール・ヒントをエンティティ・オブジェクトに追加するには:
注意:
Javaで定義される数値および日付のフォーマット・マスクの標準セットは、OracleデータベースのSQLおよびPL/SQL言語によって使用されるものとは異なります。詳細は、java.text.DecimalFormat
およびjava.text.SimpleDateFormat
クラスのJavadocを参照してください。
エンティティ・オブジェクトの属性のコントロール・ヒントを定義すると、これらを格納するリソース・バンドル・ファイルが作成されます。定義したヒントは、関連付けられたビュー・クライアントにおいて生成されたフォームおよび表によって使用できます。ファイルのタイプとその粒度は、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」オプションで指定します。詳細は、「リソース・バンドルの使用」を参照してください。
(「UIヒント」タブで)属性(たとえば、Simple Date)の「フォーマット・タイプ」コントロール・ヒントを設定する場合、この属性にフォーマット・マスクも指定して、UIでの値の表示方法をカスタマイズできます。使用するマスクが「フォーマット」ドロップダウン・リストに表示されない場合は、フィールドに直接入力できます。
すべてのフォーマッタがフォーマット・マスクを必要とするわけではありません。フォーマット・マスクは、フォーマッタ・タイプによって必要とされる場合にのみ指定が必要です。たとえば、日付フォーマッタにはフォーマット・マスクが必要ですが、通貨フォーマッタには必要ありません。実際、通貨フォーマッタではフォーマット・マスクはサポートされていません。
使用できるマスク要素は、関連するJavaフォーマット・クラスで定義されています。Simple Dateフォーマット・タイプのマスク要素の詳細は、Javadocのjava.text.SimpleDateFormat
を参照してください。Numberフォーマット・タイプのマスク要素の詳細は、Javadocのjava.text.DecimalFormat
を参照してください。
コントロール・ヒントで使用するために、フォーマッタをドメインにマッピングする場合、oracle.jbo.format
パッケージで提供されているデフォルト・フォーマッタの1つを修正するか、oracle.jbo.format.Formatter
クラスを拡張して新しいフォーマッタ・クラスを作成できます。JDeveloperに用意されているデフォルトのフォーマッタは、java.text
パッケージで提供されているフォーマッタを集約したものです。
フォーマット・マスクを定義してJDeveloperの「UIヒント」タブから使用する方法の詳細は、「フォーマット・マスクの定義方法」を参照してください。
今後繰り返し使用するフォーマット・マスクがある場合は、「UIヒント」タブの「フォーマット」ドロップダウン・リストで利用できるように、formatinfo.xml
ファイルに追加できます。このファイルのエントリでは、ドメイン・クラスのフォーマット・マスクおよびフォーマッタ・クラスを定義します。次の例は、java.util.Date
ドメインのフォーマット定義を示しています。
<?xml version="1.0"?><FORMATTERS> . . . <DOMAIN CLASS="java.util.Date"> <FORMATTER name="Simple Date" class="oracle.jbo.format.DefaultDateFormatter"> <FORMAT text="yyyy-MM-dd" /> <FORMAT text="EEE, MMM d, ''yy" /> <FORMAT text="dd-MM-yy" /> <FORMAT text="dd-MMM-yyyy" /> <FORMAT text="dd/MMM/yyyy" /> </FORMATTER> </DOMAIN> . . . </FORMATTERS>
formatinfo.xml
ファイルは、JDeveloperシステム・ディレクトリのBC4Jサブディレクトリにあります(たとえば、C:\Documents and Settings\
username
\Application Data\JDeveloper\
system##
\o.BC4J\formatinfo.xml
です)。
フォーマット・マスクの定義は、フォーマッタおよびドメイン・クラスに属し、「UIヒント」タブに表示されるマスクのテキスト指定が含まれています。特定のタイプの属性(DOMAIN CLASS
)にフォーマット・タイプ(FORMATTER name
)を指定すると、「フォーマット」ドロップダウン・リストにマスク(FORMAT text
)が表示されます。
フォーマッタをマッピングする上で新規のドメインを作成する必要はありません。ビジネス・コンポーネント・プロジェクトに、フォーマッタと同じデータ型のドメインが含まれている場合は、既存のドメインを使用できます。
始める前に:
コントロール・ヒントがエンティティ・オブジェクトで使用される方法に関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトの属性のコントロール・ヒントの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
新しいマスクをJDeveloperで使用できるようにする手順は次のとおりです。
formatinfo.xml
ファイルを開きます。FORMATTER
要素内に新しいFORMAT
エントリを挿入します。フォーマット・マスクの新しいドメインを作成する場合は、フォーマッタのXML定義に、FORMATTER
(名前とクラスを含む)およびフォーマッタ・クラスで指定するFORMAT
定義のリストに加えて新しいDOMAIN CLASS
を含める必要があります。
フォーマット・マスクを定義したら、「UIヒント」タブの「フォーマット」ドロップダウン・リストから新しいフォーマット・マスクを選択できます。
JDeveloperでは、ADFビジネス・コンポーネントで定義した翻訳可能な文字列がプロジェクト・レベルのリソース・バンドル・ファイルに格納されます。
翻訳可能な文字列(バリデータ・エラー・メッセージや、エンティティ・オブジェクトまたはビュー・オブジェクトの属性コントロール・ヒントなど)を定義する場合、デフォルトでは、プロジェクト・レベルのリソース・バンドル・ファイルが作成され、そこに翻訳可能文字列が格納されます。たとえば、Model
プロジェクトでエンティティ・オブジェクトのコントロール・ヒントを定義すると、パッケージには、ModelBundle.
xxx
という名前のメッセージ・バンドル・ファイルが作成されます。定義したヒントは、関連付けられたビュー・クライアントにおいて生成されたフォームおよび表によって使用できます。
JDeveloperで使用されるリソース・バンドルのオプションは、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」ページのオプションで指定します。このオプションは、デフォルトで「プロパティ・バンドル」に設定され、.properties
ファイルが生成されます。このオプションおよびその他のリソース・バンドル・オプションの詳細は、「メッセージ・バンドル・オプションの設定方法」を参照してください。
「アプリケーション」ウィンドウでオブジェクトを選択し、「構造」ウィンドウの対応する「ソース 」ノードを参照して、エンティティ・オブジェクトのメッセージ・バンドル・ファイルを検証できます。「構造」ウィンドウには、「アプリケーション」ウィンドウで選択したコンポーネントの実装ファイルが表示されます。
次の例は、コントロール・ヒントの情報が表示されたメッセージ・バンドル・ファイルのサンプルを示しています。各String
配列の最初のエントリはメッセージ・キーで、2番目のエントリはこのキーに対応するロケール固有のString
値です。
oracle.summit.model.views.ItemVO.QuantityShipped_LABEL=Shipped oracle.summit.model.views.ItemVO.ItemTotal_LABEL=Item Total oracle.summit.model.views.ItemVO.ItemTotal_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter oracle.summit.model.views.ItemVO.Price_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter oracle.summit.model.views.OrdVO.DateOrdered_FMT_FORMATTER=oracle.jbo.format.DefaultDateFormatter oracle.summit.model.views.OrdVO.DateOrdered_FMT_FORMAT=dd-MM-yyyy oracle.summit.model.views.OrdVO.DateShipped_FMT_FORMATTER=oracle.jbo.format.DefaultDateFormatter oracle.summit.model.views.OrdVO.DateShipped_FMT_FORMAT=dd-MM-yyyy oracle.summit.model.entities.OrdEO_Rule_0=You cannot have a shipping date that is before the order date oracle.summit.model.entities.OrdEO.CustomerId_Rule_0=This is an invalid customer id errorId=This customer must pay cash oracle.summit.model.entities.ItemEO.ProductId_Rule_0=Invalid Product Id oracle.summit.model.views.OrdVO.Total_LABEL=Order Total oracle.summit.model.views.OrdVO.Total_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter oracle.summit.model.views.OrdVO.OrderFilled_LABEL=Order Filled . . .
JDeveloperでコントロール・ヒントおよびその他の翻訳可能文字列の保存に使用されるリソース・バンドル・オプションは、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」ページのオプションで指定します。このオプションは、デフォルトで「プロパティ・バンドル」に設定され、.properties
ファイルが生成されます。
始める前に:
リソース・バンドルが使用される方法に関する知識が役立つ場合があります。詳細は、「リソース・バンドルの使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
プロジェクトのリソース・バンドル・オプションを設定するには:
翻訳可能な文字列(属性コントロール・ヒントなど)を定義する場合は、「テキスト・リソースの選択」ダイアログで新しい文字列を入力したり、オブジェクトのデフォルトのリソース・バンドルで定義済の文字列を選択できます。必要に応じて、異なるリソース・バンドルを使用することもできます。これは、プロジェクト間で共有される共通のリソース・バンドルを使用する場合に便利です。
始める前に:
リソース・バンドルが使用される方法に関する知識が役立つ場合があります。詳細は、「リソース・バンドルの使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
デフォルト以外のリソース・バンドルの設定を使用するには:
ADFビジネス・コンポーネントを使用して作成したアプリケーションのモデル・レイヤーを国際化するには、各コンポーネントのメッセージ・バンドル・ファイルの翻訳バージョンを生成する必要があります。たとえば、OrdersImplMsgBundle
メッセージ・バンドルのイタリア語バージョンは、OrdersImplMsgBundle_it
という名前のクラスになり、さらに限定されたスイス・イタリア語バージョンは、OrdersImplMsgBundle_it_ch
という名前になります。通常は、これらのクラスによってベース・メッセージ・バンドル・クラスが拡張され、これらのクラスにローカライズが必要なメッセージ・キーのエントリとともにローカライズされたこれらの翻訳が含まれます。
次の例は、エンティティ・オブジェクトのメッセージ・バンドルのイタリア語バージョンを示しています。このイタリア語バージョンでは、RequestDate
およびAssignedDate
のフォーマット・マスクがdd/MM/yyyy HH:mm
に変更されています。これにより、イタリア語のユーザーには、2006年5月3日の日付値は、デフォルトのメッセージ・バンドルのフォーマット・マスクで生成される05/03/2006 15:55
ではなく03/05/2006 15:55
として表示されます。オーバーライドされたgetContents()
メソッドに注意してください。これは、スーパークラス・バンドルからオーバーライドされていない文字列にマージされた、より限定された翻訳文字列によるメッセージ配列を戻します。実行時には、現在のユーザーのロケール設定に基づいて適切なメッセージ・バンドルが自動的に使用されます。
package oracle.summit.model; import oracle.jbo.common.JboResourceBundle; public class ModelImplMsgBundle_it extends ModelImplMsgBundle { static final Object[][] sMessageStrings = { { "AssignedDate_FMT_FORMAT", "dd/MM/yyyy HH:mm" }, { "AssignedDate_LABEL", "Assegnato il" }, { "AssignedTo_LABEL", "Assegnato a" }, { "CreatedBy_LABEL", "Aperto da" }, { "ProblemDescription_LABEL", "Problema" }, { "RequestDate_FMT_FORMAT", "dd/MM/yyyy HH:mm" }, { "RequestDate_LABEL", "Aperto il" }, { "RequestDate_TOOLTIP", "La data in cui il ticket è stato aperto" }, { "Status_LABEL", "Stato" }, { "SvrId_LABEL", "Ticket" } }; public Object[][] getContents() { return super.getMergedArray(sMessageStrings, super.getContents()); } }
ビジネス・ロジック・グループを使用して、最適化されたパフォーマンスとメンテナンスのしやすさために、関連するコントロール・ヒント、デフォルト値およびADFエンティティ・オブジェクトの検証ロジックを別々のファイルに格納します。
ビジネス・ロジック・グループでは、関連するコントロール・ヒント、デフォルト値および検証ロジックをカプセル化できます。ビジネス・ロジック・グループは、ベース・エンティティとは別に独自のファイルに保存され、現在の行のコンテンツ値に基づいて動的に有効にできます。
これは、ロケールごとの専門チームによって管理される多数のロケール固有の検証(国別識別子や税法チェックなど)が定義されるHRアプリケーションなどで有効です。ビジネス・ロジック・グループでは、これらの検証を個別のファイルに格納するため管理が容易になり、必要時にのみロードすることでパフォーマンスが最適化されます。
各ビジネス・ロジック・グループには、ビジネス・ロジック・ユニットのセットが格納されます。各ユニットでは、ビジネス・ロジック・グループに関連付けられた属性値に基づいて、エンティティでロードされるビジネス・ロジックのセットを識別します。
たとえば、Employee
エンティティ・オブジェクトにビジネス・ロジック・グループを定義し、EmpRegion
属性を識別子として指定できます。次に、リージョンごとにビジネス・ロジック・ユニットを定義し、各リージョンで従業員の給与に範囲バリデータを指定します。Employee
エンティティから行がロードされると、(EmpRegion
属性の値に基づいて)EmpSalary
属性の適切なバリデータがロードされます。
別の例をあげると、Orders
エンティティ・オブジェクトは識別子属性としてOrderFilled
を使用するFilledOrderGroup
と呼ばれるビジネス・ロジック・グループを持ちます。この属性には2つの有効な値(N
およびY
)があるため、対応するビジネス・ロジック・ユニットが2つあります。
このシナリオでは、各ビジネス・ロジック・ユニットにはそのユーザー・タイプのみに関連する新規または修正済ビジネス・ロジックが含まれます。
Orders_FilledOrderGroup_N
ビジネス・ロジック・ユニットには、まだ入力されていない注文に関連するロジックが含まれます。たとえば、DateShipped
属性を非表示にするように構成できます。
Orders_FilledOrderGroup_Y
ビジネス・ロジック・ユニットには、入力されている注文に関連するロジックが含まれます。たとえば、DateShipped
属性を表示するように構成できます。
エンティティ・オブジェクトのビジネス・ロジック・グループは、概要エディタから作成します。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・グループを作成するには:
新しいビジネス・ロジック・グループが概要エディタの表に追加されます。グループを作成すると、ビジネス・ロジック・ユニットを追加できます。
ビジネス・ロジック。ユニットは「新規ギャラリ」から作成できます。また、ビジネス・ロジック・グループを格納するエンティティのポップアップ・メニューから直接作成することもできます。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットを作成するには:
JDeveloperでは、概要エディタでビジネス・ロジック・ユニットを作成して開きます。「アプリケーション」ウィンドウのビジネス・ロジック・ユニットに表示される名前は、EntityName_BusLogicGroupName_BusLogicUnitName
の形式で、エンティティ・オブジェクトの名前とビジネス・ロジック・グループが含まれます。たとえば、Orders
エンティティ・オブジェクトのFilledOrderGroup
ビジネス・ロジック・グループにY
という名前でビジネス・ロジック・ユニットを作成する場合、表示されるビジネス・ロジック・ユニット名はOrders_FilledOrderGroup_Y
になります。
ビジネス・ロジック・ユニットを作成すると、そのビジネス・ロジック・ユニットに対してビジネス・ロジックを再定義できます。
ビジネス・ロジック・ユニットを作成したら、これを概要エディタで開き、ベース・エンティティで行う場合(エンティティレベルのバリデータを追加するなど)と同様に、ビジネス・ロジックを追加できます。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットにエンティティ・バリデータを追加するには:
概要エディタでビジネス・ロジック・ユニットの「属性」ページを表示すると、属性表の「拡張」列に、属性がビジネス・ロジック・ユニットで拡張されていることが表示されます。拡張属性は、ビジネス・ロジック・ユニットではなく、ベース・エンティティでのみ編集できます。ベース・エンティティではなくビジネス・ロジック・ユニットで変更する場合は、編集する前に、ビジネス・ロジック・ユニットで属性をオーバーライド済として定義する必要があります。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットの属性をオーバーライドするには:
属性をオーバーライドしたら、次の表にあるタブで通常どおりに属性を編集できます。オーバーライドされた属性では、コントロール・ヒント、バリデータおよびデフォルト値の編集のみ行えます。
ビジネス・ロジック・グループを作成すると、次のようにベース・エンティティのXMLファイルのグループに参照が追加されます。
<BusLogicGroup Name="FilledOrderGroup" DiscrAttrName="OrderFilled"/>
ビジネス・ロジック・ユニットを作成すると、エンティティ・オブジェクトのXMLファイルと同様のXMLファイルが生成されます。次の例は、ビジネス・ロジック・ユニットのXMLコードを示しています。
注意:
ビジネス・ロジック・ユニットのパッケージは、ベース・エンティティまたはビジネス・ロジック・グループのパッケージと同じにする必要はありません。これにより、コア・アプリケーションと別にビジネス・ロジック・ユニットを開発して配信できます。
<Entity xmlns="http://xmlns.oracle.com/bc4j" Name="Orders_FilledOrderGroup_N" Version="12.1.2.66.11" Extends="oracle.summit.model.buslogic.Orders" InheritPersonalization="merge" DBObjectType="table" DBObjectName="S_ORD" BindingStyle="OracleName" BusLogicGroupName="FilledOrderGroup" BusLogicUnitName="N"> <Attribute Name="Total" Precision="11" Scale="2" ColumnName="TOTAL" SQLType="NUMERIC" Type="java.math.BigDecimal" ColumnType="NUMBER" TableName="S_ORD"> <DesignTime> <Attr Name="_OverrideAttr" Value="true"/> </DesignTime> <Properties> <SchemaBasedProperties> <LABEL ResId="oracle.summit.model.buslogic.Orders_FilledOrderGroup_N.Total_LABEL"/> </SchemaBasedProperties> </Properties> </Attribute> <Attribute Name="DateShipped" ColumnName="DATE_SHIPPED" SQLType="DATE" Type="java.sql.Date" ColumnType="DATE" TableName="S_ORD"> <DesignTime> <Attr Name="_OverrideAttr" Value="true"/> </DesignTime> <Properties> <SchemaBasedProperties> <DISPLAYHINT Value="Hide"/> </SchemaBasedProperties> </Properties> </Attribute> <ResourceBundle> <PropertiesBundle PropertiesFile="oracle.summit.model.ModelBundle"/> </ResourceBundle> </Entity>
実行時にアプリケーションで行がロードされると、適用するビジネス・ロジック・ユニットがエンティティ・オブジェクトによって決定されます。
ベース・エンティティには、ビジネス・ロジック・グループのリストが格納されています。各グループは、エンティティの属性値を参照し、この値によってそれぞれのグループにロードするビジネス・ロジック・ユニットが決定されます。この評価は、ロードされる行ごとに実行されます。
ロードするビジネス・ロジック・ユニットを決定するロジックが簡単な属性値ではなく複雑な場合は、エンティティ・オブジェクトに一時属性を作成し、Groovy式を使用して一時属性の値を指定できます。
ADFエンティティ・オブジェクトを使用して、ビジネス・レイヤーのビジネス・ロジックおよび検証規則を、宣言型ランタイム機能を使用して実装します。
エンティティ・オブジェクトには、通常のエンタープライズ・ビジネス・アプリケーションの実装を簡略化するための様々な宣言的機能が用意されています。タスクによっては、宣言的機能のみでニーズが満たされる場合があります。エンティティ・オブジェクトの基本的な永続機能を記述する宣言的なランタイム機能はこの項で説明しますが、宣言的な検証およびビジネス・ルールについては、「検証とビジネス・ルールの宣言的な定義」で説明します。
注意:
必要に応じて、宣言的動作を超えて、より複雑なビジネス・ロジックまたは検証規則をビジネス・ドメイン・レイヤーに実装することができます。カスタム・コードを使用した最も一般的なエンティティ・オブジェクトの拡張方法は、「プログラムによる検証とビジネス・ルールの実装」を参照してください。
またアプリケーションを開発する際の注意点として、プログラム的または宣言的にビジネス・ロジックを実装する場合、エンティティ・オブジェクトまたはビュー行の属性が特定の順序に従って設定されることを想定しないようにすることが重要です。順序が想定されていると、それとは異なる順序でエンド・ユーザーが属性値を入力した場合に問題が発生します。
エンティティ・オブジェクトの宣言的な実行時動作を構成するには、概要エディタを使用します。
始める前に:
実行時動作の宣言的な構成に関する知識が役立つ場合があります。詳細は、「宣言的な実行時動作の構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトの宣言的な実行時動作を構成するには:
エンティティ・オブジェクトの実行時動作を記述および制御する宣言的設定は、XMLドキュメント・ファイルに格納されます。概要エディタを使用してエンティティの設定を変更する場合、JDeveloperによりコンポーネントのXML定義ファイルおよびオプションのカスタムJavaファイルが更新されます。
バッチ更新を使用すると、複数のエンティティ変更の際に発行されるDML文の数を減らすことができます。
デフォルトで、ADFビジネス・コンポーネント・フレームワークは、指定されたエンティティ定義型の変更済エンティティ1つにつき、1つのDML文(DELETE
、INSERT
、UPDATE
)を実行します。たとえば、Employeeエンティティ・オブジェクト型があり、アプリケーションの通常使用でその複数のインスタンスが変更されたとします。4つのインスタンスが作成され、2つの既存のインスタンスが変更され、3つの既存のインスタンスが削除された場合、それらの変更を保存するために、トランザクション・コミット時間に、フレームワークにより9つのDML文(4つのDELETE
、2つのINSERT
、3つのUPDATE
)が発行されます。
トランザクションで、ある型の複数のエンティティを頻繁に更新する場合、そのエンティティ定義型に対してバッチ更新機能を使用することを検討してください。この例では、バッチ更新(しきい値は1)でフレームワークが発行するDML文は、4件の削除を処理する1つの一括DELETE
文、2件の挿入を処理する1つの一括INSERT
文および3件の更新を処理する1つの一括UPDATE
文の3つのみです。
しきい値は、個別に処理された特定の操作ごとの変更済エンティティ行の数を示しています。変更済エンティティ行の数がしきい値を超えると、その操作では一括処理が使用されます。この例では、しきい値が3の場合、DELETE
は一括更新実行しますが(4つのDELETE
文のため)、INSERT
とUPDATE
は一括処理を使用しません(INSERT
文は2つのみで、UPDATE
文は3つのため)。
注意:
バッチ更新がコンポジット関連と組み合せてエンティティ・オブジェクトで使用されている場合に、親オブジェクトが1より大きいしきい値を定義する場合は、すべての構成される子オブジェクトでは、1より大きいしきい値を定義する必要があります。
次のいずれかの場合、バッチ更新機能は無効になります。
エンティティ・オブジェクトにはBLOB型またはCLOB型の属性があります。ストリーミング・データ型を持つバッチDMLはサポートされていません。
Refresh After InsertまたはRefresh After Updateに設定されている属性がエンティティ・オブジェクトにあります。1回のラウンド・トリップでトリガー割当て値のすべてを一括で戻すメソッドがないため、属性の取得や更新を行う処理はバッチDMLで動作しません。
主キーがない表からエンティティ・オブジェクトを作成しました。表に主キーがなく、その表からエンティティ・オブジェクトのリバース・エンジニアリングを行った場合、ROWID値属性が作成されて、かわりに主キーとして割り当てられます。ROWID値は、挿入時に取得スタイルの値として管理されるため、バッチDMLで動作しません。
始める前に:
実行時動作の宣言的な構成に関する知識が役立つ場合があります。詳細は、「宣言的な実行時動作の構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティに対してバッチ更新を有効化する手順:
これにより、それを超えるとOracle ADFが一括DML操作を使用して変更を処理する、バッチ処理のしきい値が指定されます。
宣言フレームワークを使用して、属性プロパティをADFビジネス・コンポーネントの概要エディタの「属性」ページで設定します。
宣言的なフレームワークによって、属性プロパティを簡単に設定できます。いずれの場合も、これらのプロパティは概要エディタの「属性」ページで設定します。
「永続的」
プロパティにより、基礎となる表内の列に属性値が対応するか、単なる一時的な値であるかを制御します。属性が永続的である場合、「データベース列」領域を使用して、属性に対応する基礎となる列の名前を変更し、精度およびスケール情報を使用してその列型を指定できます(VARCHAR2(40)
やNUMBER(4,2)
など)。エンティティ・オブジェクトは、実行時にこの情報に基づいて、属性値の最大長または精度/スケールを設定し、値が要件を満たさない場合は例外をスローします。
「表からのビジネス・コンポーネント」ウィザードと「エンティティ・オブジェクトの作成」ウィザードでは、各エンティティ・オブジェクト属性のJava型は、関連付けられている列のデータベース列型のSQL型から推測されます。
注意:
プロジェクトの「型マップ」設定は、Javaデータ型の決定にも役割を果します。ビジネス・コンポーネント・プロジェクトを初期化する場合は、ビジネス・コンポーネントが作成される前に「型マップ」設定を指定します。詳細は、「データベース接続を使用してデータ・モデル・プロジェクトを初期化する方法」を参照してください。
(「詳細」タブの)「型」フィールドでは、エンティティ属性のJava型を目的の型に変更できます。「列の型」フィールドには、属性がマップされている、基礎となるデータベース列のSQL型が表示されます。「列名」フィールドの値により、属性がマップされる列が制御されます。
エンティティ・オブジェクトは、表4-1に示す様々な列型の表を処理できます。デフォルトのJava属性タイプはすべてoracle.jbo.domain
および各種java
パッケージに格納されており、対応する型のOracleデータベースのデータを効率的に処理できるようサポートされています。「型」フィールドのドロップダウン・リストには、その他の多くの共通型が含まれており、これらもサポートされています。
表4-1 デフォルトのエンティティ・オブジェクトの属性タイプのマッピング
Oracleの列型 | エンティティの列型 | エンティティのJava型 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
注意:
ここで示した型以外にも、oracle.jbo.domain.DomainInterface
インタフェースが実装されていることを前提に、任意のJavaオブジェクト型をエンティティ・オブジェクトの属性の型として使用できます。
VARCHAR2(n)
などの最大長の定義をサポートする型を使用する場合、(「詳細」タブの)「列の型」フィールドには、値の一部として属性の最大長が含まれます。たとえば、データベースのVARCHAR2(10)
列に基づく属性の場合、データベース列の型としてVARCHAR2(10)
が表示され、最初に最大長として10文字が示されます。なんらかの理由により、String
値の属性の最大長を、基礎となる列で使用可能な文字数より少ない文字数に制限する必要がある場合は、「列の型」の値の最大長を変更します。
たとえば、PERSONS
表のEMAIL
列がVARCHAR2(50)
である場合、デフォルトでは、Persons
エンティティ・オブジェクトのEmail
属性も同じ型に設定されます。ただし、実際の電子メール・アドレスが常に8文字以下である場合は、Email
属性のデータベース列の型をVARCHAR2(8)
に更新し、エンティティ・オブジェクト・レベルでの最大長を8文字に設定できます。
これは、NUMBER(p[
,s
])
のように精度やスケールの定義をサポートするデータベース列の型に関連する属性についても同様です。たとえば、データベースのNUMBER(7,2)
列に基づく属性の精度を5、スケールを1に制限するには、「列の型」フィールドの値をNUMBER(5,1)
に更新します。
注意:
この機能は注意して使用してください。他のアプリケーションがこのデータベース列にアクセス可能な場合は、他のアプリケーションがより厳しい制限に準じるかどうかを確認できません。
「更新可能」
プロパティにより、特定の属性値をいつ更新できるかを制御します。次の値を選択できます。
常に: 属性は常に更新可能です。
なし: 属性は読取り専用です。
新規の間: エンティティ行を初めて作成するトランザクション中は属性を設定できますが、データベースへのコミットが成功した後、属性は読取り専用になります。
注意:
更新可能性の静的な宣言に加えて、エンティティのisAttributeUpdateable()
メソッドにカスタム・コードを追加して、実行時に属性の更新可能性を決定することもできます。
フィールドが必須の場合は、「必須」チェック・ボックスを選択します。必須プロパティは、実行時、(属性バリデータの実行時ではなく)エンティティ・レベルの検証が行われる際に適用されます。
「主キー」
プロパティにより、属性がエンティティを一意に識別するキーの一部であるかどうかを指定します。通常、主キーには単一属性を使用しますが、複数属性の主キーも完全にサポートされています。
実行時には、getKey()
メソッドを使用して任意のエンティティ行に関連するKey
オブジェクトにアクセスすると、このKey
オブジェクトには、このエンティティ・オブジェクトの主キー属性の値が含まれます。このエンティティ・オブジェクトに複数の主キー属性がある場合、Key
オブジェクトにはこれらの各値が含まれます。これらの値が、エンティティ・オブジェクト定義で対応する主キー属性と同じ相対順序で表示されると理解することが重要です。
たとえば、ItemEO
エンティティ・オブジェクトには、OrdId
およびItemId
という複数の主キー属性があります。概要エディタの「属性」ページでは、OrdId
が先頭で、ItemId
が2番目です。ItemEO
型のエンティティ行のKey
オブジェクトによってカプセル化された値の配列では、これらの2つの属性値がこれとまったく同じ順序になります。
「エンティティ属性」ページでは、複数の主キー属性がどのような順序で表示されているかを理解しておくことが非常に重要です。findByPrimaryKey()
を使用して複数属性の主キーを持つエンティティを検索する場合、作成したKey
オブジェクトでこれら複数の主キー属性の順序が間違っていると、エンティティ行が期待したとおりに検出されません。
さらに、主キーを新しい行に移入するには、トリガーを使用してデータベースから値を割り当てる場合もあります。詳細は、「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照してください
値のタイプが「リテラル」に設定されている場合、「詳細」タブの「値」フィールドには、属性のデフォルト値を静的に指定できます。たとえば、ServiceRequest
エンティティ・オブジェクトのStatus
属性のデフォルト値をOpen
に設定したり、User
エンティティ・オブジェクトのUserRole
属性のデフォルト値をuser
に設定できます。
注意:
1つのエンティティ・オブジェクトに複数の属性がデフォルト設定される場合は、エンティティ・オブジェクトのXMLファイルにおいてそれらの属性が表示されている順にデフォルト設定されます。
属性のデフォルト値は、Groovy式またはSQL文を使用して定義できます。実行時にデフォルト値を動的に定義できるようにする場合はこの方法が便利ですが、デフォルト値が常に同じ場合は、(「詳細」タブの)「リテラル」型のフィールドを使用すると、より簡単に値を表示および管理できます。Groovyの使用の一般的な情報は、「ビジネス・コンポーネントでのGroovyスクリプト言語の使用」を参照してください。
始める前に:
属性プロパティの設定方法の知識があると役立ちます。詳細は、「属性プロパティの設定」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
式を使用してデフォルト値を定義するには:
Groovy式を使用してデフォルト値を定義すると、<TransientExpression>
タグがビジネス・オブジェクトのXMLファイルの適切な属性内に追加されます。次の例は、CountryVO
ビュー・オブジェクトのID
属性に追加されたGroovy式のXMLコードを示しています。このXML定義は、このGroovy式スクリプトが含まれている.bcs
ファイルはoperations.xml
によって指し示されることを示しており、この例ではCodeSourceName=”CountryVOOperations.xml”
がこれに当たります。operations.xml
ファイルには.bcs
ファイルを指し示すURIが含まれています。
<View Attribute Name="Id" IsNotNull="true" PrecisionRule="true" EntityAttrName="Id" EntityUsage="CountryEO" AliasName="ID"> <TransientExpression Name="ExpressionScript" trustMode="untrusted" CodeSourceName="CountryVORow"/> </ViewAttribute>
注意:
Groovyファイル生成がこのモデル・プロジェクトまたはアプリケーション全体に対して有効になっている場合、このビジネス・コンポーネントの概要エディタ・ページ上の「デフォルト値」セクションの下に、Groovy式がハイパーリンクとして表示されます。このハイパーリンクをクリックすると、このビジネス・コンポーネントに関連する.bcs
ファイルが開きます。プロジェクト設定の詳細は、「Groovyプロジェクトの設定に関する必知事項」を参照してください。入力したGroovy式がどのように処理されるかについては、「式を入力したときの処理」を参照してください。 基礎となる列値が、挿入または更新の操作時にデータベース・トリガーによって更新される場合、「詳細」タブの「挿入時にリフレッシュ」チェック・ボックスまたは「更新時にリフレッシュ」チェック・ボックスをそれぞれ選択することにより、変更された値をフレームワークが自動的に取得し、エンティティ・オブジェクトとデータベース行を同期化できます。エンティティ・オブジェクトは、INSERT
またはUPDATE
を実行しながらOracle SQLのRETURNING INTO
機能を使用して、変更された列を1回のデータベースのラウンドトリップでアプリケーションに戻します。
注意:
DBLINKを介してリモート表に解決されるシノニムのエンティティ・オブジェクトを作成する場合、この機能を使用すると、実行時に次のようなエラーが発生します。
JBO-26041: Failed to post data to database during "Update" ## Detail 0 ## ORA-22816: unsupported feature with RETURNING clause
このようなデータベース制限を回避する技術は、「結合ビューまたはリモートDBLinkに基づくエンティティ・オブジェクト」を参照してください。
挿入後に属性がリフレッシュされる一般的なケースは、主キー属性の値がBEFORE INSERT FOR EACH ROW
トリガーによって割り当てられる場合です。多くの場合、このトリガーにより、次の例に示すようなPL/SQLロジックを使用してデータベース順序から主キーが割り当てられます。
CREATE OR REPLACE TRIGGER ASSIGN_SVR_ID BEFORE INSERT ON SERVICE_REQUESTS FOR EACH ROW BEGIN IF :NEW.SVR_ID IS NULL OR :NEW.SVR_ID < 0 THEN SELECT SERVICE_REQUESTS_SEQ.NEXTVAL INTO :NEW.SVR_ID FROM DUAL; END IF; END;
注意:
稼働中のデータベース内でトリガーを作成することもできれば、かわりにオフライン・データベースでトリガーを作成してからそれを稼働中のデータベースにインポートすることもできます。オフライン・データベース・オブジェクトの操作の詳細は、『Oracle JDeveloperによるアプリケーションの開発』のデータベース・オブジェクトの作成、編集およびドロップに関する項を参照してください。
「詳細」タブ(概要エディタの「属性」ページ)で、「型」フィールドの値を組込みデータ型(名前はDBSequence
)に設定でき、主キーが自動的にデータベース順序によって割り当てられます。このデータ型を設定すると、「挿入時にリフレッシュ」チェック・ボックスが自動的に選択されます。
注意:
「順序」タブに表示されている順序名が設計時に使用されるのは、「エンティティ・オブジェクトからのデータベース表の作成方法」で説明されているデータベース表の作成機能を使用する場合のみです。ここで示す順序は、エンティティ・オブジェクトの基礎となる表とともに作成されます。
主キーがDBSequence
である新しいエンティティ行を作成すると、一意の負の数値が一時値として割り当てられます。この値は、このエンティティ行を作成するトランザクション中、主キーとして機能します。同じトランザクション内で一連の相関エンティティを作成する場合、その他の新しい関連エンティティ行にこの一時値を外部キー値として割り当てることができます。トランザクションのコミット時に、エンティティ・オブジェクトは、RETURNING INTO
句を使用してINSERT
操作を発行し、実際にデータベースのトリガーによって割り当てられた主キー値を取得します。コンポジット関連では、先にこの一時的な負の値を外部キーとして使用していた新しい関連エンティティでは、マスターの実際の新しい主キーを反映してこの値が更新されます。
通常は、DBSequence値を持つ主キーの「更新可能」プロパティも「なし」に設定します。一時IDはエンティティ・オブジェクトによって割り当てられ、INSERT
操作の後に実際のID値を使用してリフレッシュされます。エンド・ユーザーがこの値を更新する必要はありません。
コンポジット以外のアソシエーションでこの機能を実装する方法の詳細は、「DBSequenceの値が設定される主キーに基づくアソシエーション」を参照してください。
注意:
DBSequenceのかわりにメタデータ駆動型の方法を使用する場合は、「Oracle順序を使用した主キー値の割当て」を参照してください。
ユーザー・インタフェースを使用した主キー値の更新は、ADFビジネス・コンポーネントではサポートされていません。ただし、DBSequenceを使用するか(「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照)、プログラムによって(「Oracle順序を使用した主キー値の割当て」を参照)主キー値を更新することはできます。このようなユーザー・インタフェース動作は、ユーザーに主キーの変更を許可するのではなく、サロゲート・キーを使用して実装できる場合があります。
実行時には、フレームワークによりエンティティ・オブジェクトの更新の上書きが検出され、作業中に別のユーザーによって更新およびコミットされたデータであることに気付かずにユーザーがこのデータを変更できないようにします。通常、このチェックは、基礎となる行がロックされているときにデータベース内で対応する現在の列値と各永続エンティティ属性の元の値を比較することによって実行されます。行の更新前に、エンティティ・オブジェクトによって、更新される行とデータベースの現在の状態の間に一貫性があるかどうかが検証されます。行とデータベースの状態の間に一貫性がない場合、エンティティ・オブジェクトではRowInconsistentException
が発生します。
更新の上書きの検出をより効率的にするには、エンティティが変更されるたびに値が更新されるエンティティの属性を特定します。通常、このような値には、行内のバージョン番号列や更新日付列などがあります。(「詳細」タブの)「挿入時にリフレッシュ」オプションまたは「更新時にリフレッシュ」オプションを選択したので、作成したデータベース・トリガーによって更新識別子の属性値を割り当て、エンティティ・オブジェクトでこれらの値をリフレッシュできます。また、「履歴列を使用して作成および変更した日付を追跡する方法」で説明する履歴属性の機能を使用してエンティティ・オブジェクトによって更新識別子の属性値の更新が管理されるように指定できます。
ユーザーによる問合せ以降にエンティティ行が変更されたかどうかを最も効率的な方法で検出するには、「更新識別子」オプションを選択し、更新識別子の属性値のみを比較します。更新識別子属性のないエンティティ・オブジェクトはすべての属性の値がデータ一貫性チェックで使用されるため効率が低下する場合があります。更新識別子属性のないエンティティ・オブジェクトは、ユーザー・セッションがアプリケーション・モジュールとのアフィニティを失った(アプリケーション・モジュール・プールが使用されている場合)ときにデータの一貫性を失う可能性もあります。詳細は、「状態管理とデータの整合性に関する必知事項」を参照してください。アプリケーション・モジュール・プールの詳細は、「アプリケーション・モジュール・プールのチューニング」を参照してください。
まれに、アプリケーション・モジュールの受動化を実行中に、文字列値が正しく保持されない場合があります。たとえば、空白が切り捨てられる場合があります。エンティティ・オブジェクト属性でカスタム・プロパティを定義して、文字列値をCDATAセクションにカプセル化できます。
XML_CDATA
カスタム・プロパティを定義して、エンティティ・オブジェクト属性のデフォルト値の空白形式を保持するには、次のようにします。XML_CDATA
のプロパティを追加し、値をtrue
に設定します。CategoryCode
の例に示すように、カスタム・プロパティをエンティティ・オブジェクトのXML定義に追加します。<Attribute Name="CategoryCode" ... <Properties> <CustomProperties> <Property Name="XML_CDATA" ResId="model.Departments.LocationId.XML_CDATA_VALUE"/> </CustomProperties> </Properties> </Attribute>
エンティティの作成日時や変更日時、作成者や変更者、エンティティの変更回数など、エンティティ・オブジェクトで履歴情報を追跡する必要がある場合、「変更履歴のトラッキング」オプションが選択されている属性を指定します(「詳細」タブ)。
属性のデータ型がNumber
、String
またはDate
であり、主キーの一部ではない場合、このプロパティを有効化して履歴監査用の属性値を自動的に管理できます。フレームワークにおける属性の処理方法は、次のような履歴属性のタイプの中からどれを指定するかによって異なります。
作成日付: この属性には、行が作成された日付のタイム・スタンプが移入されます。タイム・スタンプはデータベースから取得されます。
作成者: この属性には、行を作成したユーザーの名前が移入されます。ユーザー名は、Session
オブジェクトのgetUserPrincipalName()
メソッドを使用して取得されます。
変更日付: この属性には、行が更新/作成された日付のタイム・スタンプが移入されます。
変更者: この属性には、行を作成/更新したユーザーの名前が移入されます。
バージョン番号: この属性には、行が作成または更新されるたびに増分するlong値が移入されます。
エンティティ・オブジェクトでは、ItemEO
エンティティを作成するOrdEO
エンティティなど、他のエンティティを作成(または構成)する場合、コンポジット動作が示されます。この付加的な実行時動作により、ネストされた他のエンティティ・オブジェクト部分の論理コンテナとしての役割が決定されます。この関係により、コンポジット・アソシエーションは、一時属性をベースに作成できません。
注意:
コンポジットは、エンティティが検証される順序にも影響を与えます。詳細は、「検証順序に対するコンポジットの影響の理解」を参照してください。
エンティティ・オブジェクトの構成で常に有効な機能については、次の項を参照してください。
別の機能およびこれらの動作に影響するプロパティについては、次の項を参照してください。
構成されるエンティティ・オブジェクトが作成されると、この値が既存のエンティティを所有する親のエンティティとして識別しているか確認するため、外部キー属性の値の存在がチェックされます。作成時に外部キーが検出されなかった場合や、既存のエンティティ・オブジェクトを識別しない値が検出された場合、エンティティ・オブジェクトでは、親エンティティが識別されていない状態で親のない子行が作成されないように、InvalidOwnerException
がスローされます。
注意:
外部キー属性の値の存在がチェックされたことにより、現在のトランザクションで新しい保留エンティティが検索される他、必要に応じてデータベース内の既存のエンティティが検索されます。
コンポジット動作により、構成する側のエンティティ・オブジェクトと構成される側のエンティティ・オブジェクトの両方が含まれるトランザクションで実行されるデータ操作言語(DML)操作が正しい順序で実行されたかどうかを確認します。たとえば、構成する側の新しい親エンティティ・オブジェクトに対するINSERT
文は、構成される側の子エンティティ・オブジェクトに関連するDML操作の前に実行されます。
主キーが挿入時にリフレッシュするように構成されている新しいエンティティ行を保存すると、トリガーによって割り当てられた主キー値の取得後に、構成される側のエンティティの外部キー属性値が更新されて新しい主キー値が反映されます。
これ以外にも、アソシエーション作成ウィザードの「アソシエーション・プロパティ」ページまたは概要エディタの設定を介して制御可能なコンポジット関連機能が多数用意されています。図4-13は、2つのエンティティ・オブジェクトItemEO
とOrdEO
の間のSItemOrdIdFkAssoc
アソシエーションが表示された「関連」ページを示しています。
構成される側の子が存在していても、構成する側の親の削除を有効化または禁止できます。「カスケード削除の実装」オプション(図4-13を参照)の選択が解除されている場合に、構成する側の親に構成される側の子が含まれる場合、構成する側のエンティティ・オブジェクトの削除は禁止されます。
図4-13 概要エディタの「関連」ページのアソシエーション動作設定
このオプションが選択されている場合、構成する側のエンティティ・オブジェクトを、構成される側の子エンティティとともに無条件に削除できます。関連する「データベースのカスケード削除用に最適化」オプションの選択が解除されている場合、構成される側のエンティティ・オブジェクトでは、トランザクションのコミット時に通常のDELETE
文が実行され、変更が永続的になります。このオプションが選択されている場合、データベースのON DELETE CASCADE
制約によって対応する行の削除が処理されることを前提に、構成される側のエンティティではDELETE
文は実行されません。
「カスケード更新キー属性」オプション(図4-13を参照)を選択すると、構成する側のエンティティの主キー値が変更される場合に、構成される側のエンティティの外部キー属性値が自動で更新されるよう設定できます。
「最上位コンテナのロック」オプション(図4-13を参照)を選択すると、構成される側の詳細エンティティ行の追加、削除または変更時に、変更を保存する前に、構成する側のエンティティをロックするかどうかを制御できます。
バッチ更新がコンポジット関連と組み合せてエンティティ・オブジェクトで使用されている場合に、親オブジェクトが1より大きいしきい値を定義する場合は、すべての構成される子オブジェクトでは、1より大きいしきい値を定義する必要があります。
「トップレベルの履歴列の更新」オプション(図4-13を参照)を選択すると、構成される側の詳細エンティティ・オブジェクトの追加、削除または変更時に、構成する側の親エンティティの「変更者」および「変更日付」履歴属性を更新するかどうかを制御できます。
場合によっては、論理的に関連付けられた様々なオブジェクトに関する情報が1つのデータベース表に格納されることがあります。たとえば、給与計算アプリケーションでは、アルバイト、正社員、契約社員すべてが、EMPLOYEE_TYPE
列を持つ1つのEMPLOYEES
表に格納されることがあります。この場合、EMPLOYEE_TYPE
列では、H
、S
、C
などの値を使用して、特定の行がそれぞれアルバイト(Hourly)、正社員(Salaried)、契約社員(Contract)のいずれを表すのかを示します。多くの属性や動作はすべての従業員で同一の場合もありますが、特定のプロパティやビジネス・ロジックは従業員のタイプによって異なることがあります。
関連するオブジェクトの間に共通情報が存在する場合は、継承階層を使用してこのような異なるタイプのエンティティ・オブジェクトを表すと便利です。たとえば、すべての従業員に共通の属性およびメソッドは、ベースとなるEmployee
エンティティ・オブジェクトの一部とし、HourlyEmployee
、SalariedEmployee
およびContractEmployee
などのサブタイプのエンティティ・オブジェクトでは、ベースとなるEmployee
オブジェクトを拡張し、別のプロパティや動作を追加します。「多相化識別子」属性設定を使用して、行のタイプを識別する属性の値を示します。継承の設定方法および使用方法は、「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
データベースの主キーは順序から生成されることが多く、様々な理由からユーザーへの公開を避けることが必要な場合があります。このため、多くのケースでは一意の代替キー値を使用すると便利です。たとえば、すべての顧客に一意の電子メール・アドレスを持たせるとします。顧客は電子メール・アドレスを変更する可能性があるため、主キーとしてはこの値を使用できませんが、それぞれの顧客に対してログインなどの目的で使用できる一意のフィールドを持たせる必要があります。
代替キーは、メソッドのfindByKey
クラスを介して直接行を参照する場合に便利です。代替キーは、中間層で効率的な一意性チェックを行う場合にもよく使用されます。値が一意であるかどうかの確認方法の詳細は、「キー値の一意性を確認する方法」を参照してください。
代替キーの定義には、「エンティティ制約の作成」ウィザードを使用します。
始める前に:
属性プロパティの設定方法の知識があると役立ちます。詳細は、「属性プロパティの設定」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
代替キー値を定義するには:
1対多の列に基づいてADFエンティティ・オブジェクトに一時属性を追加します。エンティティ・オブジェクトのJavaクラスを使用するか、Groovy式を使用して一時属性の計算属性を定義します。
基礎となる表内の列にマップされる属性のみでなく、エンティティ・オブジェクトには、値ホルダーである一時属性や(JavaやGroovyなどを使用して)計算された値を表示する一時属性を組み込むことができます。たとえば、FullName
などの作成した一時属性は、FirstName
属性の値とLastName
属性の値を連結した値に基づいて計算できます。
一時属性を作成すると、エンティティ・オブジェクトのJavaクラスで計算を実行したり、属性定義でGroovy式を使用してデフォルト値を指定できます。
実行時に値を計算する方法を変更できるようにする必要がある場合は、Groovy式を使用します。値を計算する方法が変更される可能性が低い場合は(明細項目の合計など)、エンティティ・オブジェクトのJavaクラス内で直接計算できます。
一時属性を作成するには、概要エディタの「属性」ページを使用します。
始める前に:
一時属性および計算属性の使用に関する知識があると役立ちます。詳細は、「エンティティ・オブジェクトへの一時属性および計算属性の追加」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトに一時属性を追加するには:
一時属性を追加すると、エンティティ・オブジェクトのXMLドキュメントが更新され、新しい属性が反映されます。次の例に示すように、一時属性の<Attribute>
タグには、TableName
またはColumnName
はありません。
<Attribute Name="FullName" IsUpdateable="false" IsQueriable="false" IsPersistent="false" Type="java.lang.String" SQLType="VARCHAR" > </Attribute>
これに対し、永続的エンティティ属性には、次の例のようにTableName
とColumnName
の両方があります。
<Attribute Name="FirstName" IsNotNull="true" Precision="30" ColumnName="FIRST_NAME" Type="java.lang.String" ColumnType="VARCHAR2" SQLType="VARCHAR" TableName="USERS" > </Attribute>
一時属性を作成する場合、デフォルト値の指定にGroovy式を使用できます。
始める前に:
一時属性および計算属性の知識があると役立ちます。詳細は、「エンティティ・オブジェクトへの一時属性および計算属性の追加」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
Groovy式に基づいて一時属性を作成するには:
注意:
値式またはオプションの再計算式がベース・エンティティ・オブジェクトの属性を参照する場合は、これを依存性として定義する必要があります。式を定義したときに「式エディタの編集」ダイアログで依存性を指定しなかった場合は、「依存性」タブ(「属性」ページ)でそれを実行できます。「依存性」タブで、「使用可能」リストで属性を特定し、それぞれを「選択済」リストに移動します。
一時属性がGroovy式に準拠している場合は、次の例に示すように<TransientExpression>
タグがエンティティ・オブジェクトのXMLファイルの適切な属性内に追加されます。
<Attribute Name="QuantityTotal" ColumnName="QUANTITY_TOTAL" ... <TransientExpression Name="ExpressionScript" trustMode="untrusted" CodeSourceName="ItemEORow"/> <Dependencies> <Item Value="Price"/> <Item Value="QuantityShipped"/> </Dependencies> </Attribute>
一時属性は、データ値のプレースホルダです。一時属性の「更新可能」プロパティを「新規の間」または「常に」に変更すると、エンド・ユーザーは属性値を入力できるようになります。一時属性に計算値を表示する場合は通常、「更新可能」プロパティを「なし」に設定し、値を計算するカスタムJavaコードを記述します。
エンティティ・オブジェクトに一時属性を追加した後、この属性計算属性にするには、次のようにする必要があります。
概要エディタの「Java」ページでカスタム・エンティティ・オブジェクト・クラスを有効にし、アクセッサ・メソッドの生成を選択します。
一時属性のアクセッサ・メソッドの内部で、計算済値を戻すJavaコードを記述します。
「属性」ページの「依存性」タブで、一時属性の依存属性をそれぞれ指定します。
たとえば、ビュー行クラスを生成した後、一時属性の計算値を戻すJavaコードは、次の例のように属性のgetterメソッド(FullName
など)にあります。
// Getter method for FullName calculated attribute in UserImpl.java public String getFullName() { // Commented out original line since we'll always calculate the value // return (String)getAttributeInternal(FULLNAME); return getFirstName()+" "+getLastName(); }
エンド・ユーザーによって、連結される属性(LastName
やFirstName
など)が変更されるたびに、一時属性が再評価されるようにするには、一時属性の依存属性を指定します。コード内で計算を実行する場合でも、依存属性の値が変更されたときにUIをリフレッシュする適切なイベントを起動するには依存性が必要です。依存属性を指定するには、「属性」ページの「依存性」タブで、「使用可能」リストで属性を特定し、それぞれを「選択済」リストに移動します。
ビジネス・イベントを定義してADFモデル・レイヤーから公開したり、Oracle Mediator経由で外部システムと同期できます。
モデル・レイヤーから呼び出されたビジネス・イベントは、ビジネス・プロセスを起動する際や外部システム同期化をトリガーする際に役立ちます。
モデル・レイヤーから呼び出されたビジネス・イベントは、ビジネス・プロセスを起動する際や、Oracle SOA SuiteのコンポーネントであるOracle Mediator経由で外部システム同期化をトリガーする際に役立ちます。
注意:
Oracle Mediatorは、軽量なフレームワークを提供し、コンポジット・アプリケーション内の様々なコンポーネントを仲介します。また、様々なコンポーネントで公開される様々なインタフェース間での通信が容易になるようにデータを変換します。これらのコンポーネントは接続されて、SOAコンポジット・アプリケーションが構築されます。たとえば、Oracle Mediatorでは、アプリケーションまたはサービスからテキスト・ファイルに格納されたデータを受け入れ、顧客リポジトリとして使用するデータベースの更新に適した書式に変換し、変換したデータをそのデータベースにルーティングおよび配信できます。
Oracle Mediatorでは、ビジネス・イベントをアクションにマッピングする宣言的サブスクリプションがサポートされています。つまり、あるコンポーネントでビジネス・イベント(新規顧客作成など)を定義して公開した後、別のコンポーネントでこのイベントをサブスクライブすると、このビジネス・プロセスが発生したときに通知されます。次に、サブスクライブ中のコンポーネントで、そのイベントに割り当てているアクション(新規顧客への挨拶メールの送信など)を実行できます。
ビジネス・イベントはエンティティ・レベルで宣言的に定義します。また、それらのイベントを呼び出す条件を指定できます。指定の基準を満たすビジネス・イベントは、変更されたデータが正常にコミットされたときに呼び出されます。ビジネス・イベントは、エンティティ・オブジェクトが正常に作成、更新または削除されたときにMediatorに発行されます。
ビジネス・イベントを実装するには、次の処理を実行します。
「ビジネス・イベントの作成方法」の説明に従ってイベント定義を作成します。
「ビジネス・イベントの公開ポイントの定義方法」の説明に従って、イベント定義をイベント・ポイントにマップし、そのイベント定義を公開します。
「ビジネス・イベントをサブスクライブする方法」の説明に従って、ビジネス・イベントが公開されたら、別のコンポーネントからそのイベントをサブスクライブできます。
イベント定義には、イベント・システムMediatorで公開および呼び出されるイベントが記述されます。イベント定義は、表4-2に示す要素とともに、エンティティ・オブジェクトのXMLファイルに格納されます。
表4-2 エンティティ・オブジェクトのイベント定義要素
要素 | 説明 |
---|---|
イベント名 |
イベントの名前( |
ペイロード |
サブスクライバに送信される属性のリスト。オプションとしてマークされた属性は、変更された場合のみペイロードに表示されます。 |
イベント・ポイントは、イベントを呼び出せる場所です。コミットが正常に行われた時点で、表4-3に示すいずれかのイベント・ポイントをトランザクションのエンティティごとにMediatorに呼び出せます。
表4-3 Mediatorに呼び出されたイベント・ポイントの例
DML型 | イベント名 | イベントの説明 |
---|---|---|
CREATE |
EntityCreated |
新しいエンティティが作成されました。 |
UPDATE |
EntityUpdated |
既存のエンティティが更新されました。 |
DELETE |
EntityDeleted |
既存のエンティティが削除されました。 |
イベントはデフォルトで呼び出されることはなく、すべてのイベントがカスタムです。イベントを作成する場合、名前とDML操作を適宜指定できます。
イベント・ポイントごとに、どのイベント定義を特定のイベント・ポイントで呼び出すかを指定する必要があります。つまり、各イベント定義をイベント・ポイントに宣言的にマッピングする必要があります。
イベント・デリバリは現在のデータベース・トランザクションの外部で発生し、非同期です。そのため、次がサポートされています。
イベント配信がトランザクションの一部であるトランザクション・イベント配信は、このフレームワークではサポートされていません。
サブスクライバがイベントの受信を確認するまで公開元がその後の処理を待機する同期化イベントは、このフレームワークではサポートされていません。
ビジネス・イベントを作成するには、概要エディタの「ビジネス・イベント」ページを使用します。
始める前に:
ビジネス・イベントの動作方法に関する知識が役立つ場合があります。詳細は、「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・イベントを作成する手順は、次のとおりです。
定義するビジネス・イベントごとにこの手順を繰り返します。イベントを公開するには、「ビジネス・イベントの公開ポイントの定義方法」を参照してください。
ビジネス・イベントを作成すると、次の例に示すように、エンティティ・オブジェクトのXMLファイルがイベント定義により更新されます。
<EventDef Name="CustBusEvent1"> <Payload> <PayloadItem AttrName="Id"/> <PayloadItem AttrName="PaymentOptionId"/> <PayloadItem AttrName="PaymentTypeEO.Id" SendOnlyIfChanged="true"/> </Payload> </EventDef>
またJDeveloperでは、イベント・スキーマの関連XSDファイルおよびエンティティ・オブジェクトのイベント定義(EDL)ファイルが生成されます。次の例に示すように、EDLファイルにはイベント定義があります。
<definitions targetNamespace="http://oracle/summit/model/entities/events/edl/OrdEO" xmlns:ns0="http://oracle/summit/model/entities/events/schema/OrdEO" xmlns="http://schemas.oracle.com/events/edl"> <schema-import namespace="http://oracle/summit/model/entities/events/schema/OrdEO" location="OrdEO.xsd"/> <event-definition name="CustBusEvent1"> <content element="ns0:CustBusEvent1Info"/> </event-definition> </definitions>
XSDファイルでは、必須の属性とオプションの属性を指定できます。必須属性は、「送信された値」-「常に」に設定されたもので、オプション属性は、「送信された値」を「変更された場合のみ」に変更されたものです。
次の例は、ビジネス・イベントのXSDイベント・スキーマを示しています。
<?xml version = '1.0' encoding = 'UTF-8'?> <xs:schema targetNamespace="http://oracle/summit/model/entities/events/schema/OrdEO" xmlns="http://oracle/summit/model/entities/events/schema/OrdEO" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="CustBusEvent1Info"> <xs:complexType> <xs:sequence> <xs:element name="Id" type="DecimalValuePair" minOccurs="1"/> <xs:element name="PaymentOptionId" type="IntValuePair" minOccurs="1"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="ValuePair" abstract="true"/> <xs:complexType name="IntValuePair"> <xs:complexContent> <xs:extension base="ValuePair"> <xs:sequence> <xs:element name="newValue" minOccurs="0"> <xs:complexType> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="value" type="xs:int"/> </xs:extension> </xs:complexContent> </xs:complexType> </xs:element> <xs:element name="oldValue" minOccurs="0"> <xs:complexType> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="value" type="xs:int"/> </xs:extension> </xs:complexContent> </xs:complexType> </xs:element> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="DecimalValuePair"> <xs:complexContent> <xs:extension base="ValuePair"> <xs:sequence> <xs:element name="newValue" minOccurs="0"> <xs:complexType> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="value" type="xs:decimal"/> </xs:extension> </xs:complexContent> </xs:complexType> </xs:element> <xs:element name="oldValue" minOccurs="0"> <xs:complexType> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="value" type="xs:decimal"/> </xs:extension> </xs:complexContent> </xs:complexType> </xs:element> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema>
ビジネス・イベントのペイロードは、関連付けられたエンティティ・オブジェクトの属性で構成されます。ビジネス・イベントのペイロード属性は、イベントの作成者によって定義されます。自動的に最適化されるわけではありません。イベントを定義するときに、属性を「常に」または「変更された場合のみ」送信するとしてマークできます。作成時に起動されるイベントでは、新しい値のみが送信されます。更新または削除時に起動されるイベントでは、新規および既存の値が送信されますが、「送信された値」設定に基づいて該当の属性のみです。最高のパフォーマンスを得るには、削除イベントでは主キー属性のみを含める必要があります。
コンポジット・シナリオ(発注書と明細項目など)をサポートするために、子エンティティは親エンティティで定義されたイベントを呼び出すことができ、子エンティティで定義されたイベントには親エンティティからの属性を含めることができます。子エンティティが親エンティティのイベントを呼び出す場合、子エンティティによるイベントの呼出し回数に関係なく、トランザクションごとに特定のトップレベル・エンティティについて1つのイベントのみが呼び出されます。
エンティティ・サブタイプのケースでは(たとえば、Staff
エンティティ・オブジェクトはPersons
エンティティのサブタイプです)、ADFビジネス・コンポーネントではビジネス・イベントのオーバーライドはサポートされません。ビジネス・イベントのサブスクライバは、イベント名を使用してイベントをリスンするため、イベントをオーバーライドすると、イベントのサブスクライバは自分に対するものではないペイロード・データを受信してしまう可能性があります。したがって、この機能はサポートされていません。
ビジネス・イベントを定義する際は、ClobDomain
属性はサポートされていますが、非常に大きなCLOBデータはパフォーマンスに影響が与える可能性があることを考慮してください。
ビジネス・イベントの公開ポイントを定義するには、エンティティ・オブジェクトの概要エディタの「ビジネス・イベント」ページを使用します。
始める前に:
ビジネス・イベントがアプリケーションで使用される方法に関する知識が役立つ場合があります。詳細は、「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
「ビジネス・イベントの作成方法」の説明に従って、イベント定義が公開前に作成されている必要があります。
ビジネス・イベントの公開ポイントを定義する方法:
ビジネス・イベントを作成したら、そのイベントをサブスクライブし、応答することができます。
注意:
ビジネス・イベントをサブスクライブしたり、JDeveloperでSOAコンポジット・アプリケーションとプロジェクトを作成してデプロイしたりするには、Oracle SOA Suite拡張機能をインストールする必要があります。この拡張機能をJDeveloperにインストールする手順は、『Oracle JDeveloperのインストール』のOracle JDeveloper拡張機能の有効化に関する項を参照してください。Oracle SOA Suiteの機能の詳細は、『Oracle SOA SuiteでのSOAアプリケーションの開発』の「Oracle SOA Suiteを使用したアプリケーション作成の概要」を参照してください。
始める前に:
ビジネス・イベントに関する知識が役立つ場合があります。詳細は、「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを完了する必要があります。
「ビジネス・イベントの公開ポイントの定義方法」の説明に従ってビジネス・イベントを公開します。
ビジネス・イベントをサブスクライブするには:
ADFエンティティ・オブジェクトで宣言型ランタイム機能を使用して実現できないカスタム・ビジネス・ロジックを実装するには、カスタムJavaの生成を有効にします。
この章でこれまで説明してきたように、エンティティ・オブジェクトのデータベース対話機能および多くの宣言的実行時機能はすべて、カスタムJavaコードを使用せずに実現できます。これらの宣言的機能を超えてエンティティにカスタム・ビジネス・ロジックを実装する必要がある場合、カスタム・コードを必要とするエンティティに対してJava生成を有効化する必要があります。通常、カスタム・エンティティ・オブジェクトおよびエンティティ定義クラスで記述、使用およびオーバーライドする一般的なコードのクイック・リファレンスは、「ADFビジネス・コンポーネントのよく使用されるメソッド」を参照してください。
ベスト・プラクティス:
ADFビジネス・コンポーネントを初めて使用する開発者には、ベース・フレームワーク・クラスをオーバーライドして、すべての生成されたカスタムJavaクラスがカスタマイズされた動作を選択できるようにすることをお薦めします。この方法は、個々のエンティティ・オブジェクトおよびビュー・オブジェクトのカスタム・クラスの作成より優先されます。カスタム・コンポーネントを生成するときは、「プロジェクト・プロパティ」ダイアログの「ADFビジネス・コンポーネント」→「ベース・クラス」ページで、フレームワーク・クラスをオーバーライドするように指定します。「ビジネス・コンポーネント機能の拡張」を参照してください。
エンティティ・オブジェクトのカスタムJavaクラスの生成を有効にするには、概要エディタの「Java」ページを使用します。
始める前に:
カスタムJavaクラスに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトのカスタムJavaクラスの生成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトのカスタムJavaクラスを生成するには:
生成するカスタムJavaクラスを1つ以上選択すると、指定したJavaファイルが作成されます。たとえば、summit.model.entities.OrdEO
という名前のエンティティ・オブジェクトの場合、そのカスタムJavaファイルのデフォルト名は、エンティティ・オブジェクト・クラスに対してはOrdEOImpl.java
、エンティティ定義クラスに対してはOrdEODefImpl.java
になります。これらのファイルは両方とも、コンポーネントのXMLドキュメント・ファイルと同じ../summit/model/entities
ディレクトリに作成されます。
エンティティ・オブジェクトのJava生成オプションは、概要エディタの「Java」ページに後でアクセスしてもそのまま反映されています。XML定義ファイルの場合と同様、このエディタでどのような変更を行っても、カスタムJavaクラスで生成されたコードは最新の状態に保たれます。後でなんらかの理由によりカスタムJavaファイルが必要なくなった場合は、「Java」ページで関連するオプションを無効にすると、カスタムJavaファイルを削除できます。
カスタム・エンティティ・オブジェクト・クラスの生成を有効にする場合、「アクセッサ」オプションも有効にすると、エンティティ・オブジェクトの属性ごとにgetterメソッドおよびsetterメソッドが生成されます。
注意:
最終的なハードコードされたメソッド名によりエンティティ行とビュー行の最適化またはカスタマイズが制限されるため、アクセッサはビジネス・ニーズ(ビュー行をJavaインタフェースとしてクライアントに公開する必要があるなど)がある場合のみ生成します。
たとえば、OrdEO
エンティティ・オブジェクトには、対応するカスタム・クラスOrdEOImpl.java
があり、(次の例に示すような)メソッドがあります。
public DBSequence getId() { ... } public void setId(DBSequence value) { ... } public Date getDateOrdered() { ... } public void setDateOrdered(Date value) { ... } public Integer getPaymentTypeId() { ... } public void setPaymentTypeId(Integer value) { ... } public Number getCustomerId() { ... } public void setCustomerId(Number value) { ... } public String getOrderFilled() { ... } public void setOrderFilled(String value) { ... }
これらのメソッドを使用して行データを操作すると、コンパイル時にデータ型の使用方法が正しいかどうかがチェックされます。つまり、CustomerId
属性の値を取得するために次のようなコードを記述するかわりに、
Number customerId = (Number)order.getAttribute("CustomerId");
次のようなコードを記述できます。
Number customerId = order.getCustomerId();
後者の方法では、CustomerId
ではなく誤ってCustomerCode
と入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error Number customerId = order.getCustomerCode();
エンティティ・オブジェクトのアクセッサ・メソッドが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both attribute name and type cast are wrong, but compiler cannot catch it String customerId = (String)order.getAttribute("CustomerCode");
これには、スペルが正しくない属性名とキャストの型が誤っている戻り値getAttribute()
が両方とも含まれています。EntityImpl
ベース・クラスが実装するRow
インタフェース上で汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
図4-16のように、カスタムJavaクラスの生成を有効にした場合、これらは、エンティティ・オブジェクトの「アプリケーション・ソース」ノードの下にも子ノードとして表示されます。すべてのADFコンポーネントと同様、「アプリケーション」ウィンドウでエンティティ・オブジェクトを選択すると、「構造」ウィンドウには、エンティティの構造ビューが表示されます。カスタムJavaファイルのソース・コードを表示または操作する必要がある場合、ソース・エディタでファイルを開く方法は複数あります。
図4-16のように、Javaファイルを右クリックし、「開く」を選択します。
「構造」ウィンドウでノードの項目を右クリックし、「ソースへ移動」を選択します。
概要エディタのJavaページのリンクをクリックできます。
図4-16 エンティティ・オブジェクトのカスタムJavaクラスの表示および操作
JDeveloperで生成されるカスタムJavaクラスにより、エンティティ・オブジェクトのベース・クラスを拡張して、生成コードの整合性を維持しながらカスタム・コードを実装できる柔軟性があります。次の各項では、カスタムJavaクラスの詳細について説明しています。
XML専用エンティティ・オブジェクトを使用する場合、実行時には、その機能はデフォルトのADFビジネス・コンポーネント実装クラスによって提供されます。生成される各カスタムJavaクラスによって適切なADFビジネス・コンポーネント・ベース・クラスが拡張されるため、コードでは、デフォルトの動作を継承し、このクラスを簡単に追加またはカスタマイズできます。エンティティ・オブジェクト・クラスはEntityImpl
を拡張し、エンティティ定義クラスはEntityDefImpl
を拡張し、エンティティ・コレクション・クラスはEntityCache
を拡張します。これらのベース・クラスはoracle.jbo.server
パッケージ内にあります。
開発者によっては、生成されたJavaソース・ファイルに独自のコードを追加することを躊躇する場合があります。JDeveloperによって作成および管理される各カスタムJavaソース・コード・ファイルには、独自のカスタム・コードをこのファイルに追加しても安全であることを示す、次のようなコメントがファイルの上部に記載されています。
// --------------------------------------------------------------------- // --- File generated by ADF Business Components Design Time. // --- Custom code may be added to this class. // --- Warning: Do not modify method signatures of generated methods. // ---------------------------------------------------------------------
「編集」ダイアログで「OK」または「適用」ボタンをクリックしても、ファイルが知らぬ間に再生成されることはありません。かわりに、管理が必要なメソッドはスマートに更新され、独自のカスタム・コードはそのまま残されます。
ビュー・オブジェクトの実行時動作をカスタマイズする必要がある場合や、バインド変数またはビュー行属性へ強く型付けされたアクセスのみ行う場合、ビュー・オブジェクトのカスタムJavaクラスを生成できます。
ADFビジネス・コンポーネントのカスタムJava生成のデフォルト設定を構成するには、メイン・メニューから「ツール」→「プリファレンス」を選択して、「ビジネス・コンポーネント」ページを開き、後で作成するビジネス・コンポーネントで使用するプリファレンスを設定します。ADFビジネス・コンポーネントを初めて使用する開発者には、カスタムJavaクラスを生成しないよう設定することをお薦めします(デフォルト設定)。カスタムJavaコードが必要な状況になった場合、そのコンポーネントに必要なカスタムJavaコードのみを有効にできます。経験を積むにつれ、どのようなデフォルト設定の組合せが最適であるかがわかるようになります。
エンティティ・オブジェクトは、XMLのみ、またはカスタムJavaクラスと組み合せたXMLドキュメントに基づいて機能するように設計されています。このどちらとして使用するかを選択できるように、属性値はエンティティのクラス(XML専用モードでは存在しないファイル)のプライベート・メンバー・フィールドには格納されません。デフォルトでは、実行時にリフレクションを使用して属性列挙が割り当てられ、属性アクセッサが起動されます。
プロジェクトに対してリフレクションを無効にすると、属性には、エンティティのXMLドキュメント・ファイル内の<Attribute>
およびアソシエーション関連の<AccessorAttribute>
タグのゼロベースの順序に基づいて、このファイルの名前と数値索引が割り当てられます。実行時には、エンティティ行の属性値は、エンティティの属性リスト内における属性の数値的位置による索引が付けられた状態で、EntityImpl
ベース・クラスによって管理される疎配列構造に格納されます。
注意:
リフレクションを無効にするには、「プロジェクト・プロパティ」ダイアログの「オプション」ページにある(「ADFビジネス・コンポーネント」の下の)「リフレクションを使用して属性アクセサを起動」の選択を解除します。
多くの場合、このプライベート実装に関する詳細は、エンティティ・オブジェクトを使用する開発者は理解する必要がないため、重要ではありません。ただし、エンティティ・オブジェクトのカスタムJavaクラスを有効にする場合、エンティティ・オブジェクト・クラスで管理される生成コードの一部にこの実装の詳細が関わってきます。この場合、コードの使用目的を理解している方が賢明です。たとえば、Orders
エンティティ・オブジェクトのカスタムJavaクラスの場合、属性またはアクセッサ属性ごとに対応する生成済の整数列挙があります。JDeveloperでは、これらの定数の値がXMLドキュメント内の属性の順序を正しく反映しているかどうかが確認されます。
また、自動的に管理される、エンティティ・オブジェクト・クラスで強く型付けされたgetterメソッドおよびsetterメソッドでは、これらの属性列挙は次の例のように使用されます。
// In oracle.summit.model.appmodule.OrdersImpl class /** * Gets the attribute value for DateOrdered, using the alias name DateOrdered. * @return the value of DateOrdered */ public Date getDateOrdered() { return (Date)getAttributeInternal(DATEORDERED); } /** * Sets <code>value</code> as the attribute value for DateOrdered. * @param value to set the DateOrdered */ public void setDateOrdered(Date value) { setAttributeInternal(DATEORDERED, value); }
エンティティ属性定数に関連する管理コードのもう1つの側面は、getAttrInvokeAccessor()
およびsetAttrInvokeAccessor()
メソッドです。これらのメソッドにより、数値索引を使用した属性アクセスのパフォーマンスが最適化され、汎用処理の実行時にEntityImpl
ベース・クラスの汎用コードが属性値にアクセスするときの通常のパフォーマンスと同じになります。getAttrInvokeAccessor()
メソッドの例を次の例に示します。もう1つのsetAttrInvokeAccessor()
メソッドの場合も同様です。
// In oracle.summit.model.appmodule.OrdersImpl class /** * getAttrInvokeAccessor: generated method. Do not modify. * @param index the index identifying the attribute * @param attrDef the attribute * @return the attribute value * @throws Exception */
protected Object getAttrInvokeAccessor(int index, AttributeDefImpl attrDef) throws Exception { if ((index >= AttributesEnum.firstIndex()) && (index < AttributesEnum.count())) { return AttributesEnum.staticValues()[index - AttributesEnum.firstIndex()].get(this); } return super.getAttrInvokeAccessor(index, attrDef); }
属性と索引に関連して生成されたこのコードに関する経験則は、次のとおりです。
必要に応じて、強く型付けされた属性のgetterメソッドおよびsetterメソッドの内部にカスタム・コードを追加してください。
概要エディタを使用して、エンティティ・オブジェクト属性の順序または型を変更してください。
getterメソッドおよびsetterメソッドのJavaシグネチャや関連するXMLドキュメントは、自動的に変更されます。
getAttrInvokeAccessor()
メソッドおよびsetAttrInvokeAccessor()
メソッドは変更しないでください。
属性索引番号の値は手動で変更しないでください。
注意:
ソース・コントロールのマージ競合やその他の理由により、生成された属性定数を手動で編集する必要がある場合、対応するエンティティ・オブジェクトのXMLドキュメントの<Attribute>
および<AccessorAttribute>
タグの順序がゼロベースの順序に反映されていることを確認する必要があります。
カスタム・エンティティ・クラスを参照する強く型付けされたエンティティを使用する場合と汎用エンティティ参照で作業する場合の違いをより明確に評価するには、次の例を検討してください。次の例は、カスタム・アプリケーション・モジュール・クラス(AppModuleImpl.java
)内の強く型付けされたエンティティ参照のバージョンの1つを示しています。重要な違いは、次のとおりです。
属性アクセスは、強く型付けされた属性アクセッサを使用して実行されます。
アソシエーションのアクセッサ属性により、アソシエーションの反対側にある強く型付けされたエンティティ・クラスが戻されます。
カスタム・エンティティ・クラスのgetDefinitionObject()
メソッドを使用すると、完全修飾されたエンティティ定義名を文字列として使用しないようにできます。
カスタム・エンティティ・クラスのcreatePrimaryKey()
メソッドにより、エンティティのKey
オブジェクトの作成を簡略化します。
package oracle.summit.model.appmodule.service; import oracle.jbo.ApplicationModule; import oracle.jbo.JboException; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSetIterator; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.DBSequence; import oracle.jbo.domain.Number; import oracle.jbo.server.EntityDefImpl; import oracle.jbo.server.EntityImpl; import oracle.jbo.server.ViewLinkImpl; import oracle.summit.base.SummitApplicationModuleImpl; import oracle.summit.base.SummitViewObjectImpl; import oracle.summit.model.appmodule.CustomerImpl; import oracle.summit.model.appmodule.OrdersImpl; import oracle.summit.model.appmodule.service.common.AppModule; // --------------------------------------------------------------------- // --- File generated by Oracle ADF Business Components Design Time. // --- Custom code may be added to this class. // --- Warning: Do not modify method signatures of generated methods. // --------------------------------------------------------------------- public class AppModuleImpl extends SummitApplicationModuleImpl implements AppModule { /* This is the default constructor (do not remove). */ public AppModuleImpl() { } private OrdersImpl retrieveOrderById(long orderId) { EntityDefImpl orderDef = OrdersImpl.getDefinitionObject(); Key orderKey = OrdersImpl.createPrimaryKey(new DBSequence(orderId)); return (OrdersImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey); } public String findOrderAndCustomer(long orderId) { OrdersImpl order = retrieveOrderById(orderId); if (order != null) { CustomerImpl cust = order.getCustomer(); if (cust != null) { return "Customer: " + cust.getName() + ", Location: " + cust.getCity(); } else { return "Unassigned"; } } else { return null; } } /* Find an Order by Id */ public String findOrderTotal(long orderId) { OrdersImpl order = retrieveOrderById(orderId); if (order != null) { return order.getTotal().toString(); } return null; } /* Update the status of an existing order */ public void updateOrderStatus(long orderId, String newStatus) { OrdersImpl order = retrieveOrderById(orderId); if (order != null) { order.setOrderFilled(newStatus); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } } } /* Create a new Customer and Return the new id */ public long createCustomer(String name, String city, Integer countryId) { EntityDefImpl customerDef = CustomerImpl.getDefinitionObject(); CustomerImpl newCustomer = (CustomerImpl)customerDef.createInstance2(getDBTransaction(),null); newCustomer.setName(name); newCustomer.setCity(city); newCustomer.setCountryId(countryId); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } DBSequence newIdAssigned = newCustomer.getId(); return newIdAssigned.getSequenceNumber().longValue(); } /* Custom method in an application module implementation class */ public void doSomeCustomProcessing() { ViewObject vo = getCustomerView1(); // create secondary row set iterator with system-assigned name RowSetIterator iter = vo.createRowSetIterator(null); while (iter.hasNext()) { Row r = iter.next(); // Do something with the current row. Integer custId = (Integer)r.getAttribute("Id"); String name = (String)r.getAttribute("Name"); System.out.println(custId + " " + name); } // close secondary row set iterator iter.closeRowSetIterator(); } /* Container's getter for CustomerView1. * @return CustomerView1 */ public SummitViewObjectImpl getCustomerView1() { return (SummitViewObjectImpl) findViewObject("CustomerView1"); } /* Container's getter for ItemView1. * @return ItemView1 */ public SummitViewObjectImpl getItemView1() { return (SummitViewObjectImpl) findViewObject("ItemView1"); } /* Container's getter for OrderView1. * @return OrderView1 */ public SummitViewObjectImpl getOrderView1() { return (SummitViewObjectImpl) findViewObject("OrderView1"); } /* Container's getter for ItemView2. * @return ItemView2 */ public SummitViewObjectImpl getItemView2() { return (SummitViewObjectImpl) findViewObject("ItemView2"); } /* Container's getter for OrderView2. * @return OrderView2 */ public SummitViewObjectImpl getOrderView2() { return (SummitViewObjectImpl) findViewObject("OrderView2"); } /*Container's getter for SItemOrdIdFkLink1. * @return SItemOrdIdFkLink1 */ public ViewLinkImpl getSItemOrdIdFkLink1() { return (ViewLinkImpl) findViewLink("SItemOrdIdFkLink1"); } /* Container's getter for SOrdCustomerIdFkLink1. * @return SOrdCustomerIdFkLink1 */ public ViewLinkImpl getSOrdCustomerIdFkLink1() { return (ViewLinkImpl) findViewLink("SOrdCustomerIdFkLink1"); } }
JDeveloperでは、ADFアプリケーション・モジュールにアクセスし、外部クライアント・プログラムを使用してADFデータ・モデルで直接作業できます。
エンティティ・オブジェクトを直接操作する場合、UIベースのクライアントまたはプログラム的クライアントは常に必要ではありません。アプリケーション・モジュールにアクセスし、そのデータ・モデル内のビュー・オブジェクトを直接操作する際、外部クライアント・プログラム以外には使用する必要がない場合もあります。「ビュー・オブジェクトを使用したSQL問合せの定義」では、強力なアプリケーションを構築するために、ビュー・オブジェクトの柔軟なSQL問合せ機能に、エンティティ・オブジェクトのビジネス・ロジックとデータベースとの自動対話機能とを組み合せる簡単な方法について説明します。この組合せにより、現在のエンド・ユーザーのタスク上のニーズに対応するよう設計され、再使用可能なドメイン・ビジネス・オブジェクト・レイヤー内で一元管理されたビジネス・ロジックを共有する、完全に更新可能なアプリケーション・モジュールのデータ・モデルを作成できます。
ただし、まず重要なのは、ビュー・オブジェクトとエンティティ・オブジェクトを組み合せた能力の利用方法について学習する前に、これらのオブジェクト自体の使用方法について理解することです。これらのオブジェクトについて詳細に学習することにより、これらを単体で使用する必要があるときと、独自のアプリケーションでこれらを組み合せる必要があるときについて理解が深まります。
クライアントはエンティティ・オブジェクトを直接操作できないため、エンティティ・オブジェクトをプログラム的に操作するために記述するコードは、通常カスタム・アプリケーション・モジュール・クラス内または別のエンティティ・オブジェクトのカスタム・クラス内のカスタム・コードになります。
エンティティ行にアクセスするには、エンティティ定義と呼ばれる関連オブジェクトを使用します。実行時には、エンティティ・オブジェクトごとに対応するエンティティ定義を利用して、エンティティの構造を記述し、それにしたがってエンティティ・オブジェクトのインスタンスを管理します。アプリケーション・モジュールを作成し、そのカスタムJavaクラスを有効化した後に、特定の注文を戻すメソッドを作成する必要があるとします。これは、次のサンプル・コードに示すretrieveOrderById()
メソッドのようになります。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
主キーによるエンティティ・オブジェクトを検索するには:
次のサンプル・コードは、この基本手順を使用して開発したretrieveOrderById()
メソッドを示しています。
/* Helper method to return an Order by Id */ private OrdersImpl retrieveOrderById(long orderId) { EntityDefImpl orderDef = OrdersImpl.getDefinitionObject(); Key orderKey = OrdersImpl.createPrimaryKey(new DBSequence(orderId)); return (OrdersImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey); }
注意:
oracle.jbo.Key
オブジェクト・コンストラクタは、より一般的な単一の属性値のキー以外にも、複数の属性キーの作成もサポートするためのオブジェクト配列を使用できます。
SQLコードを必要としないアクセッサ属性に基づいて、関連付けられたエンティティへアクセスするためのメソッドを作成できます。たとえば、メソッドfindOrderAndCustomer()
によりオーダーを検索してから、オーダーに割り当てられた顧客を表す、関連付けられたCustomer
エンティティ・オブジェクトにアクセスします。アソシエーションを使用して1つのエンティティ・オブジェクトから別のエンティティ・オブジェクトへ簡単にアクセスする方法の詳細は、「アソシエーションの作成と構成」を参照してください。
同じアクセッサ属性を使用して同じ関連付けられたエンティティを検索するアプリケーション・モジュールの既存メソッドとの競合を防ぐため、この機能をヘルパー・メソッドにリファクタし、必要に応じてアプリケーション・モジュールの任意の場所で再使用できます。たとえば、次のサンプル・コードに示すように、retrieveOrderById()
メソッドを作成して注文を検索する機能をリファクタできます。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
アクセッサ属性を使用して関連付けられたエンティティ・オブジェクトにアクセスするには:
関連するCustomer
エンティティにアクセスするためにSQLを記述する必要はありません。Orders
エンティティ・オブジェクトとCustomer
エンティティ・オブジェクト間のADFアソシエーションで取得された関連情報のみで、データ・ナビゲーションの一般的なタスクを自動化できます。
次の例は、ヘルパー・メソッドを使用するfindOrderCustomer()
のコードを示しています。
/* Access an associated Customer entity from the Order entity */ public String findOrderAndCustomer(long orderId) { OrdersImpl order = retrieveOrderById(orderId); if (order != null) { CustomerImpl cust = order.getCustomer(); if (cust != null) { return "Customer: " + cust.getName() + ", Location: " + cust.getCity(); } else { return "Unassigned"; } } else { return null; } }
エンティティ行を取得した後、エンティティ行は簡単に更新または削除できます。たとえば、このタスクは、次のサンプル・コードに示すupdateOrderStatus()
のようなメソッドを追加することにより実行できます。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ行を更新するには:
エンティティ行の削除のコードもこれと同じですが、既存のエンティティを検索した後、トランザクションをコミットする前に、次の行を使用してエンティティを削除する点が異なります。
// Remove the entity instead order.remove();
エンティティ定義を使用して既存のエンティティ行を検索する以外にも、エンティティ定義を使用して新しいエンティティ行を作成することもできます。Customerエンティティの場合、次の例のようなcreateCustomer()
メソッドを記述することにより、新しい顧客の名前や説明を取得し、この顧客に割り当てられた新しい顧客IDを戻すことができます。この例は、Customer
エンティティ・オブジェクトのId
属性がすでに更新され、その型がDBSequence
型になっていること(「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照)が前提となります。この設定により、アプリケーション・スキーマの表の順序から割り当てられたトリガーの値が、対応するデータベース表から反映されるように属性値がリフレッシュされます。
/* Create a new Customer and Return the new id */ public long createCustomer(String name, String city, Integer countryId) { EntityDefImpl customerDef = CustomerImpl.getDefinitionObject(); CustomerImpl newCustomer = (CustomerImpl)customerDef.createInstance2(getDBTransaction(),null); newCustomer.setName(name); newCustomer.setCity(city); newCustomer.setCountryId(countryId); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } DBSequence newIdAssigned = newCustomer.getId(); return newIdAssigned.getSequenceNumber().longValue(); }
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ行を作成するには:
新規行を作成する場合、トリガーにより割り当てられた値を使用する(「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照)かわりに、Oracle順序を使用して値を主キーに割り当てることができます。このメタデータ駆動型の方法を使用すると、主キーを取得するためのコードの管理を、複数のエンティティ・オブジェクトにより再使用可能なJavaファイルに一元化できます。
次の例は、エンティティ・オブジェクトが基づくCustomEntityImpl
という簡単なフレームワーク拡張クラスを示しています。このクラスのオーバーライドされたcreate()
メソッドにより、SequenceName
という名前のカスタム属性レベルのメタデータ・プロパティが存在するかどうかがテストされ、検出された場合は、その順序内の次の番号から属性のデフォルト値が移入されます。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
Oracle順序を使用して主キー値を割り当てるには:
package sample; import oracle.jbo.AttributeDef; import oracle.jbo.AttributeList; import oracle.jbo.server.EntityImpl; import oracle.jbo.server.SequenceImpl; public class CustomEntityImpl extends EntityImpl { protected void create(AttributeList attributeList) { super.create(attributeList); for (AttributeDef def : getEntityDef().getAttributeDefs()) { String sequenceName = (String)def.getProperty("SequenceName"); if (sequenceName != null) { SequenceImpl s = new SequenceImpl(sequenceName,getDBTransaction()); setAttribute(def.getIndex(),s.getSequenceNumber()); } } } }
監査のため、表に追加した行を表から物理的に削除できない場合があります。そのような場合は、エンド・ユーザーがユーザー・インタフェースで行を削除する際に、DELETED
列の値をN
からY
に変更し、削除済としてマークします。この処理を行うために、2つのメソッドのオーバーライドを使用して、エンティティ・オブジェクトのデフォルト動作を変更できます。
そのためには、次のタスクを実行する必要があります。
「行が削除されたときの削除済フラグの更新」の説明に従って、行が削除されたときに削除済フラグを更新します。
「削除のかわりの更新DML操作の強制」の説明に従って、エンティティ・オブジェクトを削除するのではなく更新します。
行が削除されたときに削除済フラグを更新するには、エンティティ・オブジェクトのカスタムJavaクラスを有効にし、remove()
メソッドをオーバーライドして、super.remove()
メソッドを呼び出す前に削除済フラグを設定するようにします。次の例は、エンティティ・オブジェクトのカスタムJavaクラスが、この変更でどのようになるかを示しています。削除された行の属性を設定しようとするとDeadEntityAccessException
が発生するため、super.remove()
を呼び出す前に属性を設定することが重要です。
この例では、表を変更してDELETED
列を追加し、エンティティ・オブジェクトとデータベースの同期を取って、対応するDeleted
属性を追加してあるものとします。
行は行セットから削除されますが、エンティティ・キャッシュでDeleted
フラグの値はY
に変更されています。この動作の実装の第2の部分は、DML操作の実行を要求されたときに、エンティティにDELETE
のかわりにUPDATE
の実行を強制することです。完全なソリューションのためには、両方の部分を実装する必要があります。
// In your custom Java entity class public void remove() { setDeleted("Y"); super.remove(); }
エンティティ・オブジェクトを削除のかわりに更新するには、doDML()
メソッドをオーバーライドし、operation
フラグを条件付きで変更するコードを記述します。操作フラグがDML_DELETE
の場合は、このコードでかわりにDML_UPDATE
に変更します。次の例は、エンティティ・オブジェクトのカスタムJavaクラスが、この変更でどのようになるかを示しています。
この例では、表を変更してDELETED
列を追加し、エンティティ・オブジェクトとデータベースの同期を取って、対応するDeleted
属性を追加してあるものとします。
オーバーライドしたdoDML()
メソッドを配置し、「行が削除されたときの削除済フラグの更新」で説明したオーバーライドされたremove()
メソッドを補完することで、対応するエンティティ・オブジェクトの慣用名を持つビュー・オブジェクトからエンティティを削除しようとすると、行が物理的に削除されるかわりに、列DELETED
が更新されます。削除された製品がビュー・オブジェクトの問合せ結果に表示されることを防ぐため、WHERE DELETED = 'N'
の製品のみを含むようにWHERE
句を変更する必要があります。
// In your custom Java entity class protected void doDML(int operation, TransactionEvent e) { if (operation == DML_DELETE) { operation = DML_UPDATE; } super.doDML(operation, e); }
データベース制約のため、同じトランザクション内でDML操作を実行して複数の関連するエンティティ・オブジェクトに変更を保存するときは、操作の実行順序が重要になる場合があります。外部キー参照を含む新しい行を、参照されている行を挿入する前に挿入しようとすると、データベースで制約違反が発生する可能性があります。コミット時のエンティティ・オブジェクトのデフォルトの処理順序、および必要に応じてその順序をプログラムで変更する方法を理解している必要があります。
注意:
この項の例は、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.controlpostorder
パッケージを参照しています。
デフォルトでは、トランザクションをコミットすると、保留中の変更リスト内のエンティティ・オブジェクトは、時間順、つまりエンティティがリストに追加された順序で処理されます。つまり、たとえば、新しい従業員(EmpEO
エンティティ・オブジェクト)を作成、その後その製品に関連する新しい部門(DeptEO
エンティティ・オブジェクト)を作成すると、最初に新しいEmpEO
が、2番目に新しいDeptEO
が挿入されます。
2つのエンティティ・オブジェクトがコンポジットで関連付けられていると、厳密な時間順序が自動的に変更されて、コンポジット関係にある親と子のエンティティ行が制約に違反しない順序で保存されることが保証されます。たとえば、コンポジットされる新しい子のエンティティ行より前に、新しい親のエンティティ行が挿入されます。
関係のあるエンティティが関連付けられてはいてもコンポジットではない場合は、コードを少し作成して、関連するエンティティが正しい順序で保存されるようにする必要があります。
次の例のAppModule
アプリケーション・モジュールのnewEmployeeForNewDepartment()
カスタム・メソッドについて考えます。このメソッドは、一連のパラメータを受け取って、次の処理を行います。
Result
Java Beanを作成します。注意:
このコードでは、EmpEO.Id
とDeptEO.Id
には、順序に基づいて主キーを設定するためDBSequence
にデータ型が設定されているものと想定しています。
// In AppModuleImpl.java public Result newEmployeeForNewDepartment(String deptName, Number regionId, String lastName, String firstName, Number salary) { oracle.jbo.domain.Date today = new Date(Date.getCurrentDate()); Number objectId = new Number(0); // 1. Create a new employee EmpEOImpl newEmp = createNewEmp(); // 2. Create a new department DeptEOImpl newDept = createNewDept(); newDept.setName(deptName); newDept.setRegionId(regionId); // 3. Set the department id to which the employee pertains newEmp.setDeptId(newDept.getId().getSequenceNumber()); newEmp.setLastName(lastName); newEmp.setFirstName(firstName); newEmp.setUserid((firstName + "." + lastName).substring(8)); newEmp.setSalary(salary); // 4. Commit the transaction getDBTransaction().commit(); // 5. Construct a bean to hold new department id and employee id Result result = new Result(); result.setEmpId(newEmp.getId().getSequenceNumber()); result.setDeptId(newDept.getId().getSequenceNumber()); // 6. Return the result return result; } private DeptEOImpl createNewDept(){ EntityDefImpl deptDef = DeptEOImpl.getDefinitionObject(); return (DeptEOImpl) deptDef.createInstance2(getDBTransaction(), null); } private EmpEOImpl createNewEmp(){ EntityDefImpl empDef = EmpEOImpl.getDefinitionObject(); return (EmpEOImpl) empDef.createInstance2(getDBTransaction(), null); }
このメソッドをアプリケーション・モジュールのクライアント・インタフェースに追加し、テスト用のクライアント・プログラムからテストすると、エラーが発生します。
oracle.jbo.DMLConstraintException: JBO-26048: Constraint "S_EMP_DEPT_ID_FK" is violated during post operation "Insert" using SQL statement "BEGIN INSERT INTO S_EMP(ID,LAST_NAME,FIRST_NAME,USERID,DEPT_ID,SALARY) VALUES (:1,:2,:3,:4,:5,:6) RETURNING ID INTO :7; END;". : ORA-02291: integrity constraint (SUMMIT_ADF.S_EMP_DEPT_ID_FK) violated - parent key not found
S_EMP
行が挿入されたとき、その外部キーDEPT_ID
の値に対応する行がS_DEPT
表に存在しないというエラーをデータベースが出力します。これは次の理由で発生します。
コードにより、部門の前に従業員が作成された。
エンティティ・オブジェクトEmpEO
とDeptEO
が、関連付けられてはいるがコンポジットではない。
新しいエンティティ行を保存するDML操作が時間順に行われるため、新しいDeptEO
より前に新しいEmpEO
が挿入される。
まだ有効でない部門IDで従業員を追加しようとする問題を修復するには、例のコード行を並べ替えて、最初にDeptEO
を、次にEmpEO
を作成できます。目先の問題にはこれで対処できますが、別のアプリケーション開発者が間違った順序でエンティティを作成する可能性が残っています。
さらによい解決策は、エンティティ・オブジェクト自体がポスト順序を処理するようにして、作成順序に関係なくポストが正しく動作するようにすることです。そのためには、関連するエンティティ・オブジェクトを参照する外部キー属性を含むエンティティのpostChanges()
メソッドをオーバーライドし、次の例に示すようなコードを記述します。この例では、DeptEO
エンティティに対する外部キーが含まれているのはEmpEO
であるため、EmpEO
を更新し、条件によっては、サービス・リクエストをポストする前に関連する新しいDeptEO
を強制的にポストするようにします。
このコードでは、ポストしているエンティティがSTATUS_NEW
状態またはSTATUS_MODIFIED
状態のいずれかであるかを検査します。その場合は、getDeptEO()
アソシエーション・アクセッサを使用して、関連する製品を取得します。関連するDeptEO
のポスト状態もSTATUS_NEW
である場合は、super.postChanges()
をコールして自分のDMLを実行する前に、まず関連する親行のpostChanges()
をコールします。
この例を実行すると、newEmployeeForNewDepartment()
メソッドのコードの作成順序を変更しなくても、エンティティは、新しいDeptEO
が先で新しいEmpEO
が後という正しい順序でポストされます。しかし、まだ問題があります。まだ制約違反が発生しますが、今度は理由が異なります。
DeptEO
エンティティ・オブジェクトの主キーがユーザーによって割り当てられている場合は、次の例のコードのみで、ポスト順序を修正して制約違反に対処できます。しかし、DeptEO.Id
はデータベース順序から割り当てられ、ユーザー割当てではありません。そのため、新しいDeptEO
エンティティ行がポストされる時点で、データベースが割り当てる順序値を反映するように、Id
属性が更新されます。新しいサプライヤを参照するEmpEO.DeptId
属性の外部キー値は、サプライヤID値のこの更新により親を失います。製品行が保存されるときには、S_DEPT_ID
の値と一致する行がS_DEPT
表にまだ存在しないため、制約違反が再び発生します。次の2つの項では、このような親を失うという問題に対処するための解決策について説明します。
// In EmpEOImpl.java public void postChanges(TransactionEvent transactionEvent) { /* If current entity is new or modified */ if (getPostState() == STATUS_NEW || getPostState() == STATUS_MODIFIED) { /* Get the associated dept for the employee */ DeptEOImpl dept = getDeptEO(); /* If there is an associated dept */ if (dept != null) { /* And if it's post-status is NEW */ if (dept.getPostState() == STATUS_NEW) { /* * Post the department first, before posting this * entity by calling super below */ dept.postChanges(transactionEvent); } } } super.postChanges(transactionEvent); }
注意:
このプログラム的な手法(Java EEアプリケーション・レイヤーで問題を解決する)のかわりに、データベース・レイヤーで遅延可能制約を使用する方法もあります。データベース・スキーマを制御できる場合は、外部キー制約をDEFERRABLE INITIALLY DEFERRED
として定義する(または変更する)ことを検討します。このデータベース設定により、データベースは制約の検査をトランザクションのコミット時まで遅延します。これが実行されると、COMMIT
時までにすべての適切な関連する行が保存されている場合、アプリケーションはDML操作を任意の順序で実行でき、親/子の順序は緩和されます。ただし、親の主キーが順序から割り当てられる場合は、外部キーの値をカスケード更新するために、「DBSequenceの値が設定される主キーに基づくアソシエーション」と「DBSequence割当ての外部キーに対する参照の更新」で説明するコードを作成する必要があります。
「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」で説明されているように、エンティティ・オブジェクトの主キー属性がDBSequence
型の場合は、そのオブジェクトが作成されるトランザクションの間、キーの数値は一時的に一意の負の値になります。同じトランザクション内で関連するエンティティの値を作成すると、これらの間の関連は、この一時的な負のキー値に基づくことになります。DBSequence
の値が主キーであるエンティティ・オブジェクトをポストすると、その主キーはデータベースが割り当てる正しい順序番号を反映して更新されますが、それに関連付けられたエンティティは一時的な負の外部キー値を保持したままで親を失います。
コンポジットに基づくエンティティ・オブジェクトの場合は、親エンティティ・オブジェクトのDBSequence
値が設定されている主キーが更新されると、コンポジットされる子エンティティ行の一時的な負の外部キー値は、親の更新されたデータベース割当ての主キーを反映して、自動的に更新されます。つまり、コンポジット・エンティティの場合、親を失うという問題は発生しません。
ただし、エンティティ・オブジェクトがコンポジットではないアソシエーションによって関連付けられている場合は、コードを作成して、一時的に負の値を参照する関連エンティティ行を、更新されたデータベース割当ての主キー値で更新する必要があります。次の項では、必要なコードについて説明します。
この例のDeptEO
のようなエンティティがDBSequence
値の主キーを持ち、それと関連付けられた(ただしコンポジットではない)他のエンティティによって外部キーとして参照される場合は、次のサンプル・コードに示すように、この新しいDeptEO
行を参照する可能性のあるエンティティ行の行セットに対する参照を保存するように、postChanges()
メソッドをオーバーライドする必要があります。
現在のDeptEO
行のステータスがNew
の場合は、getEmp()
アソシエーション・アクセッサから戻るRowSet
の値を、super.postChanges()
をコールする前に、newEmployeesBeforePost
メンバー・フィールドに割り当てます。
この保存したRowSet
オブジェクトは、後で次の例で示されているオーバーライドされたrefreshFKInNewContainees()
メソッドで使用します。新しいエンティティ行は、このメソッドを呼び出すことで、postChanges()
を呼び出す前にそれを参照していた他のエンティティ行を更新された主キー値でカスケード更新できます。newEmployeesBeforePost
行セット(NULLでない場合)のEmpEOImpl
行を反復処理し、それぞれのサプライヤID値に、新しくポストされたDeptEO
エンティティの、順序から割り当てられた新しいサプライヤ値を設定します。
// In DeptEOImpl.java protected void refreshFKInNewContainees() { if (newEmployeesBeforePost != null) { Number newDeptId = getId().getSequenceNumber(); /* * Process the rowset of employees that referenced * the new department prior to posting, and update their * Id attribute to reflect the refreshed Id value * that was assigned by a database sequence during posting. */ while (newEmployeesBeforePost.hasNext()){ EmpEOImpl emp = (EmpEOImpl)newEmployeesBeforePost.next(); emp.setDeptId(newDeptId); } closeNewProductRowSet(); } }
この変更を実装すると、「実際のポスト順序の問題の観察」のサンプルは、データベース制約違反を起こさないで動作するようになります。
// In DeptEOImpl.java RowSet newEmployeesBeforePost = null; public void postChanges(TransactionEvent transactionEvent) { /* Only bother to update references if Department is a NEW one */ if (getPostState() == STATUS_NEW) { /* * Get a rowset of employees related * to this new department before calling super */ newEmployeesBeforePost = (RowSet)getEmpEO(); } super.postChanges(transactionEvent); }
ここでは、エンティティ・オブジェクト間のアソシエーションの処理に関する高度な手法について説明します。
対応する属性の等価性のみに基づく関連より複雑なエンティティ間の関連を表す必要があるときは、アソシエーションのSQL句を変更して、さらに複雑な条件を組み込むことができます。たとえば、2つのエンティティ間の関連が有効期間に依存する場合があります。Product
をSupplier
と関連付けることができても、サプライヤの名前が時間とともに変化する場合は、その製品行が使用されている(または、使用されていた)期間を記録するEFFECTIVE_FROM
列とEFFECTIVE_UNTIL
列が、SUPPLIERS
表の各行に追加されている可能性があります。その場合、Product
とそれが関連付けられるSupplier
の間の関係は、SupplierId
属性の一致と、製品のRequestDate
がサプライヤのEffectiveFrom
の日付とEffectiveUntil
の日付の間にあるという条件の組合せによって記述されます。
このような複雑な関係は、概要エディタで、アソシエーションに対して設定できます。まず、「関係」ページで、必要な追加属性のペアを追加します。この例では、(EffectiveFrom
、RequestDate
)のペアと、(EffectiveUntil
、RequestDate
)のペアです。次に、「問合せ」ページで「WHERE」フィールドを編集して、WHERE句を次のように変更できます。
(:Bind_SupplierId = Product.SUPPLIER_ID) AND (Product.REQUEST_DATE BETWEEN :Bind_EffectiveFrom AND :Bind_EffectiveUntil)
アソシエーションの作成の詳細は、「アソシエーションの作成と構成」を参照してください。
2つのエンティティ・ベースのビュー・オブジェクト間のビュー・リンクを作成する際、「ビュー・リンク・プロパティ」ページに、ビュー・オブジェクト・レベルとエンティティ・オブジェクト・レベルの両方でビュー・リンク・アクセッサ属性を公開するオプションがあります。デフォルトでは、ビュー・リンク・アクセッサは、リンク先ビュー・オブジェクトのビュー・オブジェクト・レベルでのみ公開されます。「エンティティ・オブジェクト: SourceEntityName」チェックボックスまたは「エンティティ・オブジェクト: DestinationEntityName」チェックボックスから適切なものを選択することで、リンク元またはリンク先のエンティティ・オブジェクトの一方または両方にビュー・リンク属性をJDeveloperにより含めることができます。これにより、エンティティ・オブジェクトが関連するビュー行のセットにアクセスするための便利な方法が提供されます。特に、行を生成するための問合せが現在の行の属性にのみ依存する場合に有効です。
エンティティ・オブジェクト・クラスで実装するカスタム・メソッドは、アプリケーション・モジュールの戻り型に依存できません。実行時には、ある場合、そのような依存性を持って実行されるメソッドがClassCastException
をスローすることがあります。戻されたアプリケーション・モジュールが、期待された型と一致しないからです。そのため、実装するカスタム・メソッドには、次に示すように、特定のアプリケーション・モジュールやビュー・オブジェクトの実装を取得するコードを記述しないことをお薦めします。
((MyAM)getTransaction().getRootApplicationModule()).getMyVO
特に、次のシナリオにおいて、上のコードがClassCastException
で失敗します。
コード内のエンティティ・オブジェクトが、別のビュー・オブジェクト(メソッドで参照しているビュー・オブジェクト以外)のコンテキストで使用されている場合。アプリケーションでは、複数のビュー・オブジェクト定義が同じエンティティ・オブジェクトにマップされることがあるため、ビュー・オブジェクトへの依存性を持つメソッドが、ADFビジネス・コンポーネント・ランタイムで一貫性を持って実行されることは保証できません。
ルート・アプリケーション・モジュールの下に、手動でアプリケーション・モジュールをネストする場合。この場合、ネストされたアプリケーション・モジュールが同じTransaction
オブジェクトを共有するため、前述のコードによって、期待されるアプリケーション・モジュールの型が戻される保証はありません。
ADFビジネス・コンポーネント・フレームワークの実装がリリースを追うごとに変化する場合。たとえば、以前のリリースでは、アプリケーションがADFタスク・フローを使用して定義した宣言トランザクションを制御するために、フレームワークで内部的なルート・アプリケーション・モジュールが作成されていました。
ADFビジネス・コンポーネントはユーザーが作成できるプログラム・エンティティ・オブジェクトをサポートして、カスタム・データ・ソースと対話します。
ADFモデル・オブジェクトでエンティティ・オブジェクトのEntityObjectImpl
クラスを生成するときに、フレームワーク・ベース・クラスoracle.jbo.server.ProgrammaticEntityImpl
を使用して、カスタム・データ・ソースの各行に対する作成/削除/更新/ロック/ロールバック操作の実行を制御できます。エンティティの移入、コミット、またはロールバックの各ライフサイクルという様々なフェーズで、フレームワークはこれらのフック・ポイントを呼出し、フレームワーク・ベース実装クラスはアプリケーション開発者に、カスタム・データ・ソースとの対話およびこれらの操作の実行の制御を付与します。
注意:
ProgrammaticEntityImpl
ベース・クラスの前に、アプリケーションはベース・クラスoracle.jbo.server.EntityImpl
から拡張し、カスタム動作の多くのライフサイクルをオーバーライドしています。プログラム的にエンティティ・ベース・クラスを使用するということは、エンティティ・ライフサイクル全体を理解する必要なく、カスタム・データ・ソースとの統合がアプリケーション開発者により実行されることを意味します。
ADFモデル・オブジェクトのエンティティ・オブジェクトをプログラム的に操作するには、エンティティ・オブジェクトの作成ウィザードでプログラムオプションを選択します。こうすると、ウィザードで生成するエンティティ・オブジェクトJavaクラスは、クラシック・スタイルのフレームワーク・クラスoracle.jbo.server.EntityImpl
ではなく、フレームワーク・ベース・クラスoracle.jbo.server.ProgrammaticEntityImpl
からデフォルトで拡張します。
注意:
エンティティ・オブジェクトを作成する際にこの動作を確実に実行するには、ADFビジネス・コンポーネントの設定ダイアログで、ベース・クラスoracle.jbo.server.EntityImpl
からの拡張を有効にする機能を選択解除する必要があります。ツール-設定ダイアログのADFビジネス・コンポーネント-ビュー・オブジェクトページのオプションはクラシック・プログラム・ビューです(選択解除してProgrammaticEntityImpl
からの拡張を可能にする)。
oracle.jbo.server.ProgrammaticEntityImpl
を拡張するエンティティ・オブジェクトをプログラム的に作成するには、エンティティ・オブジェクト作成ウィザードを使用して、プログラムデータ・ソース・オプションを選択します。
始める前に:
クラシック・スタイルのプログラム的なエンティティ・オブジェクト生成を無効にする必要があります。ツール-設定ダイアログのADFビジネス・コンポーネント-ビュー・オブジェクトページで、オプションクラシック・プログラム・ビューの選択を解除します。このオプションを選択したまま(デフォルト)にしておくと、JDeveloperはoracle.jbo.server.EntityImpl
を拡張するエンティティ・オブジェクトJavaクラスを生成します。クラシック・モードの詳細は、「代替データ・ソースに対するクラシック・スタイル・プログラムでのビュー・オブジェクトの使用」を参照してください。
ProgrammaticEntityImplを拡張してプログラムによるエンティティ・オブジェクトを作成するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
エンティティ・オブジェクト作成ウィザードの「名前」ページで、エンティティ・オブジェクトの名前とパッケージを入力します。データ・ソースについて、「プログラム関連」を選択します。
「属性」ページで、「新規」を複数回クリックし、プログラムによるエンティティ・オブジェクトが必要とするエンティティ・オブジェクト属性を定義します。
「属性の設定」ページで、定義した属性に必要な設定を調節します。
Javaページでエンティティ・オブジェクト・クラスの生成を選択し、カスタム・エンティティ・クラス(ProgrammaticEntityImpl
)がユーザー・コードを含めるようにします。
「終了」をクリックし、エンティティ・オブジェクトを作成します。
エンティティ・オブジェクトのカスタムJavaクラスで、「ProgrammaticEntityImplベースのエンティティ・オブジェクト用にオーバーライドする主要なフレームワーク・メソッド」で説明されているメソッドをオーバーライドし、独自のデータ取得方法を実装します。
プログラムによるエンティティ・オブジェクトで生成するEntityObjectImpl.java
には、オーバーライドする必要がある次のメソッドがあります。
postDataProvider(int operationType , ArrayList retAttrNames)
このメソッドはdoDML実行中に呼び出されます。デフォルトで、フレームワークはベース実装から例外をスローします。operationType
引数に指定できる値はEntityImpl.DML_INSERT
、EntityImpl.DML_UPDATE
またはEntityImpl.DML_DELETE
です。操作タイプに基づき、アプリケーションはカスタム・データ・ソース(または、セカンダリ・キャッシュを維持している場合は、セカンダリ・キャッシュ)の行を挿入/更新/削除する必要があります。retAttrNames
引数には、行が更新/挿入された後にアプリケーションが戻す値の属性名のリストが含まれます。
getRowFromDataSource(HashMap[String , Object] origPrimaryKeyMap)
アプリケーションがカスタム・データ・ソースから最新の行データを戻すには、このAPIをオーバーライドする必要があります。エンティティのフォールトイン、コミットまたは再移入の間、フレームワークは複数ユーザー・アクセスのユース・ケースを処理するために、最新の行データを知る必要があります。origPrimaryKeyMap引数は、主要属性のいずれかをアプリケーションが変更しても、元のプライマリ主要情報を含みます。
lockDataProvider(boolean lock)
アプリケーションがカスタム・データ・ソースの該当する行をロック/ロック解除するためには、このAPIをオーバーライドする必要があります。データベースの動作と同様、フレームワークは複数ユーザーによる同時コミットを避けるために、カスタム行DataProvider
のロックを試みます。行がすでにロックされている場合、アプリケーションはoracle.jbo.AlreadyLockedException
例外をスローします。
rollbackDataProvider()
カスタムDataSource
が中間キャッシュも維持している場合、アプリケーションはロールバックを実行するためにこのAPIをオーバーライドできます。コミット/postChangesフェーズ中にどの例外も発生しなければ、フレームワークはこのAPIを呼び出します。
commitDataProvider(ProgrammaticEntityImpl.CommitActionType commitActionType)
カスタムDataSource
が中間キャッシュも維持している場合、アプリケーションはこのAPIをオーバーライドすることにより、キャッシュをオリジナル・データとマージします。commitActionType
引数に指定できる値は次のとおりです。
CommitActionType.DELETE
- キャッシュおよび元のカスタムDSから行を削除します
CommitActionType.MERGE
- キャッシュの行を元のカスタムDSにマージします
CommitActionType.IGNORE
- キャッシュされたこの行を無視し、元の行に対してNOアクションを実行します
他の有益な保護API
getPrimaryKeyMap(boolean orig)
orig
引数に基づき、このAPIはプライマリ・キーの値マップを返します。orig
がTRUEの場合はデータ・ソースから読み取られたプライマリ・キー値が返され、それ以外の場合は現在のプライマリ・キー値が返されます。
getChangedAttributeMap()
このAPIは変更されたすべての属性のHashMapを返します。
ADFドメインというカスタム・データ型に共通検証ロジックをカプセル化することで、アプリケーション・メンテナンスを簡略化し、検証を1か所に納めます。
複数のエンティティ・オブジェクトで似た属性値に対して同じ妥当性検査を繰り返し行っている場合は、この検証をカプセル化した独自のデータ型を作成することで、時間と労力を節約できます。たとえば、ビジネス・ドメイン・レイヤーに、電子メール・アドレスを表す文字列を格納する大量のエンティティ・オブジェクト属性がある場合を考えてみてください。ビジネス・ドメイン・レイヤーのどこでもエンド・ユーザーが有効な電子メール・アドレスを入力できるように保証する方法としては、次のようなものがあります。
各属性に対して基本のString
データ型を使用
Javaコードを使用して属性レベルのメソッド・バリデータを追加し、属性ごとにString
値の形式が有効な電子メール・アドレスであることを確認
しかし、このような方法は、大規模なアプリケーションでは急速に冗漫になります。ADFビジネス・コンポーネントで提供されている代替手段を使用すると、電子メール・アドレスを表す独自のEmailAddress
データ型を作成できます。電子メール・アドレスの値に関するすべての妥当性検査をこの新しいカスタム・データ型にまとめた後は、アプリケーション内にある電子メール・アドレスを表すすべての属性の型として、EmailAddress
を使用できます。このようにすると、属性値の意図が他の開発者に対していっそう明確になり、検証処理が1か所にまとまるためアプリケーションのメンテナンスが簡単になります。ADFビジネス・コンポーネントでは、これを開発者作成のデータ型ドメインと呼びます。
ドメインとは、String
、Number
、Date
などの基本データ型を拡張するJavaクラスであり、関連する妥当性検査に候補値が合格することを保証するコンストラクタ時の検証が追加されています。基本データ型検証、書式設定、カスタム・メタデータ・プロパティなどの横断的動作でカスタム・データ型を定義する手段を、ドメインを属性のJava型として使用する任意のエンティティ・オブジェクトまたはビュー・オブジェクトによって継承される方法で提供します。
注意:
この項の例は、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.domains
パッケージを参照しています。
ドメインを作成するには、ドメイン作成ウィザードを使用します。このウィザードは、「ADFビジネス・コンポーネント」カテゴリの「新規ギャラリ」で使用できます。
始める前に:
ドメインの使用に関する知識があると役立ちます。詳細は、「ドメインを使用したカスタム検証済データ型の作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
ドメインを作成するには:
ドメインを作成すると、プロジェクトのソース・パスのサブディレクトリに、選択したパッケージ名に対応するXMLドキュメントが作成されます。たとえば、summit.model.domains
パッケージにShortEmailAddress
ドメインを作成すると、./summit/model/domains
サブディレクトリにShortEmailAddress.xml
ファイルが作成されます。ドメインには常に対応するJavaクラスがあり、ドメインが存在するパッケージのcommon
サブパッケージに作成されます。つまり、summit.model.domains.common
パッケージにShortEmailAddress.java
クラスが作成されます。ドメインのJavaクラスは、組込みデータ型と同じように動作する適切なコードを備えて生成されます。
ドメインは様々に異なる型として作成でき、標準の属性ではなく異なる特性を持ちます。ここでは、ドメインを使用する際に理解しておく必要のある事項について説明します。
プロジェクトでドメインを作成すると、エンティティ・オブジェクトおよびビュー・オブジェクトのウィザードとダイアログで「属性」の「型」ドロップダウン・リストに、使用可能なデータ型の1つとして表示されます。特定の属性の型としてドメインを使用するには、リストから選択します。
注意:
エンティティ・ベースのビュー・オブジェクトのエンティティにマップされた属性は、対応する基礎となるエンティティ・オブジェクト属性からデータ型を継承します。したがって、エンティティ属性でドメイン・タイプを使用する場合、一致するビュー・オブジェクト属性も同様になります。ビュー・オブジェクトの一時属性またはSQL導出属性の場合は、基礎のエンティティから継承しないため、ドメインを使用するように型を直接設定できます。
通常、ドメインについてユーザーが行う必要のある唯一のコーディング作業は、生成されるvalidate()
メソッドの内部のカスタム・コードを記述することです。validate()
メソッドの実装では、設定している候補値の妥当性検査を行い、検証が失敗した場合はoracle.jbo
パッケージでDataCreationException
をスローします。
翻訳可能な例外メッセージをスローするには、次の例で示されているようなメッセージ・バンドル・クラスを作成できます。このクラスは、ドメイン・クラス自体と同じパッケージに作成します。メッセージ・バンドルは、{
MessageKeyString
、
TranslatableMessageString
}
というペアの配列を返します。
注意:
リソース・バンドルでは、XLIFFなどの追加の形式を使用できます。詳細は、「リソース・バンドルの使用」を参照してください。
package oracle.summit.model.domains; import java.util.ListResourceBundle; public class ErrorMessages extends ListResourceBundle { public static final String INVALID_SHORTEMAIL = "30002"; public static final String INVALID_EVENNUMBER = "30003"; private static final Object[][] sMessageStrings = new String[][] { { INVALID_SHORTEMAIL, "A valid short email address has no @-sign or dot."}, { INVALID_EVENNUMBER, "Number must be even."} }; /** * Return String Identifiers and corresponding Messages * in a two-dimensional array. */ protected Object[][] getContents() { return sMessageStrings; } }
String
はJDKのベース型であるため、String
に基づくドメインは、private mData String
メンバー・フィールドを集約して、ドメインが表す値を保持します。その後、クラスはADFランタイムが期待するDomainInterface
とともにSerializable
インタフェースも実装するので、Oracle ADFコンポーネントのカスタム・クライアント・インタフェースのメソッドの引数または戻り型でドメインを使用できます。
次の例は、簡単なShortEmailAddress
ドメイン・クラスのvalidate()
メソッドを示しています。このメソッドは、mData
値にアットマークまたはドットが含まれないていないことを確認し、含まれている場合は、翻訳可能なエラー・メッセージの適切なメッセージ・バンドルとメッセージ・キーを参照するDataCreationException
をスローします。
public class ShortEmailAddress implements DomainInterface, Serializable { private String mData; // . . . protected void validate() { /**Implements domain validation logic and throws a JboException on error. */ int atpos = mData.indexOf('@'); int dotpos = mData.lastIndexOf('.'); if (atpos > -1 || dotpos > -1) { throw new DataCreationException(ErrorMessages.class, ErrorMessages.INVALID_SHORTEMAIL,null,null); } } // . . . }
oracle.jbo.domain
パッケージの組込み型に基づく他の簡単なドメインは、次の例に示すように、ベース型を拡張します。この例では、偶数を表すEvenNumber
という名前のNumberベースのドメインに対するvalidate()
メソッドが示されています。
public class EvenNumber extends Number { // . . . /** * Validates that value is an even number, otherwise * throws a DataCreationException with a custom * error message. */ protected void validate() { if (getValue() % 2 == 1) { throw new DataCreationException(ErrorMessages.class, ErrorMessages.INVALID_EVENNUMBER,null,null); } } // . . . }
基本データ型に基づいて作成された単純なドメインは、不変なクラスになります。つまり、次のようにして新しいインスタンスを作成します。
ShortEmailAddress email = new ShortEmailAddress("emailaddress1");
このようにして作成したインスタンスの値は変更できません。別の短い電子メール・アドレスを参照する場合は、別のインスタンスを作成します。
ShortEmailAddress email = new ShortEmailAddress("emailaddress2");
これは新しい概念ではなく、String
、Number
、Date
などのクラスと同じ動作です。
Oracle Databaseでは、データベースでユーザー定義型を作成できます。たとえば、次のDDL文を使用してPOINT_TYPE
という名前の型を作成できます。
create type point_type as object ( x_coord number, y_coord number );
POINT_TYPE
のようなユーザー定義型を使用する場合は、その型に基づくドメインを作成したり、オブジェクト型の列を含む表をリバース・エンジニアリングしてドメインを自動的に作成したりできます。ドメイン作成ウィザードを使用して、Oracleオブジェクト型のドメインを手動で作成できます。
Oracleオブジェクト型のドメインを手動で作成するには:
Oracleオブジェクト型のドメインをリバース・エンジニアリングするには:
オブジェクト型ドメインを手動で作成する以外にも、表からのビジネス・コンポーネント・ウィザードを使用してOracleオブジェクト型の列を含む表を選択すると、JDeveloperによってそれらのオブジェクト型のドメインがリバース・エンジニアリング・プロセスの一部として作成されます。たとえば、POINT_TYPE
型の列を含む次のような表を作成するものとします。
create table interesting_points( id number primary key, coordinates point_type, description varchar2(20) );
表からのビジネス・コンポーネント・ウィザードでINTERESTING_POINTS
表に対するエンティティ・オブジェクトを作成すると、InterestingPoints
エンティティ・オブジェクトとPointType
ドメインの両方を取得できます。後者がPOINT_TYPE
オブジェクト型に基づいて生成されるのは、InterestingPoints
エンティティ・オブジェクトのCoordinates
属性のデータ型として必要であるためです。
単純なドメインとは異なり、オブジェクト型ドメインは可変です。オブジェクト型の構造体の各要素に対し、ドメイン・クラスにgetterメソッドとsetterメソッドが生成されます。ドメインのプロパティを変更した後、そのドメインをビュー・オブジェクトまたはエンティティ・オブジェクトの属性として設定するとき、ドメインは1つの単位として扱われます。Oracle ADFは、ドメインのプロパティの変更は追跡せず、ドメインが値として使用される属性値の変更のみを追跡します。
注意:
Oracleオブジェクト型に基づくドメインは、基礎となる型がOracleオブジェクト型であるデータをプログラムで処理するときに便利です。また、ストアド・プロシージャとの間における構造体情報の授受も簡単になります。ただし、ADFバインディング・レイヤーでのオブジェクト型ドメインの処理のサポートは完全ではないため、宣言的にデータバインドされたユーザー・インタフェースでオブジェクト・ドメイン値属性を使用することは容易ではありません。
「コンポーネントをADFライブラリJARにパッケージ化する方法」で説明されているように、ビジネス・コンポーネントのADFライブラリJARを作成すると、プロジェクトのソース・パスの*.commonサブディレクトリにあるドメイン・クラスとメッセージ・バンドル・ファイルは、*CSCommon.jar
にパッケージ化されます。これらは、サポートする必要のある中間層アプリケーション・サーバーと最終的なリモート・クライアントの両方に共通のクラスです。
ドメインではカスタム・メタデータ・プロパティを定義できます。そのようなドメインに基づくエンティティ・オブジェクトまたはビュー・オブジェクトの属性は、属性自体で定義されている場合と同じように、これらのカスタム・プロパティを継承します。エンティティ・オブジェクトまたはビュー・オブジェクトの属性で同じカスタム・プロパティが定義されている場合は、その設定が、ドメインから継承する値より優先されます。
JDeveloperでは、ドメイン定義レベルで適用されている宣言的な設定を施行します。この設定は、ドメイン型に基づく属性のエンティティ・オブジェクトまたはビュー・オブジェクトでは制限を緩和できません。たとえば、ドメインの「更新可能」プロパティを「新規の間」に設定した場合、そのドメインをエンティティ・オブジェクト属性のJava型として使用するときに、「更新可能」を「なし」(より厳しい制限)に設定することはできますが、「常に」に設定することはできません。同様に、ドメインを「永続的」に定義した場合、後でそれを一時的なものにすることはできません。アプリケーションで使用するときは、ドメインの宣言的プロパティはできるかぎり緩く設定し、後で必要に応じて厳しくできるようにします。
履歴タイプを使用して時間固有のデータを追跡します。JDeveloperにはすぐに使用できるADF履歴タイプが用意されていますが、カスタム履歴タイプを作成することもできます。
履歴タイプは、ある時点に固有のデータの追跡に使用されます。JDeveloperにはいくつかの履歴タイプが付属していますが、独自のものも作成できます。標準の履歴タイプとその使用方法は、「履歴列を使用して作成および変更した日付を追跡する方法」を参照してください。
付属の履歴タイプに限定されることなく、「プリファレンス」ダイアログの「履歴タイプ」ページを使用してカスタム履歴タイプを追加または削除してから、カスタムJavaコードを記述して目的の動作を実装できます。カスタム履歴タイプを処理するコードは、再利用できるようアプリケーション全体にわたるエンティティ・ベース・クラスで記述します。
図4-17は、タイプIDが11
のlast update login
カスタム・タイプです。last_update_login
がFND_LOGINS
表の外部キーであると仮定します。
図4-17 概要エディタでの新規履歴タイプ
始める前に:
履歴タイプに関する知識が役立つ場合があります。詳細は、「新規履歴タイプの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
カスタム履歴列タイプを作成するには:
履歴タイプは通常、アプリケーションの存続期間中は値の監査に使用されるため、削除する必要はありません。ただし、削除する必要がある場合は、次の作業を実行します。
「プリファレンス」ダイアログで、JDeveloper履歴タイプ・リストから目的の履歴タイプを削除します。
エンティティ・オブジェクトのJavaクラス・ファイル(またはそれを基礎としている拡張クラス)のgetHistoryContextForAttribute
メソッドで履歴タイプをサポートするために実装したカスタム・コードを削除します。
エンティティ属性メタデータの履歴タイプのすべての慣用名を削除します。この履歴タイプを使用するように定義した属性があれば、編集する必要があります。
始める前に:
履歴タイプに関する知識が役立つ場合があります。詳細は、「新規履歴タイプの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
JDeveloper履歴タイプ・リストから目的の履歴タイプを削除するには:
ADFフレームワークを使用すると、PL/SQL APIをデータベース・ビューと組み合せて使用できます。このため、クライアント・プログラムでは、データベース・ビューを使用して読み取り、PL/SQL APIを使用して表に書き込むことができます。
基礎となる表に対する挿入、更新、および削除のアクセスをカプセル化するPL/SQLパッケージがある場合は、その表を表すエンティティ・オブジェクトに対するデフォルトのDML処理イベントをオーバーライドし、かわりにPL/SQL APIの中でプロシージャを呼び出すことができます。通常、このようなPL/SQLパッケージは、付随するデータベース・ビューと組み合せて使用されます。クライアント・プログラムは、データベース・ビューを使用して基になる表からデータを読み取り、PL/SQLパッケージのプロシージャを使用してデータを表に書き戻します。
たとえば、このようなビューとパッケージの組合せに基づいてProduct
エンティティ・オブジェクトを作成するとします。
Summit ADFスキーマのS_PRODUCT
表を使用し、次のDDL文を使用して作成されるPRODUCT_V
という名前のデータベース・ビューについて考えます。
create or replace view product_v as select id,name,image_id,short_desc from s_product;
さらに、基になるS_PRODUCT
表に対する挿入、更新、削除権限をカプセル化する、次に示されるような簡単なPRODUCTS_API
パッケージを使用します。
create or replace PACKAGE PRODUCTS_API as procedure insert_product(p_id number, p_name varchar2, p_short_desc varchar2, p_longtext_id number, p_image_id number, p_suggested_whlsl_price number, p_whlsl_units varchar2); procedure update_product(p_id number, p_name varchar2, p_short_desc varchar2, p_longtext_id number, p_image_id number, p_suggested_whlsl_price number, p_whlsl_units varchar2); procedure delete_product(p_id number); end products_api;
この組合せのデータベース・ビューとPL/SQLパッケージに基づいてエンティティ・オブジェクトを作成するには、次の手順を実行します。
「ビューを基にしてエンティティ・オブジェクトを作成する方法」の説明に従ってビューを基にしたエンティティ・オブジェクトを作成します。
「PL/SQLベースのエンティティに関する詳細をベース・クラスに集中化する方法」の説明に従ってエンティティ・オブジェクトのベース・クラスを作成します。
「DML操作のストアド・プロシージャ呼出しを実装する方法」の説明に従って適切なストアド・プロシージャ・コールを実装します。
必要に応じて、「選択処理およびロック処理を追加する方法」の説明に従って選択機能とロック機能を処理します。
注意:
この項の例は、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.wrapplsql
パッケージを参照しています。
例ではビューベースのエンティティ・オブジェクトではなく表ベースのエンティティ・オブジェクトが使用されていますが、構成の詳細は同一です。
ビューを基にしてエンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
始める前に:
エンティティ・オブジェクトでPS/SQL APIを使用できる方法に関する知識が役立つ場合があります。詳細は、「PL/SQLパッケージAPIに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
「エンティティの作成ウィザードで単一のエンティティ・オブジェクトを作成する方法」の説明に従って、エンティティ・オブジェクトの作成ウィザードを起動してウィザードを実行する必要がありますが、次の手順で示す例外があります。
ビューを基にしてエンティティ・オブジェクトを作成するには:
ビューに基づくエンティティ・オブジェクトは、デフォルトで、基礎となるデータベース・ビューに対して次のすべての文を直接実行します。
SELECT
文(findByPrimaryKey()
の場合)
SELECT FOR UPDATE
文(lock()
の場合)
INSERT
、UPDATE
、DELETE
文(doDML()
の場合)
ストアド・プロシージャ・コールを使用するには、(「PL/SQLベースのエンティティに関する詳細をベース・クラスに集中化する方法」の説明に従って)doDML()
操作をオーバーライドし、必要に応じて(「DML操作のストアド・プロシージャ呼出しを実装する方法」の説明に従って)lock()
およびfindByPrimaryKey()
の処理をオーバーライドする必要があります。
PL/SQL APIに基づくエンティティ・オブジェクトが複数ある場合は、一般的な詳細をベース・フレームワーク拡張クラスに抽象化するのがよい方法です。この場合、「ビジネス・コンポーネント機能の拡張」で説明されている概念のいくつかを利用します。まず、PL/SQLベースの各エンティティでベース・クラスとして使用できるEntityImpl
ベース・クラスを拡張するPLSQLEntityImpl
クラスを作成します。
注意:
エンティティにすでに拡張したエンティティ実装クラスを使用している場合、PLSQLEntityImpl
クラスを使用してさらに拡張できます。たとえば、zzEntityImpl
というフレームワーク拡張クラスがある場合、zzEntityImpl
クラスを拡張するPLSQLEntityImpl
クラスを作成します。
次に示すように、ベース・クラスのdoDML()
メソッドをオーバーライドし、操作に基づいて異なるヘルパー・メソッドを起動します。
// In PLSQLEntityImpl.java protected void doDML(int operation, TransactionEvent e) { // super.doDML(operation, e); if (operation == DML_INSERT) callInsertProcedure(e); else if (operation == DML_UPDATE) callUpdateProcedure(e); else if (operation == DML_DELETE) callDeleteProcedure(e); }
ベース・クラスPLSQLEntityImpl.java
では、次に示すように、デフォルトの処理を実行するようにヘルパー・メソッドを記述できます。
// In PLSQLEntityImpl.java /* Override in a subclass to perform nondefault processing */ protected void callInsertProcedure(TransactionEvent e) { super.doDML(DML_INSERT, e); } /* Override in a subclass to perform nondefault processing */ protected void callUpdateProcedure(TransactionEvent e) { super.doDML(DML_UPDATE, e); } /* Override in a subclass to perform nondefault processing */ protected void callDeleteProcedure(TransactionEvent e) { super.doDML(DML_DELETE, e); }
このインフラストラクチャを配置した後、PLSQLEntityImpl
クラスに基づくエンティティ・オブジェクトでは、「ソース」→「メソッドのオーバーライド」メニュー項目を使用して、callInsertProcedure()
、callUpdateProcedure()
およびcallDeleteProcedure()
の各ヘルパー・メソッドをオーバーライドして、その特定のエンティティに適したストアド・プロシージャ呼出しを実行できます。
注意:
これらのヘルパー・メソッドをサブクラスでオーバーライドしない場合、スーパークラスで定義されているデフォルトの処理が実行されます。代替処理を提供するdoDML()
メソッドで操作のオーバーライドのみを行います。
これらのコールを実装するタスクを簡単にするには、(「ストアド・プロシージャとストアド・ファンクションの呼出し」で説明した)callStoredProcedure()
ヘルパー・メソッドをPLSQLEntityImpl
クラスに追加することもできます。このようにすると、このクラスを拡張するPL/SQLベースのエンティティ・オブジェクトはすべて、ヘルパー・メソッドを利用できます。
DML操作に対するストアド・プロシージャ呼出しを実装するには、エンティティ・オブジェクトのカスタムJavaクラスを作成して、その中の操作をオーバーライドする必要があります。
始める前に:
PL/SQLパッケージAPIに基づくエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、「PL/SQLパッケージAPIに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
メソッドのオーバーライドでカスタムJavaクラスを作成するには:
PLSQLEntityImpl
クラスのパッケージとクラスを入力するか、「参照」をクリックして検索して選択します。callInsertProcedure()
、callUpdateProcedure()
およびcallDeleteProcedure()
の各メソッドを選択し、次に「OK」をクリックします。次の例は、挿入、更新、削除プロシージャを起動するためにこれらのオーバーライドしたヘルパー・メソッドで記述するサンプル・コードを示しています。
// In ProductImpl.java protected void callInsertProcedure(TransactionEvent e) { callStoredProcedure("products_api.insert_product(?,?,?,?,?,?,?)", new Object[] { getId(), getName(), getShortDesc(), getLongtextId(), getImageId(), getSuggestedWhlslPrice(), getWhlslUnits() }); } protected void callUpdateProcedure(TransactionEvent e) { callStoredProcedure("products_api.update_product(?,?,?,?,?,?)", new Object[] { getId(), getName(), getShortDesc(), getLongtextId(), getImageId(), getSuggestedWhlslPrice(), getWhlslUnits() }); } protected void callDeleteProcedure(TransactionEvent e) { callStoredProcedure("products_api.delete_product(?)", new Object[] { getId() }); }
この段階で、Product
エンティティ・オブジェクトに対してProductView
という名前でデフォルトのエンティティ・ベースのビュー・オブジェクトを作成し、そのインスタンスをAppModule
アプリケーション・モジュールに追加すると、Oracle ADFモデル・テスターで、ProductView
ビュー・オブジェクト・インスタンスの行の挿入、更新、削除を簡単にテストできます。
通常は、挿入、更新、および削除の操作をオーバーライドするのみで十分です。データベース・ビューに対してfindByPrimaryKey()
のSELECT
文およびlock()
のSELECT FOR UPDATE
文を実行するデフォルトの動作は、ほとんどの基本的な種類のビューで動作します。
ただし、ビューが複雑でSELECT FOR UPDATE
をサポートしない場合、または他のストアド・プロシージャAPIを使用してfindByPrimaryKey()
機能およびlock()
機能を実行する必要がある場合、「選択処理およびロック処理を追加する方法」に説明されている方法を実行できます。
必要に応じて、ストアド・プロシージャを呼び出して、エンティティ・オブジェクトのlock()
およびfindByPrimaryKey()
機能を処理できます。PRODUCTS_API
パッケージを更新し、次に示されている2つのプロシージャを追加したものとします。
/* Added to PRODUCTS_API package */ procedure lock_product(p_id number, p_name OUT varchar2, p_short_desc OUT varchar2, p_longtext_id OUT number, p_image_id OUT number, p_suggested_whlsl_price OUT number, p_whlsl_units OUT varchar2); procedure select_product(p_id number, p_name OUT varchar2, p_short_desc OUT varchar2, p_longtext_id OUT number, p_image_id OUT number, p_suggested_whlsl_price OUT number, p_whlsl_units OUT varchar2);
lock_product
プロシージャとselect_product
プロシージャはどちらも、IN
パラメータとして主キー属性を受け取り、OUT
パラメータを使用して残りの属性の値を返します。
選択処理およびロック処理を追加するには、次のタスクを実行する必要があります。
「ロックと選択を処理するためのPLSQLEntityImplベース・クラスの更新」の説明に従って、ロックと選択を処理するためにベース・クラスを更新します。
「Productエンティティに対するロックおよび選択の実装」の説明に従って、ロックと選択の動作を実装するためにエンティティ・オブジェクト実装クラスを更新します。
「RowInconsistentException後のエンティティ・オブジェクトのリフレッシュ」の説明に従って、RowInconsistentException
が発生した後、エンティティ・オブジェクト実装クラスのlock()
メソッドをオーバーライドし、エンティティ・オブジェクトをリフレッシュします。
挿入、更新、削除用に追加したものと似たヘルパー・メソッドを使用して、lock()
およびfindByPrimaryKey()
のオーバーライドを処理するように、PLSQLEntityImpl
ベース・クラスを拡張できます。実行時には、lock()
操作とfindByPrimaryKey()
操作はどちらも、doSelect(boolean lock)
という名前の下位レベルのエンティティ・オブジェクト・メソッドを呼び出します。lock()
操作はパラメータをtrue
値にしてdoSelect()
を呼び出しますが、findByPrimaryKey()
操作はfalse
を渡して呼び出します。
次の例は、必要に応じてサブクラスがオーバーライドできる2つのヘルパー・メソッドに委譲するようオーバーライドされたPLSQLEntityImpl
のdoSelect()
メソッドを示しています。
// In PLSQLEntityImpl.java protected void doSelect(boolean lock) { if (lock) { callLockProcedureAndCheckForRowInconsistency(); } else { callSelectProcedure(); } }
2つのヘルパー・メソッドは、ベース・クラスPLSQLEntityImpl
のデフォルト機能を実行するためだけに記述されています。
// In PLSQLEntityImpl.java /* Override in a subclass to perform nondefault processing */ protected void callLockProcedureAndCheckForRowInconsistency() { super.doSelect(true); } /* Override in a subclass to perform nondefault processing */ protected void callSelectProcedure() { super.doSelect(false); }
ロックを実行するヘルパー・メソッドの名前がcallLockProcedureAndCheckForRowInconsistency()
であることに注意してください。これは、行をロックするときに、エンティティ・キャッシュ内のエンティティ・オブジェクトが現在のデータベース値であると認識している値と新規選択行の値が同じかどうかを検出する検査を実行する必要があることを、伝えています。
この古い値と新しい値の属性の比較をサブクラスが実行するのを助けるため、次のようなfinalのヘルパー・メソッドをPLSQLEntityImpl
クラスに追加できます。
// In PLSQLEntityImpl protected void compareOldAttrTo(int attrIndex, Object newVal) { if ((getPostedAttribute(attrIndex) == null && newVal != null) || (getPostedAttribute(attrIndex) != null && newVal == null) || (getPostedAttribute(attrIndex) != null && newVal != null && !getPostedAttribute(attrIndex).equals(newVal))) { throw new RowInconsistentException(getKey()); } }
追加のインフラストラクチャをベース・クラスPLSQLEntityImpl
に置くと、エンティティ・オブジェクト実装クラス(ProductImpl
など)のcallSelectProcedure()
およびcallLockProcedureAndCheckForRowInconsistency()
ヘルパー・メソッドをオーバーライドできます。select_product
プロシージャとlock_product
プロシージャはOUT
引数を持っているため、「他の種類のストアド・プロシージャの呼出し方法」で説明したように、JDBCのCallableStatement
オブジェクトを使用してこれらの呼出しを実行する必要があります。
次の例は、ProductImpl
エンティティ・オブジェクト実装クラスのselect_product
プロシージャを起動して主キーで行を選択するために使用するコードを示しています。このコードが実行する基本的な手順は次のとおりです。
CallableStatement
を作成します。OUT
パラメータと型を登録します。IN
パラメータの値を設定します。// In ProductImpl.java protected void callSelectProcedure() { String stmt = "begin products_api.select_product(?,?,?,?,?,?,?);end;"; // 1. Create a CallableStatement for the PLSQL block to invoke CallableStatement st = getDBTransaction().createCallableStatement(stmt, 0); try { // 2. Register the OUT parameters and types st.registerOutParameter(2, VARCHAR2); st.registerOutParameter(3, VARCHAR2); st.registerOutParameter(4, NUMBER); st.registerOutParameter(5, NUMBER); st.registerOutParameter(6, NUMBER); st.registerOutParameter(7, VARCHAR2); // 3. Set the IN parameter value st.setObject(1, getId()); // 4. Execute the statement st.executeUpdate(); // 5. Retrieve the possibly updated column values String possiblyUpdatedName = st.getString(2); String possiblyUpdatedShortDesc = st.getString(3); String possiblyUpdatedLongTextId = st.getString(4); String possiblyUpdatedImageId = st.getString(5); String possiblyUpdatedSuggestedPrice = st.getString(6); String possiblyUpdatedWhlslUnits = st.getString(7); // 6. Populate the possibly updated attribute values in the row populateAttribute(NAME, possiblyUpdatedName, true, false, false); populateAttribute(SHORTDESC, possiblyUpdatedShortDesc, true, false, false); populateAttribute(LONGTEXTID, possiblyUpdatedLongTextId, true, false, false); populateAttribute(IMAGEID, possiblyUpdatedImageId, true, false, false); populateAttribute(SUGGESTEDWHLSLPRICE, possiblyUpdatedSuggestedPrice, true, false, false); populateAttribute(WHLSLUNITS, possiblyUpdatedWhlslUnits, true, false, false); } catch (SQLException e) { throw new JboException(e); } finally { if (st != null) { try { // 7. Closing the statement st.close(); } catch (SQLException e) { } } } }
次の例は、lock_product
プロシージャを起動するためのコードを示しています。基本的には前記と同じ手順を行いますが、次の2点のみ異なります。
OUT
パラメータから更新された可能性のある列の値を取得した後、PLSQLEntityImpl
から継承されたcompareOldAttrTo()
ヘルパー・メソッドを使用して、行ロック試行の結果としてRowInconsistentException
をスローする必要があるかどうかを判定します。
catch (SQLException e)
ブロックでは、データベースがエラーをスローしたかどうかを検査しています。
ORA-00054: resource busy and acquire with NOWAIT specified
例外がスローされている場合は、lock()
機能のデフォルトのエンティティ・オブジェクトの実装がこのような状況で行うのと同じように、再びADFビジネス・コンポーネントのAlreadyLockedException
をスローします。
これらのメソッドを配置すると、すべてのデータベース操作についてPL/SQLパッケージをラップするエンティティ・オブジェクト(この場合、PRODUCTS_API
パッケージのProduct
エンティティ・オブジェクト)ができます。ビュー・オブジェクトのデータ問合せ機能およびエンティティ・オブジェクトのデータ検証と保存機能を明確に分離するため、通常のエンティティ・オブジェクトを使用するのと同じように、このエンティティ・オブジェクトを利用できます。このエンティティ・オブジェクトをエンティティ慣用名として使用する異なるビュー・オブジェクトを、必要な数だけ作成できます。
// In ProductsImpl.java protected void callLockProcedureAndCheckForRowInconsistency() { String stmt = "begin products_api.lock_product(?,?,?,?,?,?,?);end;"; CallableStatement st = getDBTransaction().createCallableStatement(stmt, 0); try { st.registerOutParameter(2, VARCHAR2); st.registerOutParameter(3, VARCHAR2); st.registerOutParameter(4, NUMBER); st.registerOutParameter(5, NUMBER); st.registerOutParameter(6, NUMBER); st.registerOutParameter(7, VARCHAR2); st.setObject(1, getId()); st.executeUpdate(); String possiblyUpdatedName = st.getString(2); String possiblyUpdatedShortDesc = st.getString(3); String possiblyUpdatedLongTextId = st.getString(4); String possiblyUpdatedImageId = st.getString(5); String possiblyUpdatedSuggestedPrice = st.getString(6); String possiblyUpdatedWlhslUnits = st.getString(7); compareOldAttrTo(NAME, possiblyUpdatedName); compareOldAttrTo(SHORTDESC, possiblyUpdatedShortDesc); compareOldAttrTo(LONGTEXTID, possiblyUpdatedLongTextId); compareOldAttrTo(IMAGEID, possiblyUpdatedImageId); compareOldAttrTo(SUGGESTEDWHLSLPRICE, possiblyUpdatedSuggestedPrice); compareOldAttrTo(WHLSLUNITS, possiblyUpdatedWlhslUnits); } catch (SQLException e) { if (Math.abs(e.getErrorCode()) == 54) { throw new AlreadyLockedException(e); } else { throw new JboException(e); } } finally { if (st != null) { try { st.close(); } catch (SQLException e) { } } } }
RowInconsistentException
が発生した後で、lock()
メソッドをオーバーライドして、エンティティ・オブジェクトをリフレッシュすることができます。次の例は、エンティティ・オブジェクト実装クラスに追加してRowInconsistentException
を捕捉しエンティティ・オブジェクトをリフレッシュできるコードを示しています。
// In the entity object implementation class @Override public void lock() { try { super.lock(); } catch (RowInconsistentException ex) { this.refresh(REFRESH_UNDO_CHANGES); throw ex; } }
スキーマのタイプによっては、RETURNING
句がサポートされません。したがって、ADFエンティティ・オブジェクトではこの句を使用不可にします。すると、エンティティ・オブジェクトでは、「挿入時にリフレッシュ」
および「更新時にリフレッシュ」
が実装されます。
次のいずれかに基づくエンティティ・オブジェクトを作成する必要がある場合があります。
DBLINK
を介したリモート表に解決されるシノニム
INSTEAD OF
トリガーを持つビュー
このような場合、いずれかの属性がRefresh on InsertまたはRefresh on Updateとしてマークされていると、次のようなエラーが発生します。
JBO-26041: Failed to post data to database during "Update" ## Detail 0 ## ORA-22816: unsupported feature with RETURNING clause
これらの種類のスキーマ・オブジェクトはRETURNING
句をサポートしません。この句は、デフォルトでは、INSERT
操作またはUPDATE
操作が実行されたものと同じデータベース・ラウンドトリップでリフレッシュされた値をより効率よく返すために、エンティティ・オブジェクトが使用します。
これらの種類のスキーマ・オブジェクトはRETURNING
句をサポートしていないので、RETURNING
句をエンティティ・オブジェクトで無効にする必要がある場合があります。次の各手順では、実行方法について説明します。
始める前に:
RETURNING
句をサポートしていないスキーマ・オブジェクトの種類に関する知識があると役立ちます。詳細は、「結合ビューまたはリモートDBLinkに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
この種のエンティティ・オブジェクトでRETURNING句の使用を無効にするには:
createDef()
メソッドをオーバーライドしてsetUseReturningClause(false)
を呼び出します。「RETURNING句の使用を無効にする方法」の説明に従って、RETURNING
句の使用を無効にすると、実行時には、エンティティ・オブジェクトが別のSELECT
文を使用して「挿入時にリフレッシュ」動作および「更新時にリフレッシュ」動作を実装し、挿入または更新の後でリフレッシュするための値を取得します。
ADFビジネス・コンポーネントでは継承をサポートしており、既存のコンポーネントを拡張することで新しいコンポーネントを作成できます。
継承はオブジェクト指向開発の強力な機能であり、適切に使用すれば開発とメンテナンスを簡単にできます。「継承を使用する拡張コンポーネントの作成」で説明されているように、ADFビジネス・コンポーネントでは、継承を使用して既存のコンポーネントを拡張する新しいコンポーネントを作成し、新しいプロパティまたは動作を追加したり、親コンポーネントの動作を変更したりできます。継承は、再利用可能なビジネス・ドメイン・レイヤーで異なる種類のエンティティをモデリングする際に便利です。
注意:
この項の例は、SummitADF_Examples
ワークスペースのoracle.summit.model.polymorphic
パッケージの拡張です。
アプリケーションのデータベース・スキーマでは、同じ表の行に論理的に異なる種類のビジネス情報が格納される場合があります。このような表には、通常、各行に格納される情報の種類を決定する値を含む列があります。たとえば、アプリケーションのS_CUSTOMER
表では、国内および国際の両方の顧客に関する情報が同一の表に格納されます。また、CUSTOMER_TYPE_CODE
列の値によって、その行が表すS_CUSTOMER
の種類が決まります。
Summit ADFサンプル・アプリケーションの実装にはこの区別は含まれていませんが、アプリケーションの改訂では次のことが必要になることが考えられます。
国内顧客に固有または国際顧客に固有の追加のデータベース利用の属性を管理する
国内顧客または国際顧客によって違いがあるすべてのユーザー向けに共通の動作を実装する
国内顧客のみまたは国際顧客のみに固有な新機能を実装する
図4-19は、Customers
、Domestics
、Internationals
の各エンティティ・オブジェクトを個別に作成し、異なる種類のビジネス情報をアプリケーション内でより形式的に区別できるようにした場合の、ビジネス・ドメイン・レイヤーの様子を示しています。国内および国際は顧客の特別な種類であるため、それに対応するエンティティ・オブジェクトはベース・エンティティ・オブジェクトのCustomers
を拡張します。このベースのCustomers
エンティティ・オブジェクトには、すべての種類のユーザーに共通する属性とメソッドがすべて含まれています。図のperformCustomerFunction()
メソッドは、共通メソッドの1つを表しています。
その後、Domestics
およびInternationals
エンティティ・オブジェクトの場合は、その種類のユーザーに対して一意の特定の追加属性とメソッドを追加できます。たとえば、Domestics
には型がString
の追加のState
属性があり、国内顧客が所在する州を追跡できます。また、国内顧客に固有のperformDomesticFunction()
メソッドもあります。同様に、Internationals
エンティティ・オブジェクトには、顧客が英語を話すかどうかを追跡するLanguage
属性が追加されています。performInternationalFunction()
は国際顧客に固有のメソッドです。
図4-19 継承を使用した顧客、国内、国際の区別
異なる種類の顧客を、ビジネス・ドメイン・レイヤーの継承階層において異なるエンティティ・オブジェクトとしてモデル化することで、共通するデータや動作の共有が簡単になり、ユーザーを区別するアプリケーションの部分を実装できます。
継承階層にエンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用して各エンティティを作成します。
継承階層にエンティティ・オブジェクトを作成するには、次の作業を行います。
「識別子列と個別の値の識別」の説明に従って、識別子列と値を識別します。
「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性のサブセットを識別します。
「継承階層へのベース・エンティティ・オブジェクトの作成」の説明に従って、ベース・エンティティ・オブジェクトを作成します。
「継承階層へのサブタイプ・エンティティ・オブジェクトの作成」の説明に従って、サブタイプ・エンティティ・オブジェクトを作成します。
異なる種類の情報を含む表に基づいて継承階層にエンティティ・オブジェクトを作成する前にまず、行の種類を区別するために使用されている表の列を識別する必要があります。
たとえば、S_CUSTOMER
表では、これがCUSTOMER_TYPE_CODE
列になることがあります。このような列は表の行を異なるグループに分離つまり識別するのに役立つため、識別子列と呼ばれます。
次に、表の識別子列で有効な値を決定します。値がわからない場合は、JDeveloperの「SQLワークシート」で簡単なSQL文を実行して答えを得ることもできます。ワークシートを使用するには次のようにします。
図4-20は、「SQLワークシート」で、S_CUSTOMER
表のCUSTOMER_TYPE_CODE
列に対してSELECT DISTINCT
問合せを実行した結果を示しています。これは、CUSTOMER_TYPE_CODE
識別子の値DOMESTIC
およびINTERNATIONAL
に基づいて行が2つのグループに分類されることを示しています。
図4-20 「SQLワークシート」を使用した識別子列の異なる値の検索
表に格納されている異なるビジネス・エンティティの種類の数がわかると、個別の項目をモデル化するために作成する必要のあるエンティティ・オブジェクトの数もわかります。通常は、項目の種類ごとに1つのエンティティ・オブジェクトを作成します。次に、階層のベースとして機能するエンティティを識別するため、各項目の種類に関係する属性のサブセットを特定する必要があります。
たとえば、State
とLanguage
を除いてすべての属性はすべてのユーザーに関連することと次のことを調べると仮定します。
State
は国内顧客に固有である。
Language
は国際顧客に固有である。
この情報からCustomers
エンティティ・オブジェクトを階層のベースとし、Domestics
およびInternationals
エンティティ・オブジェクトがそれぞれCustomers
を拡張して固有の属性を追加するように決定できます。
継承階層にベース・エンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
また、「識別子列と個別の値の識別」の説明に従って、識別子列と値を判別し、「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性を判別する必要があります。
ベース・エンティティ・オブジェクトを作成する手順
継承階層にサブタイプ・エンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
「識別子列と個別の値の識別」の説明に従って識別子列と値を調べます。
「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性を調べます。
「継承階層へのベース・エンティティ・オブジェクトの作成」の説明に従って、新しいエンティティ・オブジェクトが拡張する元となる親エンティティ・オブジェクトを作成します。
親エンティティで識別子属性がすでに識別されていることを確認します。
識別されていない場合は、継承された子を作成する前に、概要エディタを使用して親エンティティの識別子属性で「多相化識別子」プロパティを設定します。
階層にサブタイプ・エンティティ・オブジェクトを作成するには:
注意:
同じ手順を繰り返して、Customers
を拡張するInternationals
エンティティ・オブジェクトを定義し、Language
属性を追加し、CustomerTypeCode
識別子属性の「デフォルト値」を値INTERNATIONAL
に変更できます。
継承階層内のエンティティ・オブジェクトにメソッドを追加するには、エンティティ・オブジェクトでカスタムJavaクラスを有効にし、ソース・エディタを使用してメソッドを追加します。階層内のすべてのエンティティ・オブジェクトに共通するメソッドをベース・エンティティに追加し、サブタイプに固有のメソッドをサブタイプに追加します。また、必要に応じてサブタイプにおいてベース・エンティティ・オブジェクトのメソッドをオーバーライドできます。
階層内のすべてのエンティティ・オブジェクトに共通するメソッドを追加するには、ベース・エンティティ・オブジェクトの実装クラスにメソッドを追加します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
「エンティティ・オブジェクトのカスタムJavaクラスの生成」の説明に従い、ベース・エンティティ・オブジェクトのカスタムJava実装クラスを作成します。
階層内の全エンティティ・オブジェクトに共通するメソッドを追加するには:
これはベース・エンティティ・オブジェクトのクラスなので、ここで実装するメソッドは、階層のすべてのサブタイプ・エンティティ・オブジェクトによって継承されます。
階層内のすべてのエンティティ・オブジェクトに共通するサブタイプ・エンティティ・オブジェクトをオーバーライドするには、ベース・エンティティ・オブジェクトから継承した共通メソッドをサブタイプ・エンティティ・オブジェクトの実装クラスで修正します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
「階層内の全エンティティ・オブジェクトに共通するメソッドの追加」の説明に従って、ベース・エンティティ・オブジェクトの共通メソッドを作成します(サブタイプ・エンティティ・オブジェクトがオーバーライドします)。
「エンティティ・オブジェクトのカスタムJavaクラスの生成」の説明に従い、サブタイプ・エンティティ・オブジェクトのカスタムJava実装クラスを作成します。
サブタイプ・エンティティ・オブジェクトのメソッドをオーバーライドするには:
サブタイプ階層でエンティティ・オブジェクトのインスタンスについての作業を行う場合、複数の異なるサブタイプのインスタンスを処理することがあります。Domestics
およびInternationals
エンティティ・オブジェクトはCustomers
の特別な種類であるため、それらに共通するすべてが含まれるより汎用的なCustomersImpl
型を使用して、それらのすべてで動作するコードを記述できます。階層内のサブタイプ・ファミリであるクラスのこのような汎用処理を行うとき、Javaは、使用可能なメソッドで最も固有性の高いオーバーライドを常に呼び出します。
つまり、CustomersImpl
のインスタンスでperformCustomerFunction()
メソッドを呼び出し、実際にはさらに固有のDomesticsImpl
サブタイプがある場合、結果の出力は次のようになります。
## performCustomerFunction as Domestics
これに対し、標準のCustomersImpl
インスタンスによるデフォルトの出力結果は次のようになります。
## performCustomerFunction as Customer
階層内のサブタイプ・エンティティ・オブジェクトに特有のメソッドを追加するには、ソース・エディタを使用してサブタイプの実装クラスにメソッドを追加します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
「エンティティ・オブジェクトのカスタムJavaクラスの生成」の説明に従い、サブタイプ・エンティティ・オブジェクトのカスタムJava実装クラスを作成します。
サブタイプ・エンティティ・オブジェクトに固有のメソッドを追加するには:
継承を使用すると、新しいベース・エンティティの導入、主キーによるサブタイプ・エンティティの検索、および多相エンティティ・オブジェクトの慣用名を持つビュー・オブジェクトの作成も行うことができます。
たとえば、Customers
エンティティ・オブジェクトがS_CUSTOMERS
表の実際の行に対応する場合は、継承階層でベース・エンティティの役割も果たします。つまり、そのすべての属性は、階層内の全エンティティ・オブジェクトに共通でした。しかし、Customers
エンティティ・オブジェクトに国内または国際顧客に共通しない顧客固有のプロパティが必要な場合が発生することがあります。
このような場合は、階層内でベース・エンティティとして機能する新しいエンティティ・オブジェクト(BaseCustomers
など)を導入できます。これには、Customers
、Domestics
、Internationals
のすべてのエンティティ・オブジェクトに共通するすべての属性が含まれます。そして、この表に出現する具体的な行に対応する3つの各エンティティは、BaseCustomers
から継承する属性と、個々のサブタイプに固有の属性を保持できます。BaseCustomers
型では、CustomerTypeCode
属性を識別子属性としてマークするかぎり、「デフォルト値」を空白にしておいてかまいません(または、表のCUSTOMER_TYPE_CODE
列に出現しない他の値にします)。BaseCustomers
エンティティのインスタンスはアプリケーションで使用されないため、識別子のデフォルト値は問題ではありません。
エンティティ定義でfindByPrimaryKey()
メソッドを使用するときは、それを呼び出すエンティティ・オブジェクト型のエンティティ・キャッシュのみが検索されます。前述の例では、CustomersImpl.getDefinitionObject()
をコールしてCustomers
エンティティ・オブジェクトのエンティティ定義にアクセスする場合、そこでfindByPrimaryKey()
をコールすると、たまたま顧客であるキャッシュ内のエントリのみが検索結果となります。これが求める動作である場合もあります。
しかし、主キーで継承階層内のサブタイプも含めてエンティティ・オブジェクトを検索する場合は、かわりにEntityDefImpl
クラスのfindByPKExtended()
メソッドを使用できます。
たとえば、Customers
エンティティ・オブジェクトのサブタイプを作成した場合、この代替検索メソッドを使用すると、エンティティ・オブジェクトが顧客、国内または国際のいずれであっても、主キーで検索できます。その後は、Javaのinstanceof
演算子を使用して発見された型を検査し、CustomersImpl
オブジェクトをさらに固有のエンティティ・オブジェクト型にキャストして、そのサブタイプに固有の機能で作業できます。
継承階層のベース・エンティティ・オブジェクトに対応するエンティティ・オブジェクトの慣用名を持つエンティティ・ベースのビュー・オブジェクトを作成するときは、ベース・エンティティのサブタイプ階層の複数の異なるサブタイプに対応する行を問い合せるように、ビュー・オブジェクトを構成できます。ビュー・オブジェクトの各行は、識別子属性の値の対応に基づいて、適切なサブタイプ・エンティティ・オブジェクトをエンティティ行の部分として使用します。このようなビュー・オブジェクトを設定して使用する方法に関する固有の指示は、「多相エンティティ・オブジェクトの慣用名を持つサブタイプ・ビュー・オブジェクトを作成する方法」を参照してください。