Oracle® Fusion Middleware Oracle Application Development FrameworkによるFusion Webアプリケーションの開発 12c (12.1.3) E59441-03 |
|
![]() 前 |
![]() 次 |
この章では、ADFエンティティ・オブジェクトを使用して、Oracle ADFアプリケーションでビジネス・ドメインについて記述しているJavaオブジェクトの再使用可能なビジネス・レイヤーを作成する方法について説明します。
この章の内容は次のとおりです。
エンティティ・オブジェクトは、指定したデータソース内の行を表し(一般に単一のデータベース表、ビューまたはシノニム)、関連付けられた属性の変更を簡略化するためのADFビジネス・コンポーネントです。重要なことは、エンティティ・オブジェクトを使用すると、ドメイン・ビジネス・ロジックをカプセル化できるため、ビジネス・ポリシーおよびビジネス・ルールを一貫性のある方法で検証できるということです。
エンティティ・オブジェクトは、データの有効性を強化するために様々な宣言的ビジネス・ロジック機能をサポートしています。通常、追加のカスタム・アプリケーション・ロジックおよびビジネス・ルールで宣言的な検証に補完することにより、各エンティティ・オブジェクトに最大の量のドメイン・ビジネス・ロジックを効率的にカプセル化します。関連付けられた一連のエンティティ・オブジェクトによって、複数のアプリケーションで活用できる再利用可能なビジネス・ドメインが形成されます。
エンティティ・オブジェクト(図4-1に示します)の主要な概念は次のとおりです。
エンティティ・オブジェクトを定義するには、エンティティ・オブジェクトが示す行が含まれるデータベース表を指定します。
エンティティ・オブジェクト間の関連を反映するアソシエーションを作成できます。
実行時には、エンティティ行はエンティティ定義オブジェクトによって管理されます。
各エンティティ行は、関連する行キーによって識別されます。
エンティティ行は、データベース・トランザクションを提供するアプリケーション・モジュールのコンテキストで取得および変更します。
エンティティ・オブジェクトで作業を開始する前に、その他のOracle ADF機能を理解しておくと役に立つ場合があります。次に、関連する他の機能へのリンクを示します。
エンティティ・オブジェクトでの宣言的な検証を使用する方法の詳細は、第11章「検証とビジネス・ルールの宣言的な定義」を参照してください。
oracle.jbo
パッケージに関連するAPIのドキュメントについては、次のJavadocリファレンス・ドキュメントを参照してください。
Oracle ADFモデルJava APIリファレンス
すでに使用しているデータベース・スキーマがある場合、エンティティ・オブジェクトおよびアソシエーションを作成する最も簡単な方法は、これらを既存の表からリバース・エンジニアリングする方法です。必要な場合には、エンティティ・オブジェクトを最初から作成し、後でこのエンティティ・オブジェクト用の表を生成することもできます。
1つ以上のエンティティ・オブジェクトを作成するには、「新規ギャラリ」で使用できる「表からのビジネス・コンポーネント」ウィザードを使用します。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
既存の表から1つ以上のエンティティ・オブジェクトおよびアソシエーションを作成するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから「表からのビジネス・コンポーネント」を選択し、「OK」をクリックします。
これがプロジェクトで作成する最初のコンポーネントである場合、「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログが表示され、データベース接続を選択できます。
「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログで、データベース接続を選択するか、「新規データベース接続を作成します。」アイコンを選択して接続を作成します。「OK」をクリックします。
「エンティティ・オブジェクト」ページで、次の操作を実行してエンティティ・オブジェクトを作成します。
すべてのエンティティ・オブジェクトが作成されるパッケージ名を入力します。
「使用可能」リストからエンティティ・オブジェクトを作成する表を選択します。
「自動問合せ」チェック・ボックスを選択すると、使用可能な表のリストが即座に表示されます。必要に応じて、「名前フィルタ」フィールドで表名またはその一部を入力して、使用可能な表をリアルタイムでフィルタ処理できます。自動問合せ機能を使用するかわりに、「問合せ」ボタンをクリックしてオプションの表の名前フィルタに基づいてリストを取得することもできます。名前フィルタが入力されていない場合は、JDeveloperにより、選択したスキーマに対するすべての表オブジェクトが取得されます。
使用可能なデータベース・オブジェクトのうち、サブセットのみを表示する場合は、「フィルタ・タイプ」をクリックします。表、ビューまたはシノニムをフィルタ処理できます。
「使用可能」リストから表を選択した後、「選択済」リストに、この表について提案されるエンティティ・オブジェクト名とともに、関連する表名がカッコで囲まれて表示されます。
「選択済」リストでエンティティ・オブジェクト名を選択すると、「エンティティ名」フィールドを使用してデフォルトのエンティティ・オブジェクト名を変更できます。
ベスト・プラクティス: 各エンティティ・オブジェクト・インスタンスは特定の表の単一行を表しているため、エンティティ・オブジェクトの名前には、複数形の名詞ではなく、単数形の名詞(Address、Order、Personなど)を使用してください。図4-2に、Summit ADFスキーマでS_COUNTRIES 表を選択し、パッケージ名としてsummit.model.entities を設定し、エンティティ・オブジェクトの名前を単数形に変更した後のウィザード・ページを示します。 |
目的の表オブジェクトとそれに対応するエンティティ・オブジェクトの名前を選択したら、「終了」をクリックします。
「アプリケーション」ウィンドウに指定したパッケージのエンティティ・オブジェクトが表示されます。
ベスト・プラクティス: アソシエーションを作成したら、エンティティ・オブジェクトとは別に表示および管理できるように、すべてのアソシエーションを別のパッケージに移動します。図4-3では、アソシエーションはサブパッケージ(assoc )に移動されているため、「アプリケーション」ウィンドウのentities パッケージには表示されません。詳細は、4.3.4項「アソシエーションの名前変更および別のパッケージへの移動方法」を参照してください。 |
単一のエンティティ・オブジェクトを作成するには、「新規ギャラリ」から使用可能なエンティティ・オブジェクト作成ウィザードを使用します。
注意: エンティティ・オブジェクト作成ウィザードを使用すると、アソシエーションは生成されません。ただし、「表からのビジネス・コンポーネント」ウィザードを使用すると、アソシエーションは生成されますエンティティ・オブジェクトの作成ウィザードを使用して、エンティティ・オブジェクトを作成する場合、対応するアソシエーションを手動で作成する必要があります。 |
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
単一のエンティティ・オブジェクトを作成するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
これがプロジェクトで作成する最初のコンポーネントである場合、「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログが表示され、データベース接続を選択できます。
「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログで、データベース接続を選択するか、「新規」を選択して接続を作成します。「OK」をクリックします。
「名前」ページで、次の操作を実行してエンティティ・オブジェクトを作成します。
エンティティ・オブジェクトの名前を入力します。
エンティティ・オブジェクトが作成されるパッケージ名を入力します。
「参照」(「スキーマ・オブジェクト」フィールドの横)をクリックし、エンティティ・オブジェクトを作成する表を選択します。
または、表を後で作成する場合は、現在使用されていない名前を入力できます。
「スキーマ・オブジェクト」フィールドに表の名前を手動で入力する場合、ウィザードの「属性」ページで、各属性を定義する必要があります。「次へ」をクリックします。
4.2.6項「エンティティ・オブジェクトからのデータベース表の作成方法」で説明するように、この表は手動で作成または生成できます。
目的の表オブジェクトとそれに対応するエンティティ・オブジェクトの名前の操作が完了したら、「終了」をクリックします。
既存の表からエンティティ・オブジェクトを作成する場合、最初に、次の情報を推測するためにデータ・ディクショナリからデータが取得されます。
表の列名に基づく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ファイルが作成されます。
表に主キー制約がない場合、JDeveloperは、エンティティ・オブジェクトの主キーを推測できません。すべてのエンティティ・オブジェクトには少なくとも1つの属性が主キーとしてマークされている必要があるため、ウィザードでは、エンティティの主キーの一部としてすべての列がマークされています。必要な場合、後でエンティティ・オブジェクトを編集し、別の属性を主キーとしてマークし、その他の属性の設定を削除できます。エンティティ・オブジェクト作成ウィザードを使用する場合、その他の属性を主キーとして設定していないと、すべての列を主キーとして使用するよう求められます。
「表からのビジネス・コンポーネント」ウィザードまたはエンティティ・オブジェクトの作成ウィザードを使用してエンティティ・オブジェクトを作成する場合、オブジェクトは基礎となる表、シノニム、またはビューを表すことができます。フレームワークは、データ・ディクショナリ内のデータベースの主キーおよび外部キー制約を調べることにより、主キーおよび関連するアソシエーションを推測できます。
ただし、選択したスキーマ・オブジェクトがデータベース・ビューである場合、データベース・ビューにはデータベース制約がないため、主キーもアソシエーションも推測できません。この場合、「表からのビジネス・コンポーネント」ウィザードを使用すると、主キーはRowID
にデフォルト設定されます。エンティティ・オブジェクト作成ウィザードを使用する場合、少なくとも1つの属性を主キーとしてマークすることにより、主キーを手動で指定する必要があります。詳細は、4.2.3.2項「表に主キーがないときに生成されるエンティティ・オブジェクト・キー」を参照してください。
選択したスキーマ・オブジェクトがシノニムである場合、結果には2種類あります。シノニムが表のシノニムである場合、ウィザードおよびエディタは、表を指定した場合と同じように動作します。かわりに、シノニムがデータベース・ビューを参照している場合、ウィザードおよびエディタは、ビューを指定した場合と同じように動作します。
新しいエンティティ・オブジェクトまたはアソシエーションを作成した後、概要エディタでその設定を編集できます。エディタを起動するには、「アプリケーション」のエンティティ・オブジェクトまたはアソシエーションのポップアップ・メニューで「開く」を選択するか、オブジェクトをダブルクリックします。エディタの別のタブをクリックすることにより、オブジェクトを定義する設定や、その実行時の動作を制御する設定を調整できます。
エンティティ・オブジェクトに基づいてデータベース表を作成するには、エンティティ・オブジェクトが含まれる「アプリケーション」ウィンドウでパッケージを右クリックし、ポップアップ・メニューから「データベース・オブジェクトの作成」を選択します。ダイアログが表示され、表の作成元のエンティティを選択できます。このツールを使用して、作成したエンティティ・オブジェクトの表を最初から生成することも、既存の表をドロップして再作成することもできます。
警告: データベースに対して操作が直接実行され、既存の表がドロップされます。この機能では、後で実行するスクリプトは生成されません。作業に進む前に、この処理を実行するかどうかを確認するダイアログが表示されます。既存の表に基づくエンティティの場合、慎重に作業してください。 |
アソシエーションの概要エディタの「アソシエーション・プロパティ」ページにある「データベース・キー制約の使用」チェック・ボックスにより、エンティティ・オブジェクトの表の作成時に、関連する外部キー制約を生成するかどうかを制御します。このオプションを選択しても、実行時には影響しません。
すでにエンティティ・オブジェクトを作成した表も、自分で(またはDBAが)変更する必要がある場合があります。基礎となる表に属性が追加されても既存のエンティティに混乱が生じることはありませんが、Java EEアプリケーションで表内の新しい列にアクセスするには、エンティティ・オブジェクトをデータベース表と同期化する必要があります。
たとえば、SQL*Plusコマンド・プロンプトで次を実行し、新しいSECURITY_QUESTION
列をPERSONS
表に追加したとします。
ALTER TABLE PERSONS ADD (security_question VARCHAR2(60));
同期化機能を使用すると、エンティティ・オブジェクトに新規列を属性として追加できます。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
データベース表の変更とエンティティを同期化するには:
「アプリケーション」ウィンドウで、目的のエンティティ・オブジェクトを右クリックし、「データベースとの同期化」を選択します。
「データベースとの同期化」ダイアログに、ビジネス・ロジック層とデータベースの同期化に実行可能なアクションのリストが表示されます。
実行するアクションを選択します。
選択した項目を同期化するには、リストからアクションを1つ以上選択し、「同期化」をクリックします。
リスト内のすべてのアクションを実行するには、「すべて同期化」をクリックします。
アクション・リストをテキスト・ファイルに保存するには、「保存」をクリックします。この機能では、変更内容を追跡できます。
「OK」をクリックします。
同期化機能では、ドロップされた列は処理されません。エンティティ・オブジェクトの作成後に基礎となるデータベースから列がドロップされた場合は、対応する属性をエンティティ・オブジェクトから削除できます。属性がアプリケーションの他の部分で使用される場合は、それらの使用方法も削除する必要があります。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ属性を削除するには:
「アプリケーション」ウィンドウで、削除する属性を含むエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックします。
「属性」ページで属性を右クリックし、「削除」を選択します。
他の使用方法がある場合は、「削除の確認」ダイアログに「使用方法が見つかりました。」のメッセージが表示されます。
使用方法が見つかった場合は、「使用方法の表示」をクリックします。
ダイアログに、属性のすべての使用方法が表示され、クリックするとソース・エディタで使用方法を表示できます。
「プレビュー」をクリックすると「ログ」ウィンドウに属性の使用方法が表示され、リストで作業してエンティティ属性のすべての使用方法を削除できます。
同期化機能では、変更されたデータ型は処理されません。基礎となる表でのデータ型の変更(精度の向上など)では、属性のすべての使用方法を特定して、必要に応じて手動で変更する必要があります。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ属性のすべての使用方法を特定するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックします。
「属性」ページで属性を右クリックし、「使用方法の検索」を選択します。
他の使用方法がある場合は、「ログ」ウィンドウに表示されます。
特定の時点に関するデータセットを表示する場合は、有効日が指定された表が使用されます。有効日が指定された表は、次のような問合せを処理するHRMSやPayrollなどのアプリケーションで広く使用されています。
2005年8月31日現在の従業員への課税率はどの程度か。
2004年10月現在の従業員の給料はどの程度か。
この2つのいずれかについて、従業員のデータがそれ以降変更されている場合があります。
有効日が指定されたエンティティ・タイプと日付が指定されたエンティティ・タイプの主な違いは、日付が指定されたエンティティ・タイプでは、更新および削除中に行が分割されないことです。
注意: マスター/ディテール関係の親エンティティ・オブジェクトで有効日を使用しないでください。子オブジェクトは親オブジェクトに対するイベントを呼び出すことができ、有効日が指定されたエンティティ・オブジェクトは更新および削除操作中に行が分割されるため、親と子が同じトランザクション中に更新されると、子オブジェクトは間違った親オブジェクトに対するイベントを呼び出す可能性があります。 |
有効日が指定されたエンティティ・オブジェクトを作成する場合は、エンティティを有効日指定として識別し、開始日と終了日を表すエンティティの属性を指定します。開始日と終了日の属性はDate型にする必要があります。
また、有効日が指定されたエンティティの順序を表す属性や、順序のフラグを表す属性を指定することもできます。これらの属性では、1日に行われた複数の変更を追跡できます。
始める前に:
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
有効日が指定されたエンティティ・オブジェクトを作成するには
「アプリケーション」ウィンドウで、有効日を使用可能にするエンティティ・オブジェクトをダブルクリックします。
「プロパティ」ウィンドウで、「タイプ」カテゴリを開きます。
必要に応じて、「ウィンドウ」メニューから「プロパティ」を選択し、「プロパティ」ウィンドウを表示します。
「プロパティ」ウィンドウに「タイプ」カテゴリが表示されていない場合は、概要エディタの「一般」タブをクリックし、適切なフォーカスを設定します。
「有効日タイプ」のプロパティ・メニューから、「編集」を選択します。
プロパティ・メニューを表示するには、プロパティ・フィールドの横の下矢印をクリックします。
「プロパティの編集」ダイアログで、次の設定を指定します。
「有効日タイプ」で、EffectiveDatedを選択します。
「開始日属性」で、開始日に対応する属性を選択します。
「終了日属性」で、終了日に対応する属性を選択します。
必要に応じて、1日に行われた複数の変更の追跡を可能にする属性を指定することもできます。
「有効日シーケンス」で、変更の順序を格納する属性を選択します。
「有効日シーケンス・フラグ」で、順序内の最新の変更を示すフラグを格納する属性を選択します。
「有効日シーケンス」属性と「有効日シーケンス・フラグ」属性を指定しない場合、有効日指定のデフォルトの粒度は1日になります。このため、1日に複数の変更を行うことはできなくなります。1日にエンティティを複数回更新しようとすると、例外がスローされます。これらの2つの属性を指定すると、フレームワークには、1日に行われた複数の変更の追跡に必要な値が挿入されて更新されます。
「OK」をクリックします。
注意: 「プロパティ」ウィンドウを使用して、該当の属性に開始日と終了日の属性を指定することもできます。この場合は、概要エディタで該当の属性を選択し、「プロパティ」ウィンドウでIsEffectiveStartDateプロパティまたはIsEffectiveEndDateプロパティをtrueに設定します。 |
有効日が指定されたエンティティ・オブジェクトを作成すると、SysEffectiveDate
と呼ばれる一時属性が作成され、行の有効日が格納されます。通常は、挿入、更新および削除の操作によって一時属性が変更されますが、ADFビジネス・コンポーネント・フレームワークでは、「有効日開始」と「有効日終了」で適切な値を決定します。
例4-1は、有効日が指定されたエンティティを作成したときに生成されるサンプルのXMLエントリの一部を示しています。有効日が指定されたオブジェクトの使用方法の詳細は、5.6項「有効日付範囲を使用したビュー・オブジェクト行の制限」を参照してください。
例4-1 有効日が指定されたエンティティの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"/>
「表からのビジネス・コンポーネント」ウィザードを使用すると、同時に多くのビジネス・コンポーネントを簡単に作成できます。とはいえ、単にこの処理が可能だという理由でこのウィザードを使用して、データベース・スキーマ内のすべての表についてエンティティ・オブジェクトを即座に作成する必要はありません。アプリケーションですべての表が必要な場合は、この方法が適しています。ただし、このウィザードは必要であればいつでも使用できるため、アプリケーションに必要なことがわかっている表についてエンティティ・オブジェクトを作成することをお薦めします。
13.4項「ネストされたアプリケーション・モジュールの定義」では、ビジネス・サービスのユース・ケース駆動型設計方法について説明されており、アプリケーションのビジネス・ロジックのニーズをサポートするために必要なエンティティ・オブジェクトを理解する上で役立ちます。エンティティ・オブジェクトは、必要に応じて後でいつでも追加できます。
データベース表に外部キー制約が定義されていない場合、作成されたエンティティ・オブジェクト間のアソシエーションはJDeveloperにより推測されません。一部のADFビジネス・コンポーネント・ランタイム機能はエンティティ・アソシエーションが存在しているかどうかに依存するため、外部キーが存在しない場合は、エンティティ・アソシエーションを手動で作成することをお薦めします。
アソシエーションを作成するには、「新規ギャラリ」から使用できる新規アソシエーション作成ウィザードを使用します。
始める前に:
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
アソシエーションを作成するには、次のようにします。
「アプリケーション」ウィンドウで、アソシエーションを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択したら、「アソシエーション」を選択し、「OK」をクリックします。
「アソシエーションの作成」ウィザードの「名前」ページで次のことを実行します。
アソシエーションが作成されるパッケージ名を入力します。
アソシエーション・コンポーネントの名前を入力します。
「次へ」をクリックします。
「エンティティ・オブジェクト」ページで、ソースおよびリンク先のエンティティ属性を選択します。
アソシエーションに含まれ、マスターとして機能するエンティティ・オブジェクトの1つからソース属性を選択します。
アソシエーションに含まれるその他のエンティティ・オブジェクトから、対応するリンク属性を選択します。
たとえば、図4-5では、OrdEO
エンティティ・オブジェクトからソース・エンティティ属性としてId
属性が選択されています。ItemEO
の各行には、これらの行を特定のOrdEO
行に関連付けるオーダーIDが含まれるため、ItemEO
エンティティ・オブジェクトでこのOrdId
外部キー属性をリンク先属性として選択します。
「追加」をクリックし、対応する属性ペアを、下にあるソース属性とリンク先属性のペアの表に追加します。
デフォルトでは、ソース属性とリンク先属性の両方で「バインド済」チェックボックスが選択されています。このチェックボックスでは、(選択する側に応じて)ソース・エンティティからターゲット・エンティティ、またはターゲット・エンティティからソース・エンティティに移動する際に内部で作成されるアソシエーションSQL文に値をバインドするかどうかを指定できます。
値が定数であるためにエンティティを取得するためのアソシエーションSQL文に含めない一時エンティティ属性である関連内の属性のチェック・ボックスは、通常は選択を解除します。
アソシエーションを定義するために複数の属性ペアが必要な場合、これらのステップを繰り返し、ソース/ターゲット属性ペアをさらに追加できます。
最後に、「カーディナリティ」ドロップダウンに表示されているアソシエーションのカーディナリティが正しいことを確認します。デフォルトは1対多関係です。「次へ」をクリックします。
たとえば、OrdEO
行と対応するItemEO
行間の関連は1対多であるため、デフォルト設定のままでかまいません。
「アソシエーションSQL」ページで、ソース・エンティティ・オブジェクトの特定のインスタンスに関連するリンク先エンティティ・オブジェクトにアクセスするために実行時に使用するアソシエーションSQL述語をプレビューできます。
一方向関連を表すアソシエーションを作成する場合は、「アソシエーション・プロパティ」ページで、ソース・エンティティ・オブジェクトまたはリンク先エンティティ・オブジェクトの「アクセッサの公開」チェック・ボックスの選択を解除します。ビジネス検証ロジックを作成する場合、双方向関連ナビゲーションの方が便利なため、通常これらのデフォルトのチェック・ボックス設定のままにしておきます。
たとえば、図4-6は、どちら側のエンティティ・オブジェクトも必要に応じて反対側の関連エンティティ行にアクセスできる双方向関係を表しています。つまり、この例では、OrdEO
エンティティ・オブジェクトのインスタンスを操作している場合、関連するOrderItemEO
行のコレクションに簡単にアクセスできます。また、OrderItemEO
エンティティ・オブジェクトのインスタンスを使用して、これが属するOrder
にも簡単にアクセスできます。
目的のアソシエーションを定義したら、「終了」をクリックします。
アソシエーションを作成すると、適切なXMLドキュメント・ファイルが作成され、パッケージ名と対応するディレクトリに保存されます。たとえば、oracle.summit.model.entities.assoc
サブパッケージ内にSItemOrderIdFkAssoc
という名前のアソシエーションを作成した場合、アソシエーションXMLファイルがSItemOrderIdFkAssoc.xml
という名前で../oracle/summit/model/entities/assoc
ディレクトリに作成されます。実行時には、エンティティ・オブジェクトはこのアソシエーション情報を使用して、一連の関連エンティティの操作を自動化します。
「アソシエーション・プロパティ」ページのアクセッサ名のデフォルト設定について検討し、これらの名前をより直感的なものに変更した方が適切かどうかを決定する必要があります。このデフォルト設定により、実行時に関連の反対側のエンティティにプログラム的にアクセスするときに使用するアクセッサ属性の名前が定義されます。デフォルトでは、これらのアクセッサ名は、反対側のエンティティ・オブジェクトの名前になります。エンティティのアクセッサ名は、エンティティ・オブジェクト属性とその他のアクセッサ間で一意である必要があるため、1つのエンティティが複数の方法で別のエンティティに関連付けられている場合、デフォルトのアクセッサ名は、名前を一意にするために数値接尾辞を使用して変更されます。
既存のアソシエーションでは、「アソシエーション・プロパティ」ダイアログを使用してアクセッサの名前を変更できます。
始める前に:
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
アソシエーション内のエンティティ・アクセッサの名前を変更するには:
「アプリケーション」ウィンドウで、名前を変更するエンティティ・アソシエーションを含むアソシエーションをダブルクリックします。
概要エディタで、「関連」ナビゲーション・タブをクリックします。
「関連」ページで、「アクセッサ」カテゴリを開き、「編集」アイコンをクリックします。
「アソシエーション・プロパティ」ダイアログに、アソシエーションのアクセッサの現在の設定が表示されます。
「アソシエーション・プロパティ」ダイアログで、必要に応じて名前を変更し、「OK」をクリックします。
アソシエーションは、通常はプロジェクトの最初に構成し、それ以降はそれほど頻繁に変更しないコンポーネントであるため、アソシエーションを別のパッケージに移動し、エンティティ・オブジェクトを見やすくできます。
コンポーネントの名前変更と別のパッケージへの移動は両方とも、JDeveloperのリファクタ機能を使用して直接実行します。ただし、参照が失われるとアプリケーションが破壊されることがあるため、ADFビジネス・コンポーネント・オブジェクトの手動での名前の変更や移動はお薦めしません。JDeveloperのリファクタ機能の詳細は、第43章「Fusion Webアプリケーションのリファクタ」を参照してください。
始める前に:
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
別のパッケージにビジネス・コンポーネントのセットを移動するには:
「アプリケーション」ウィンドウで、移動するコンポーネントを選択します。
選択したコンポーネントを右クリックし、「リファクタ」→「移動」を選択します。
「ビジネス・コンポーネントを移動」ダイアログで、コンポーネントを移動するパッケージの名前を入力するか、「参照」をクリックしてパッケージに移動して選択します。
「OK」をクリックします。
コンポーネントの名前を変更するには:
「アプリケーション」ウィンドウで、名前を変更するコンポーネントを右クリックし、「リファクタ」→「名前の変更」を選択します。
「名前の変更」ダイアログで、コンポーネントの新しい名前を入力し、「OK」をクリックします。
ADFビジネス・コンポーネントをリファクタする場合、コンポーネントに関連するXMLファイルおよびJavaファイルがJDeveloperにより移動されるとともに、これらを参照するその他のコンポーネントが更新されます。
図4-7は、すべてのアソシエーションの名前を変更し、これらをoracle.summit.model.entities.assoc
サブパッケージに移動した後の「アプリケーション」ウィンドウの様子を示しています。任意のパッケージ名を選択してアソシエーションをリファクタすることにより、サブパッケージを使用してアソシエーションとエンティティとの論理的な関連を維持しながらアソシエーションのパッケージを閉じることができるため、「アプリケーション」ウィンドウに表示するファイルをより効率的に管理できます。
カスタム・ビュー・オブジェクトをエンティティ・アソシエーションのリンク元またはリンク先(あるいは両方)に関連付けることができます。
コード内でエンティティ・アソシエーションをトラバースするときに、エンティティがキャッシュにない場合は、ADFビジネス・コンポーネント・フレームワークによって問合せが実行され、エンティティ(複数可)がキャッシュに取り込まれます。デフォルトでは、エンティティをキャッシュに取り込むために実行される問合せは、主キーによる検索の問合せで、基礎となる表からすべての永続エンティティ属性の値を選択します。アプリケーションでプログラム的なエンティティ・アソシエーションのトラバースが多く実行される場合、すべての属性を取得することは、無理なユースケースになる場合があります。
エンティティ・アソシエーションでは、カスタムのエンティティ・ベースのビュー・オブジェクトをアソシエーション内のソース・エンティティまたは関連先エンティティ(あるいは両方)に関連付けることができます。指定するエンティティ・ベースのビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名は、それを使用するアソシエーションの端のエンティティ・タイプに一致する必要があります。
カスタム・ビュー・オブジェクトの問合せには少ない列を含めることが可能で、ORDER BY
句を含めることができるため、カスタム・ビュー・オブジェクトを使用する方が便利な場合があります。これにより、アソシエーションのトラバースによりエンティティがキャッシュに取り込まれる際に取得されるデータの量だけでなく、関連するエンティティのコレクションの順序も制御できます。
カスタム・ビュー・オブジェクトの作成の詳細は、10.5.2項「プログラムでエンティティ・ベースのビュー・オブジェクトを作成する方法」を参照してください。
アソシエーションは、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ダイアグラムでは、アソシエーションで他方を構成する側に黒い菱形を使用して、より強力なコンポジット関連を示しています。
「表からのビジネス・コンポーネント」ウィザードでは、ON DELETE CASCADE
オプションを持つ外部キーに対してデフォルトでコンポジット・アソシエーションが作成されます。アソシエーション作成ウィザードまたはアソシエーションの概要エディタを使用すると、アソシエーションをコンポジット・アソシエーションとして指定できます。「アソシエーションの作成」ウィザードの「アソシエーション・プロパティ」ページ、または概要エディタの「関連」ページで、「コンポジット・アソシエーション」チェック・ボックスを選択します。
注意: コンポジット・アソシエーションは、一時属性をベースに作成できません。 |
コンポジット・アソシエーションがある場合、エンティティ・オブジェクトには実行時に別の動作が追加されます。動作の制御設定については、4.10.13項「コンポジット動作の構成方法」を参照してください。
ビジネス・ドメイン・オブジェクトのレイヤーはチームにとって再使用可能な主要資産となるため、多くの場合、UMLモデルを使用して視覚化すると使いやすくなります。JDeveloperでは、自分やチームのメンバーが参照用として使用できるビジネス・ドメイン・レイヤーのダイアグラムを簡単に作成できるようサポートされています。
ビジネス・コンポーネントのUMLダイアグラムは、エンティティ・オブジェクトをダイアグラムにドロップしたときを示す静的な図のみではありません。むしろ、UMLダイアグラムは、現在のコンポーネント定義をUMLベースでレンダリングした図であるため、常に現在の状況を示しています。さらに、UMLダイアグラムは、視覚的なサポートとナビゲーションを提供するツールであるとともに、編集用のツールでもあります。ダイアグラムでエンティティ・オブジェクトについて概要エディタを開くには、目的のオブジェクトを右クリックして「開く」を選択します。また、エンティティ・オブジェクトおよびエンティティ属性の名前変更や属性の追加または削除など、エンティティ・オブジェクトの編集タスクをダイアグラム上で直接実行することもできます。ただし、ビジネス・コンポーネント・ダイアグラムでのエンティティ・オブジェクトへの変更は、基礎となるデータベース・オブジェクトには影響しません。
エンティティ・オブジェクトのダイアグラムを作成するには、「新規ギャラリ」から使用可能な「ビジネス・コンポーネント・ダイアグラムの作成」ダイアログを使用できます。
始める前に:
エンティティ・ダイアグラムがアプリケーションで使用される方法に関する知識が役立つ場合があります。詳細は、4.4項「ビジネス・レイヤーのエンティティ・オブジェクトのダイアグラムの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
既存のエンティティ・オブジェクトをモデル化するビジネス・コンポーネント・ダイアグラムを作成する手順
「アプリケーション」ウィンドウで、エンティティ・ダイアグラムを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択したら、「ビジネス・コンポーネント・ダイアグラム」を選択し、「OK」をクリックします。
このダイアログで、次の操作を実行してダイアグラムを作成します。
ダイアグラムの名前を入力します(Business Domain Objects
など)。
ダイアグラムが作成されるパッケージ名を入力します。たとえば、myproject.model.design
などのサブパッケージ内にダイアグラムを作成します。
「OK」をクリックします。
既存のエンティティ・オブジェクトをダイアグラムに追加するには、「アプリケーション」ウィンドウでこれらのオブジェクトを選択し、ダイアグラムにドロップします。
ダイアグラムを作成したら、「プロパティ」ウィンドウを使用してダイアグラムのビジュアル・プロパティを調整できます。たとえば、次の操作が可能です。
パッケージ名の表示と非表示の切替え
フォントの変更
グリッドおよび改ページの切替え
アソシエーション名の表示(表示しないと曖昧になる場合)
ダイアグラム上でポップアップ・メニューから「ダイアグラムの公開」
を選択すると、ダイアグラムのイメージをPNG
、JPG
、SVG
または圧縮されたSVG書式で作成することもできます。
図4-9は、ビジネス・ドメイン・レイヤーの様々なエンティティ・オブジェクトをモデル化したサンプル・ダイアグラムを示しています。
ビジネス・コンポーネント・ダイアグラムを作成すると、ダイアグラムが格納されているパッケージ名と一致するプロジェクトのモデル・パスのサブディレクトリに、ダイアグラムを示すXMLファイル*.adfbc_diagram
が作成されます。
デフォルトでは、「アプリケーション」ウィンドウによってプロジェクト・コンテンツ・パスの表示が統一され、ソース・パスのADFコンポーネントおよびJavaファイルがプロジェクト・モデル・パスのUMLモデル・アーティファクトと同じパッケージ・ツリーに表示されます。ただし、図4-10のように、「アプリケーション」ウィンドウの「アプリケーション・ウィンドウのオプション」ボタンを使用すると、必要に応じて、プロジェクト・コンテンツ・パスのルート・ディレクトリを個別に表示できます。
エンティティ・オブジェクトなどのビジネス・コンポーネントをUMLダイアグラムに含めると、例4-2
のように、コンポーネントのXMLコンポーネント・ディスクリプタの<Data>セクションにメタデータが追加されます。この追加情報が使用されるのは設計時のみです。
例4-2 エンティティ・オブジェクトXMLディスクリプタに追加されたUMLメタデータ
<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ビジネス・コンポーネント・オブジェクトから参照するための便利なメカニズムです。プロパティ・セットは、エンティティ・オブジェクトとその属性、ビュー・オブジェクトとその属性、およびアプリケーション・モジュールで使用できます。
プロパティ・セットは、コントロール・ヒントやエラー・メッセージなど、様々な機能に使用できます。各プロパティ・セットには、コントロール・ヒントなどのカスタム・プロパティを含めることができ、それらを別のオブジェクトの複数の属性に関連付けることもできます。
プロパティ・セット内で定義されたプロパティは、翻訳可能に構成でき、そのようなケースでは、翻訳されたプロパティはプロパティ・セットが所有するメッセージ・バンドル・ファイルに格納されます。プロパティセットはスキーマ・ドリブンであるため、名前の追加以外に説明も追加できます。
翻訳可能なコンテンツを含むプロパティセットを定義する場合は、異なるコンテンツで共通の用語をオーバーロードしないようにしてください。ソース言語では複数のコンテンツで同じ用語にできる場合でも、コンテンツごとに区別できる用語を使用するようにしてください。たとえば、ある言語で「Name」という用語がオブジェクトと個人の両方に適用できるフィールド・ラベルとして使用されているが、ターゲット言語では2つの異なる用語に翻訳される場合があります。この場合、ソース言語の値が両方の「Name」になるような2つの名前/値ペアを提供します。その後、オブジェクト名はある用語で、個人名は別の用語でターゲット言語に翻訳すると、各用語を適切に翻訳できます。プロパティの説明を使用して用語ごとに個別のコンテキストを提供できます。
プロパティ・セットを定義するには、ダイアログを使用して新規のプロパティ・セットを作成し、「プロパティ」ウィンドウを使用してプロパティを指定します。
始める前に:
プロパティ・セットを使用できる方法に関する知識が役立つ場合があります。詳細は、4.5項「プロパティ・セットの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
プロパティ・セットを定義するには:
「アプリケーション」ウィンドウで、プロパティ・セットを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから「プロパティ・セット」を選択し、「OK」をクリックします。
「プロパティ・セットの作成」ダイアログで、プロパティ・セットの名前および場所を入力し、「OK」をクリックします。
メイン・メニューから、「ウィンドウ」→「プロパティ」を選択します。
「プロパティ」ウィンドウで、プロパティ・セットのプロパティを定義します。
プロパティ・セットを作成すると、プロパティ・セットをエンティティ・オブジェクトやエンティティ属性に適用できるだけでなく、定義されたプロパティを使用(必要に応じてオーバーライド)することもできます。
始める前に:
プロパティ・セットを使用できる方法に関する知識が役立つ場合があります。詳細は、4.5項「プロパティ・セットの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトまたはビュー・オブジェクトにプロパティ・セットを適用するには:
「アプリケーション」ウィンドウで、プロパティ・セットを適用するオブジェクト(エンティティ・オブジェクトまたはビュー・オブジェクト)をダブルクリックします。
概要エディタの「一般」ナビゲーション・タブをクリックし、「プロパティ・セット」の横の「編集」アイコンをクリックします。
「プロパティ・セット」ダイアログで、適切なプロパティ・セットを選択し、「OK」をクリックします。
プロパティ・セットを属性に適用するには:
「アプリケーション」ウィンドウで、プロパティ・セットを適用する属性を含むオブジェクト(エンティティ・オブジェクトまたはビュー・オブジェクト)をダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックしてから、編集する属性を選択し、「詳細」タブをクリックします。
「詳細」ページで、「プロパティ・セット」ドロップダウン・リストから適切なプロパティ・セットを選択します。
コントロール・ヒントを使用すると、エンティティ・オブジェクト属性のラベル・テキスト、ツールチップおよびフォーマット・マスクのヒントを定義できます。必要に応じて、Groovy式を使用してUIヒントの値を計算できます。ビジネス・ドメイン・レイヤーで定義するUIヒントは、エンティティ・ベースのビュー・オブジェクトによっても継承されます。また、ビュー・オブジェクトやアプリケーション・モジュールでも同様にコントロール・ヒントを追加設定できます。
属性のコントロール・ヒントをエンティティ・オブジェクトに追加するには、概要エディタを使用します。
始める前に:
コントロール・ヒントがエンティティ・オブジェクトで使用される方法に関する知識が役立つ場合があります。詳細は、4.6項「エンティティ・オブジェクトの属性のコントロール・ヒントの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
属性のコントロール・ヒントをエンティティ・オブジェクトに追加するには:
「アプリケーション」ウィンドウで、コントロール・ヒントを追加する属性が含まれているエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックし、編集する属性を選択します。
「属性」ページで、「UIヒント」タブをクリックして、必要に応じてコントロール・ヒントを指定します。
例として、図4-12は、PaymentOptionEO
エンティティ・オブジェクトの属性ExpireDate
に定義されたコントロール・ヒントを示しています。定義されるヒントは、次のとおりです。
フォーマットの種類: Simple Date
「フォーマット」マスク: mm/yy
Groovy式を使用してUIヒントを定義する場合は、テキスト・ボックスの横の矢印をクリックし、「式の表示」を選択します。次に、矢印をクリックし、「式の編集」を選択して式エディタを開きます。
注意: Javaで定義される数値および日付のフォーマット・マスクの標準セットは、OracleデータベースのSQLおよびPL/SQL言語によって使用されるものとは異なります。詳細は、java.text.DecimalFormat およびjava.text.SimpleDateFormat クラスのJavadocを参照してください。 |
エンティティ・オブジェクトの属性のコントロール・ヒントを定義すると、これらを格納するリソース・バンドル・ファイルが作成されます。定義したヒントは、関連付けられたビュー・クライアントにおいて生成されたフォームおよび表によって使用できます。ファイルのタイプとその粒度は、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」オプションで指定します。詳細は、4.7項「リソース・バンドルの使用」を参照してください。
(「UIヒント」タブで)属性(たとえば、Simple Date)の「フォーマット・タイプ」コントロール・ヒントを設定する場合、この属性にフォーマット・マスクも指定して、UIでの値の表示方法をカスタマイズできます。使用するマスクが「フォーマット」ドロップダウン・リストに表示されない場合は、フィールドに直接入力できます。
すべてのフォーマッタがフォーマット・マスクを必要とするわけではありません。フォーマット・マスクは、フォーマッタ・タイプによって必要とされる場合にのみ指定が必要です。たとえば、日付フォーマッタにはフォーマット・マスクが必要ですが、通貨フォーマッタには必要ありません。実際、通貨フォーマッタではフォーマット・マスクはサポートされていません。
使用できるマスク要素は、関連するJavaフォーマット・クラスで定義されています。Simple Dateフォーマット・タイプのマスク要素の詳細は、Javadocのjava.text.SimpleDateFormat
を参照してください。Numberフォーマット・タイプのマスク要素の詳細は、Javadocのjava.text.DecimalFormat
を参照してください。
今後繰り返し使用するフォーマット・マスクがある場合は、「UIヒント」
タブの「フォーマット」ドロップダウン・リストで利用できるように、formatinfo.xmlファイルに追加できます。このファイルのエントリでは、ドメイン・クラスのフォーマット・マスクおよびフォーマッタ・クラスを定義します。例4-3は、java.util.Date
ドメインのフォーマット定義を示しています。
注意: formatinfo.xml ファイルは、JDeveloperシステム・ディレクトリのBC4Jサブディレクトリにあります(たとえば、C:\Documents and Settings\ username \Application Data\JDeveloper\ system## \o.BC4J\formatinfo.xml です)。 |
例4-3 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>
フォーマット・マスクの定義は、フォーマッタおよびドメイン・クラスに属し、「UIヒント」タブに表示されるマスクのテキスト指定が含まれています。特定のタイプの属性(DOMAIN CLASS)にフォーマット・タイプ
(FORMATTER name
)を指定すると、「フォーマット」
ドロップダウン・リストにマスク(FORMAT text)が表示されます。
コントロール・ヒントで使用するために、フォーマッタをドメインにマッピングする場合、oracle.jbo.format
パッケージで提供されているデフォルト・フォーマッタの1つを修正するか、oracle.jbo.format.Formatter
クラスを拡張して新しいフォーマッタ・クラスを作成できます。JDeveloperに用意されているデフォルトのフォーマッタは、java.text
パッケージで提供されているフォーマッタを集約したものです。
フォーマッタをマッピングする上で新規のドメインを作成する必要はありません。ビジネス・コンポーネント・プロジェクトに、フォーマッタと同じデータ型のドメインが含まれている場合は、既存のドメインを使用できます。
始める前に:
コントロール・ヒントがエンティティ・オブジェクトで使用される方法に関する知識が役立つ場合があります。詳細は、4.6項「エンティティ・オブジェクトの属性のコントロール・ヒントの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
新規フォーマット・マスクを定義するには:
テキスト・エディタでformatinfo.xml
ファイルを開きます。
フォーマット・マスクを追加するドメイン・クラスとフォーマッタ名を探します。
FORMATTER
要素内に新しいFORMAT
エントリを挿入します。
フォーマット・マスクを定義したら、「UIヒント」タブの「フォーマット」ドロップダウン・リストから新しいフォーマット・マスクを選択できます。
注意: フォーマット・マスクの新しいドメインを作成する場合は、フォーマッタのXML定義に、DOMAIN CLASS (新規または既存のもの)、FORMATTER (名前とクラスを含む)、およびフォーマッタ・クラスで指定するFORMAT 定義のリストを含める必要があります。 |
翻訳可能な文字列(バリデータ・エラー・メッセージや、エンティティ・オブジェクトまたはビュー・オブジェクトの属性コントロール・ヒントなど)を定義する場合、デフォルトでは、プロジェクト・レベルのリソース・バンドル・ファイルが作成され、そこに翻訳可能文字列が格納されます。たとえば、Model
プロジェクトでエンティティ・オブジェクトのコントロール・ヒントを定義すると、パッケージには、ModelBundle.
xxx
という名前のメッセージ・バンドル・ファイルが作成されます。定義したヒントは、関連付けられたビュー・クライアントにおいて生成されたフォームおよび表によって使用できます。
JDeveloperで使用されるリソース・バンドルのオプションは、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」ページのオプションで指定します。このオプションは、デフォルトで「プロパティ・バンドル」に設定され、.properties
ファイルが生成されます。このオプションおよびその他のリソース・バンドル・オプションの詳細は、4.7.1項「メッセージ・バンドル・オプションの設定方法」を参照してください。
「アプリケーション」ウィンドウでオブジェクトを選択し、「構造」ウィンドウの対応する「ソース 」ノードを参照して、エンティティ・オブジェクトのメッセージ・バンドル・ファイルを検証できます。「構造」ウィンドウには、「アプリケーション」ウィンドウで選択したコンポーネントの実装ファイルが表示されます。
例4-4は、コントロール・ヒントの情報が表示されたメッセージ・バンドル・ファイルのサンプルを示しています。各String
配列の最初のエントリはメッセージ・キーで、2番目のエントリはこのキーに対応するロケール固有のString
値です。
例4-4 プロジェクト・メッセージ・バンドルに格納されるロケールに依存したコントロール・ヒント
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
ファイルが生成されます。
始める前に:
リソース・バンドルが使用される方法に関する知識が役立つ場合があります。詳細は、4.7項「リソース・バンドルの使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
プロジェクトのリソース・バンドル・オプションを設定するには:
「アプリケーション」ウィンドウで、リソース・バンドル・オプションを指定するプロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
「プロジェクト・プロパティ」ダイアログで、「リソース・バンドル」をクリックします。
「リソース・バンドル」ページで、プロジェクトまたはカスタム設定のいずれを使用するかを指定します。
「カスタム設定を使用」を選択すると、現在のプロジェクトの作業のみに設定が適用されます。これらの設定はセッション間で保存されますが、プロジェクトには記録されないため、他のユーザーとは共有できません。「プロジェクト設定を使用」を選択すると、選択内容がプロジェクトに記録され、プロジェクトを使用する他のユーザーと共有できます。
次のオプションを選択または宣言して、設定を指定します。
バンドルの自動同期化
ハードコード化された変換可能な文字列に関して警告
常に説明の入力を要求
これらのオプションの詳細は、「ヘルプ」をクリックしてオンライン・ヘルプを参照してください。
リソース・バンドルの粒度を選択します。
1プロジェクト当たり1バンドル(デフォルト)
1ファイル当たり1バンドル
複数の共有バンドル(ADFビジネス・コンポーネントでは使用不可)
使用するファイルのタイプを選択します。
リスト・リソース・バンドル
ListResourceBundle
クラスは、名前/値の配列でリソースを管理します。各ListResourceBundleクラスは、Javaクラス・ファイルに格納されます。ListResourceBundleクラスには、ロケール固有のオブジェクトを格納できます。
プロパティ・バンドル(デフォルト)
翻訳可能なテキストを名前/値のペアで格納するテキスト・ファイルです。プロパティ・ファイル(例4-4に示すようなファイル)には、Stringオブジェクトのみに対応する値を格納できます。他のタイプのオブジェクトを格納する必要がある場合は、かわりにListResourceBundleを使用する必要があります。
Xliffリソース・バンドル
XML Localization Interchange File Format (XLIFF)は、ローカリゼーション・データの交換に対応したXMLベースのフォーマットです。
「OK」をクリックします。
翻訳可能な文字列(属性コントロール・ヒントなど)を定義する場合は、「テキスト・リソースの選択」ダイアログで新しい文字列を入力したり、オブジェクトのデフォルトのリソース・バンドルで定義済の文字列を選択できます。必要に応じて、異なるリソース・バンドルを使用することもできます。これは、プロジェクト間で共有される共通のリソース・バンドルを使用する場合に便利です。
始める前に:
リソース・バンドルが使用される方法に関する知識が役立つ場合があります。詳細は、4.7項「リソース・バンドルの使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
デフォルト以外のリソース・バンドルの設定を使用するには:
「テキスト・リソースの選択」ダイアログで、使用するバンドルを「リソース・バンドル」ドロップダウン・リストから選択します。
目的のリソース・バンドルが「リソース・バンドル」ドロップダウン・リストにない場合は、「参照」アイコンをクリックして、使用するリソース・バンドルを検索して選択します。
ダイアログには、選択したリソース・バンドルで現在定義されている文字列が表示されます。
既存の文字列を選択し、「選択」をクリックするか、新しい文字列を入力して「保存して選択」をクリックします。
新しい文字列を入力すると、選択したリソース・バンドルに書き込まれます。
ADFビジネス・コンポーネントを使用して作成したアプリケーションのモデル・レイヤーを国際化するには、各コンポーネントのメッセージ・バンドル・ファイルの翻訳バージョンを生成する必要があります。たとえば、OrdersImplMsgBundle
メッセージ・バンドルのイタリア語バージョンは、OrdersImplMsgBundle_it
という名前のクラスになり、さらに限定されたスイス・イタリア語バージョンは、OrdersImplMsgBundle_it_ch
という名前になります。通常は、これらのクラスによってベース・メッセージ・バンドル・クラスが拡張され、これらのクラスにローカライズが必要なメッセージ・キーのエントリとともにローカライズされたこれらの翻訳が含まれます。
例4-5は、エンティティ・オブジェクトのメッセージ・バンドルのイタリア語バージョンを示しています。このイタリア語バージョンでは、RequestDate
およびAssignedDate
のフォーマット・マスクがdd/MM/yyyy HH:mm
に変更されています。これにより、イタリア語のユーザーには、2006年5月3日の日付値は、デフォルトのメッセージ・バンドルのフォーマット・マスクで生成される05/03/2006 15:55
ではなく03/05/2006 15:55
として表示されます。オーバーライドされたgetContents()
メソッドに注意してください。これは、スーパークラス・バンドルからオーバーライドされていない文字列にマージされた、より限定された翻訳文字列によるメッセージ配列を戻します。実行時には、現在のユーザーのロケール設定に基づいて適切なメッセージ・バンドルが自動的に使用されます。
例4-5 イタリア語にローカライズされたエンティティ・オブジェクト・コンポーネントのメッセージ・バンドル
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()); } }
ビジネス・ロジック・グループでは、関連するコントロール・ヒント、デフォルト値および検証ロジックをカプセル化できます。ビジネス・ロジック・グループは、ベース・エンティティとは別に独自のファイルに保存され、現在の行のコンテンツ値に基づいて動的に有効にできます。
これは、ロケールごとの専門チームによって管理される多数のロケール固有の検証(国別識別子や税法チェックなど)が定義されるHRアプリケーションなどで有効です。ビジネス・ロジック・グループでは、これらの検証を個別のファイルに格納するため管理が容易になり、必要時にのみロードすることでパフォーマンスが最適化されます。
各ビジネス・ロジック・グループには、ビジネス・ロジック・ユニットのセットが格納されます。各ユニットでは、ビジネス・ロジック・グループに関連付けられた属性値に基づいて、エンティティでロードされるビジネス・ロジックのセットを識別します。
たとえば、Employee
エンティティ・オブジェクトにビジネス・ロジック・グループを定義し、EmpRegion
属性を識別子として指定できます。次に、リージョンごとにビジネス・ロジック・ユニットを定義し、各リージョンで従業員の給与に範囲バリデータを指定します。Employee
エンティティから行がロードされると、(EmpRegion
属性の値に基づいて)EmpSalary
属性の適切なバリデータがロードされます。
別の例をあげると、Orders
エンティティ・オブジェクトは識別子属性としてOrderFilled
を使用するFilledOrderGroup
と呼ばれるビジネス・ロジック・グループを持ちます。この属性には2つの有効な値(N
およびY
)があるため、対応するビジネス・ロジック・ユニットが2つあります。
このシナリオでは、各ビジネス・ロジック・ユニットにはそのユーザー・タイプのみに関連する新規または修正済ビジネス・ロジックが含まれます。
Orders_FilledOrderGroup_N
ビジネス・ロジック・ユニットには、まだ入力されていない注文に関連するロジックが含まれます。たとえば、DateShipped
属性を非表示にするように構成できます。
Orders_FilledOrderGroup_Y
ビジネス・ロジック・ユニットには、入力されている注文に関連するロジックが含まれます。たとえば、DateShipped
属性を表示するように構成できます。
エンティティ・オブジェクトのビジネス・ロジック・グループは、概要エディタから作成します。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・グループを作成するには:
「アプリケーション」ウィンドウで、ビジネス・ロジック・グループを作成するエンティティをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックします。
「一般」ページで、「ビジネス・ロジック・グループ」セクションを拡張し、「追加」アイコンをクリックします。
作成ダイアログで、適切なグループ識別子属性を選択し、グループの名前を指定します。
ヒント: コードの可読性を高めるには、識別子を反映するようなグループ名にします。たとえば、グループ識別子属性がOrderFilled の場合は、ビジネス・ロジック・グループの名前をFilledOrderGroup にします。 |
「OK」をクリックします。
新しいビジネス・ロジック・グループが概要エディタの表に追加されます。グループを作成すると、ビジネス・ロジック・ユニットを追加できます。
ビジネス・ロジック。ユニットは「新規ギャラリ」から作成できます。また、ビジネス・ロジック・グループを格納するエンティティのポップアップ・メニューから直接作成することもできます。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットを作成するには:
「アプリケーション」ウィンドウで、ビジネス・ロジック・グループを格納するエンティティ・オブジェクトを右クリックし、新規エンティティ・ビジネス・ロジック・ユニットを選択します。
「ビジネス・ロジック・ユニットの作成」ダイアログで、ベース・エンティティの名前を指定し、適切なビジネス・ロジック・グループを選択します。
ビジネス・ロジック・ユニットの名前を入力します。
各ビジネス・ロジック・ユニットの名前には、このビジネス・ロジック・グループに関連付けられているグループ識別子属性の有効値を反映する必要があります。たとえば、グループ識別子属性がOrderFilled
の場合、Y
のOrderFilled
値に関連付けられているビジネス・ロジック・ユニットの名前はY
にする必要があります。
ビジネス・ロジック・ユニットのパッケージを指定します。
注意: ビジネス・ロジック・ユニットのパッケージは、ベース・エンティティまたはビジネス・ロジック・グループのパッケージと同じにする必要はありません。これにより、コア・アプリケーションと別にビジネス・ロジック・ユニットを開発して配信できます。 |
「OK」をクリックします。
JDeveloperでは、概要エディタでビジネス・ロジック・ユニットを作成して開きます。「アプリケーション」ウィンドウのビジネス・ロジック・ユニットに表示される名前は、EntityName_BusLogicGroupName_BusLogicUnitName
の形式で、エンティティ・オブジェクトの名前とビジネス・ロジック・グループが含まれます。たとえば、Orders
エンティティ・オブジェクトのFilledOrderGroup
ビジネス・ロジック・グループにY
という名前でビジネス・ロジック・ユニットを作成する場合、表示されるビジネス・ロジック・ユニット名はOrders_FilledOrderGroup_Y
になります。
ビジネス・ロジック・ユニットを作成すると、そのビジネス・ロジック・ユニットに対してビジネス・ロジックを再定義できます。
ビジネス・ロジック・ユニットを作成したら、これを概要エディタで開き、ベース・エンティティで行う場合(エンティティレベルのバリデータを追加するなど)と同様に、ビジネス・ロジックを追加できます。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットにエンティティ・バリデータを追加するには:
「アプリケーション」ウィンドウで、エンティティ・バリデータを追加するビジネス・ロジック・ユニットをダブルクリックします。
概要エディタで、「ビジネス・ルール」ナビゲーション・タブをクリックします。
「ビジネス・ルール」ページで、「エンティティ・バリデータ」ノードを選択し、「追加」アイコンをクリックします。
「検証ルールの追加」ダイアログで、検証ルールを定義し、「OK」をクリックします。
概要エディタでビジネス・ロジック・ユニットの「属性」ページを表示すると、属性表の「拡張」列に、属性がビジネス・ロジック・ユニットで拡張されていることが表示されます。拡張属性は、ビジネス・ロジック・ユニットではなく、ベース・エンティティでのみ編集できます。ベース・エンティティではなくビジネス・ロジック・ユニットで変更する場合は、編集する前に、ビジネス・ロジック・ユニットで属性をオーバーライド済として定義する必要があります。
始める前に:
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットの属性をオーバーライドするには:
「アプリケーション」ウィンドウで、オーバーライドする属性を含むビジネス・ロジック・ユニットをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックします。
「属性」ページで、目的の属性を選択し、 「オーバーライド」ボタンをクリックします。
属性をオーバーライドしたら、次の表にあるタブで通常どおりに属性を編集できます。オーバーライドされた属性では、コントロール・ヒント、バリデータおよびデフォルト値の編集のみ行えます。
ビジネス・ロジック・グループを作成すると、ベース・エンティティのXMLファイルのグループに参照が追加されます。例4-6は、ビジネス・ロジック・グループのベース・エンティティのXMLファイルに追加されたコードを示しています。
例4-6 ビジネス・ロジック・グループのベース・エンティティのXMLコード
<BusLogicGroup Name="FilledOrderGroup" DiscrAttrName="OrderFilled"/>
ビジネス・ロジック・ユニットを作成すると、エンティティ・オブジェクトのXMLファイルと同様のXMLファイルが生成されます。例4-7は、ビジネス・ロジック・ユニットのXMLコードを示しています。
注意: ビジネス・ロジック・ユニットのパッケージは、ベース・エンティティまたはビジネス・ロジック・グループのパッケージと同じにする必要はありません。これにより、コア・アプリケーションと別にビジネス・ロジック・ユニットを開発して配信できます。 |
例4-7 ビジネス・ロジック・ユニットの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式を使用して一時属性の値を指定できます。
エンティティ・オブジェクトには、通常のエンタープライズ・ビジネス・アプリケーションの実装を簡略化するための様々な宣言的機能が用意されています。タスクによっては、宣言的機能のみでニーズが満たされる場合があります。エンティティ・オブジェクトの基本的な永続機能を記述する宣言的な実行時機能はこの項で説明しますが、宣言的な検証およびビジネス・ルールについては、第11章「検証とビジネス・ルールの宣言的な定義」で説明します。
注意: 必要に応じて、宣言的動作を超えて、より複雑なビジネス・ロジックまたは検証規則をビジネス・ドメイン・レイヤーに実装することができます。カスタム・コードを使用した最も一般的なエンティティ・オブジェクトの拡張方法は、第12章「プログラムによる検証とビジネス・ルールの実装」を参照してください。 |
またアプリケーションを開発する際の注意点として、プログラム的または宣言的にビジネス・ロジックを実装する場合、エンティティ・オブジェクトまたはビュー行の属性が特定の順序に従って設定されることを想定しないようにすることが重要です。順序が想定されていると、それとは異なる順序でエンド・ユーザーが属性値を入力した場合に問題が発生します。
エンティティ・オブジェクトの宣言的な実行時動作を構成するには、概要エディタを使用します。
始める前に:
実行時動作の宣言的な構成に関する知識が役立つ場合があります。詳細は、4.9項「宣言的な実行時動作の構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトの宣言的な実行時動作を構成するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックしてエンティティ・オブジェクトの名前およびパッケージを表示し、関連付けられたスキーマ、代替キー、カスタム・プロパティおよびセキュリティなど、オブジェクトのエンティティ・レベルでの様々な構成を行います。
「代替キー」セクションでは、代替主キーとして機能できる、データベースにマッピングされたエンティティ・オブジェクト属性を選択できます。代替キーの詳細は、4.10.15項「代替キー値の定義方法」を参照してください。
「チューニング」セクションでは、単一のトランザクションで同じタイプの複数のエンティティを作成、変更または削除するときのデータベース操作をより効率的にするためのオプションを設定できます。詳細は、4.9.3項「バッチ更新の使用方法」を参照してください。
「カスタム・プロパティ」セクションでは、実行時にエンティティでアクセス可能なカスタム・メタデータを定義できます。
「セキュリティ」セクションでは、エンティティについてロール・ベースの更新可能権限を定義できます。詳細は、第41章「Fusion WebアプリケーションでのADFセキュリティの有効化」を参照してください。
「ビジネス・ロジック・グループ」セクションでは、ビジネス・ロジック・グループと追加および削除できます。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
「属性」ナビゲーション・タブをクリックして、エンティティ・オブジェクトに関連するデータを示す属性を作成または削除し、検証規則、カスタム・プロパティおよびセキュリティなど、属性の様々な構成を行います。
属性を選択し、「編集」アイコンをクリックして属性のプロパティにアクセスします。これらのプロパティの設定方法の詳細は、4.10項「属性プロパティの設定」を参照してください。
ヒント: エンティティの属性名のリストが長い場合、目的の属性を簡単に見つける方法があります。「構造」ウィンドウの「属性」ノードが開かれた状態で属性名を入力すると、入力した文字に応じて属性がインクリメンタル検索され、ツリー内のその名前が表示されます。 |
「ビジネス・ルール」ナビゲーション・タブをクリックして、エンティティ・オブジェクトとその属性の宣言的バリデータを定義します。スクリプト・ボックスではビジネス・ルール用のGroovyスクリプトを表示および編集でき、デバッグ用のブレークポイントを設定できます。詳細は、第11章「検証とビジネス・ルールの宣言的な定義」を参照してください。
「Java」ナビゲーション・タブをクリックし、カスタムJava実装に対して生成するクラスを選択します。Javaクラスは、プログラム的なビジネス・ルールの定義などに使用できます(第12章「プログラムによる検証とビジネス・ルールの実装」を参照)。
「ビジネス・イベント」ナビゲーション・タブをクリックし、エンティティ・オブジェクトが、その状態の変更を他に通知する際に使用できるイベント(およびオプションで、配信イベントに含めるエンティティ・オブジェクトの属性の一部または全部)を定義します。ビジネス・イベントの詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
「ビュー・アクセッサ」ナビゲーション・タブをクリックし、ビュー・アクセッサを作成および管理します。詳細は、14.4.1項「エンティティ・オブジェクトまたはビュー・オブジェクトのビュー・アクセッサの作成方法」を参照してください。
エンティティ・オブジェクトの実行時動作を記述および制御する宣言的設定は、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で動作しません。
始める前に:
実行時動作の宣言的な構成に関する知識が役立つ場合があります。詳細は、4.9項「宣言的な実行時動作の構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティに対してバッチ更新を有効化する手順:
「アプリケーション」ウィンドウで、一括更新するエンティティをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックします。
「一般」ページで「チューニング」セクションを開き、「バッチ更新を使用」チェック・ボックスを選択して、適切なしきい値を指定します。
これにより、それを超えるとOracle ADFが一括DML操作を使用して変更を処理する、バッチ処理のしきい値が指定されます。
宣言的なフレームワークによって、属性プロパティを簡単に設定できます。いずれの場合も、これらのプロパティは概要エディタの「属性」ページで設定します。
「永続的」
プロパティにより、基礎となる表内の列に属性値が対応するか、単なる一時的な値であるかを制御します。属性が永続的である場合、「データベース列」領域を使用して、属性に対応する基礎となる列の名前を変更し、精度およびスケール情報を使用してその列型を指定できます(VARCHAR2(40)
やNUMBER(4,2)
など)。エンティティ・オブジェクトは、実行時にこの情報に基づいて、属性値の最大長または精度/スケールを設定し、値が要件を満たさない場合は例外をスローします。
「表からのビジネス・コンポーネント」ウィザードと「エンティティ・オブジェクトの作成」ウィザードでは、各エンティティ・オブジェクト属性のJava型は、関連付けられている列のデータベース列型のSQL型から推測されます。
注意: プロジェクトの「型マップ」設定は、Javaデータ型の決定にも役割を果します。ビジネス・コンポーネント・プロジェクトを初期化する場合は、ビジネス・コンポーネントが作成される前に「型マップ」設定を指定します。詳細は、3.2.3項「データベース接続を使用してデータ・モデル・プロジェクトを初期化する方法」を参照してください。 |
(「詳細」タブの)「型」フィールドでは、エンティティ属性のJava型を目的の型に変更できます。「列の型」フィールドには、属性がマップされている、基礎となるデータベース列のSQL型が表示されます。「列名」フィールドの値により、属性がマップされる列が制御されます。
エンティティ・オブジェクトは、表4-1に示す様々な列型の表を処理できます。デフォルトのJava属性タイプはすべてoracle.jbo.domain
、oracle.ord.im
および各種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
オブジェクトでこれら複数の主キー属性の順序が間違っていると、エンティティ行が期待したとおりに検出されません。
さらに、主キーを新しい行に移入するには、トリガーを使用してデータベースから値を割り当てる場合もあります。詳細は、4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照してください。
値のタイプが「リテラル」に設定されている場合、「詳細」タブの「値」フィールドには、属性のデフォルト値を静的に指定できます。たとえば、ServiceRequest
エンティティ・オブジェクトのStatus
属性のデフォルト値をOpen
に設定したり、User
エンティティ・オブジェクトのUserRole
属性のデフォルト値をuser
に設定できます。
注意: 1つのエンティティ・オブジェクトに複数の属性がデフォルト設定される場合は、エンティティ・オブジェクトのXMLファイルにおいてそれらの属性が表示されている順にデフォルト設定されます。 |
属性のデフォルト値は、Groovy式またはSQL文を使用して定義できます。実行時にデフォルト値を動的に定義できるようにする場合はこの方法が便利ですが、デフォルト値が常に同じ場合は、(「詳細」タブの)「リテラル」型のフィールドを使用すると、より簡単に値を表示および管理できます。Groovyの使用の一般的な情報は、3.5.6項「ビジネス・コンポーネントでのGroovyスクリプト言語の使用方法」を参照してください。
始める前に:
属性プロパティの設定方法の知識があると役立ちます。詳細は、4.10項「属性プロパティの設定」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
式を使用してデフォルト値を定義するには:
「アプリケーション」ウィンドウで、デフォルト値を定義する属性が含まれているエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックします。
「属性」ページで編集する属性を選択し、「詳細」タブをクリックします。
「詳細」ページで、値タイプを選択して、「値」フィールドの「編集」アイコンをクリックします。
Groovy式を使用する場合は、「式」を選択します。
SELECT文を使用する場合は、「SQL」を選択します。
表示されているフィールドにGroovy式またはSQL文を入力し、「OK」をクリックします。
Groovy式を使用してデフォルト値を定義すると、<TransientExpression>
タグがエンティティ・オブジェクトのXMLファイルの適切な属性内に追加されます。例4-8に、デフォルト値に現在の日付を返すGroovy式のサンプルXMLコードを示します。
基礎となる列値が、挿入または更新の操作時にデータベース・トリガーによって更新される場合、「詳細」タブの「挿入時にリフレッシュ」チェック・ボックスまたは「更新時にリフレッシュ」チェック・ボックスをそれぞれ選択することにより、変更された値をフレームワークが自動的に取得し、エンティティ・オブジェクトとデータベース行を同期化できます。エンティティ・オブジェクトは、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 このようなデータベース制限を回避する技術は、4.18項「結合ビューまたはリモートDBLinkに基づくエンティティ・オブジェクト」を参照してください。 |
挿入後に属性がリフレッシュされる一般的なケースは、主キー属性の値がBEFORE INSERT FOR EACH ROW
トリガーによって割り当てられる場合です。多くの場合、このトリガーにより、PL/SQLロジックを使用してデータベース順序から主キーが割り当てられます。例4-9は、この例を示しています。
例4-9 データベース順序から主キーを割り当てる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
)に設定でき、主キーが自動的にデータベース順序によって割り当てられます。このデータ型を設定すると、「挿入時にリフレッシュ」チェック・ボックスが自動的に選択されます。
注意: 「順序」タブに表示されている順序名が設計時に使用されるのは、4.2.6項「エンティティ・オブジェクトからのデータベース表の作成方法」で説明されているデータベース表の作成機能を使用する場合のみです。ここで示す順序は、エンティティ・オブジェクトの基礎となる表とともに作成されます。 |
主キーがDBSequence
である新しいエンティティ行を作成すると、一意の負の数値が一時値として割り当てられます。この値は、このエンティティ行を作成するトランザクション中、主キーとして機能します。同じトランザクション内で一連の相関エンティティを作成する場合、その他の新しい関連エンティティ行にこの一時値を外部キー値として割り当てることができます。トランザクションのコミット時に、エンティティ・オブジェクトは、RETURNING INTO
句を使用してINSERT
操作を発行し、実際にデータベースのトリガーによって割り当てられた主キー値を取得します。コンポジット関連では、先にこの一時的な負の値を外部キーとして使用していた新しい関連エンティティでは、マスターの実際の新しい主キーを反映してこの値が更新されます。
通常は、DBSequence値を持つ主キーの「更新可能」プロパティも「なし」に設定します。一時IDはエンティティ・オブジェクトによって割り当てられ、INSERT
操作の後に実際のID値を使用してリフレッシュされます。エンド・ユーザーがこの値を更新する必要はありません。
コンポジット以外のアソシエーションでこの機能を実装する方法の詳細は、4.14.7.3.3項「DBSequenceの値が設定される主キーに基づくアソシエーション」を参照してください。
実行時には、フレームワークによりエンティティ・オブジェクトの更新の上書きが検出され、作業中に別のユーザーによって更新およびコミットされたデータであることに気付かずにユーザーがこのデータを変更できないようにします。通常、このチェックは、基礎となる行がロックされているときにデータベース内で対応する現在の列値と各永続エンティティ属性の元の値を比較することによって実行されます。行の更新前に、エンティティ・オブジェクトによって、更新される行とデータベースの現在の状態の間に一貫性があるかどうかが検証されます。行とデータベースの状態の間に一貫性がない場合、エンティティ・オブジェクトではRowInconsistentException
が発生します。
更新の上書きの検出をより効率的にするには、エンティティが変更されるたびに値が更新されるエンティティの属性を特定します。通常、このような値には、行内のバージョン番号列や更新日付列などがあります。(「詳細」タブの)「挿入時にリフレッシュ」オプションまたは「更新時にリフレッシュ」オプションを選択したので、作成したデータベース・トリガーによって更新識別子の属性値を割り当て、エンティティ・オブジェクトでこれらの値をリフレッシュできます。また、4.10.12項「履歴列を使用して作成および変更した日付を追跡する方法」で説明する履歴属性の機能を使用してエンティティ・オブジェクトによって更新識別子の属性値の更新が管理されるように指定できます。
ユーザーによる問合せ以降にエンティティ行が変更されたかどうかを最も効率的な方法で検出するには、「更新識別子」オプションを選択し、更新識別子の属性値のみを比較します。更新識別子属性のないエンティティ・オブジェクトはすべての属性の値がデータ一貫性チェックで使用されるため効率が低下する場合があります。更新識別子属性のないエンティティ・オブジェクトは、ユーザー・セッションがアプリケーション・モジュールとのアフィニティを失った(アプリケーション・モジュール・プールが使用されている場合)ときにデータの一貫性を失う可能性もあります。詳細は、50.3.3項「状態管理とデータの整合性に関する必知事項」を参照してください。アプリケーション・モジュール・プーリングの詳細は、第51章「アプリケーション・モジュール・プールのチューニング」を参照してください。
エンティティの作成日時や変更日時、作成者や変更者、エンティティの変更回数など、エンティティ・オブジェクトで履歴情報を追跡する必要がある場合、「変更履歴のトラッキング」オプションが選択されている属性を指定します(「詳細」タブ)。
属性のデータ型がNumber
、String
またはDate
であり、主キーの一部ではない場合、このプロパティを有効化して履歴監査用の属性値を自動的に管理できます。フレームワークにおける属性の処理方法は、次のような履歴属性のタイプの中からどれを指定するかによって異なります。
作成日付: この属性には、行が作成された日付のタイム・スタンプが移入されます。タイム・スタンプはデータベースから取得されます。
作成者: この属性には、行を作成したユーザーの名前が移入されます。ユーザー名は、Session
オブジェクトのgetUserPrincipalName()
メソッドを使用して取得されます。
変更日付: この属性には、行が更新/作成された日付のタイム・スタンプが移入されます。
変更者: この属性には、行を作成/更新したユーザーの名前が移入されます。
バージョン番号: この属性には、行が作成または更新されるたびに増分するlong値が移入されます。
エンティティ・オブジェクトでは、ItemEO
エンティティを作成するOrdEO
エンティティなど、他のエンティティを作成(または構成)する場合、コンポジット動作が示されます。この付加的な実行時動作により、ネストされた他のエンティティ・オブジェクト部分の論理コンテナとしての役割が決定されます。この関係により、コンポジット・アソシエーションは、一時属性をベースに作成できません。
エンティティ・オブジェクトの構成で常に有効な機能については、次の項を参照してください。
別の機能およびこれらの動作に影響するプロパティについては、次の項を参照してください。
構成されるエンティティ・オブジェクトが作成されると、この値が既存のエンティティを所有する親のエンティティとして識別しているか確認するため、外部キー属性の値の存在がチェックされます。作成時に外部キーが検出されなかった場合や、既存のエンティティ・オブジェクトを識別しない値が検出された場合、エンティティ・オブジェクトでは、親エンティティが識別されていない状態で親のない子行が作成されないように、InvalidOwnerException
がスローされます。
注意: 外部キー属性の値の存在がチェックされたことにより、現在のトランザクションで新しい保留エンティティが検索される他、必要に応じてデータベース内の既存のエンティティが検索されます。 |
コンポジット動作により、構成する側のエンティティ・オブジェクトと構成される側のエンティティ・オブジェクトの両方が含まれるトランザクションで実行されるデータ操作言語(DML)操作が正しい順序で実行されたかどうかを確認します。たとえば、構成する側の新しい親エンティティ・オブジェクトに対するINSERT
文は、構成される側の子エンティティ・オブジェクトに関連するDML操作の前に実行されます。
主キーが挿入時にリフレッシュするように構成されている新しいエンティティ行を保存すると、トリガーによって割り当てられた主キー値の取得後に、構成される側のエンティティの外部キー属性値が更新されて新しい主キー値が反映されます。
これ以外にも、アソシエーション作成ウィザードの「アソシエーション・プロパティ」ページまたは概要エディタの設定を介して制御可能なコンポジット関連機能が多数用意されています。図4-13は、2つのエンティティ・オブジェクトItemEO
とOrdEO
の間のSItemOrdIdFkAssoc
アソシエーションが表示された「関連」ページを示しています。
構成される側の子が存在していても、構成する側の親の削除を有効化または禁止できます。「カスケード削除の実装」オプション(図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
オブジェクトを拡張し、別のプロパティや動作を追加します。「多相化識別子」属性設定を使用して、行のタイプを識別する属性の値を示します。継承の設定方法および使用方法は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
データベースの主キーは順序から生成されることが多く、様々な理由からユーザーへの公開を避けることが必要な場合があります。このため、多くのケースでは一意の代替キー値を使用すると便利です。たとえば、すべての顧客に一意の電子メール・アドレスを持たせるとします。顧客は電子メール・アドレスを変更する可能性があるため、主キーとしてはこの値を使用できませんが、それぞれの顧客に対してログインなどの目的で使用できる一意のフィールドを持たせる必要があります。
代替キーは、メソッドのfindByKey
クラスを介して直接行を参照する場合に便利です。代替キーは、中間層で効率的な一意性チェックを行う場合にもよく使用されます。値が一意であるかどうかの確認方法の詳細は、11.5.1項「キー値の一意性を確認する方法」を参照してください。
代替キーの定義には、「エンティティ制約の作成」ウィザードを使用します。
始める前に:
属性プロパティの設定方法の知識があると役立ちます。詳細は、4.10項「属性プロパティの設定」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
代替キー値を定義するには:
「アプリケーション」ウィンドウで、代替キーを定義するエンティティ・オブジェクトを右クリックし、新規のエンティティ制約を選択します。
「エンティティ制約の作成」ウィザードの手順に従って、制約の名前を指定し、キーとして使用する属性(複数可)を選択します。
「プロパティ」ページで、「代替キー」を選択し、適切な「キー・プロパティ」オプションを選択します。
「キー・プロパティ」の詳細情報は、[F1]キーを押すか「ヘルプ」をクリックすると参照できます。
一意キー制約は、データベース内のUNIQUE
制約の順方向生成にのみ使用され、代替キー値には使用されません。
基礎となる表内の列にマップされる属性のみでなく、エンティティ・オブジェクトには、値ホルダーである一時属性や(JavaやGroovyなどを使用して)計算された値を表示する一時属性を組み込むことができます。たとえば、FullName
などの作成した一時属性は、FirstName
属性の値とLastName
属性の値を連結した値に基づいて計算できます。
一時属性を作成すると、エンティティ・オブジェクトのJavaクラスで計算を実行したり、属性定義でGroovy式を使用してデフォルト値を指定できます。
実行時に値を計算する方法を変更できるようにする必要がある場合は、Groovy式を使用します。値を計算する方法が変更される可能性が低い場合は(明細項目の合計など)、エンティティ・オブジェクトのJavaクラス内で直接計算できます。
一時属性を作成するには、概要エディタの「属性」ページを使用します。
始める前に:
一時属性および計算属性の使用に関する知識があると役立ちます。詳細は、4.11項「エンティティ・オブジェクトへの一時属性および計算属性の追加」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトに一時属性を追加するには:
「アプリケーション」ウィンドウで、一時属性を追加するエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックし、「新規」アイコンをクリックして、「新規属性」を選択します。
「新規属性」ダイアログで、属性の名前を入力し、「タイプ」ドロップダウン・リストからJava属性を選択し、「OK」をクリックします。
概要エディタの「属性」ページで、「詳細」タブをクリックし、「一時」ラジオ・ボタンを選択します。
値が計算される場合は、「更新可能」 ドロップダウン・リストから「使用しない」を選択します。
一時属性を追加すると、エンティティ・オブジェクトのXMLドキュメントが更新され、新しい属性が反映されます。例4-10
に示すように、一時属性の<Attribute>
タグには、TableName
またはColumnNameはありません。
例4-10 一時属性のXMLコード
<Attribute Name="FullName" IsUpdateable="false" IsQueriable="false" IsPersistent="false" Type="java.lang.String" SQLType="VARCHAR" > </Attribute>
これに対し、永続的エンティティ属性には、例4-11
のようにTableName
とColumnNameの両方があります。
一時属性を作成する場合、デフォルト値の指定にGroovy式を使用できます。
始める前に:
一時属性および計算属性の知識があると役立ちます。詳細は、4.11項「エンティティ・オブジェクトへの一時属性および計算属性の追加」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
Groovy式に基づいて一時属性を作成するには:
「アプリケーション」ウィンドウで、一時属性を追加するエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックし、「新規」アイコンをクリックして、「新規属性」を選択します。
「新規属性」ダイアログで、属性の名前を入力し、「タイプ」ドロップダウン・リストからJava属性を選択し、「OK」をクリックします。
概要エディタの「属性」ページで、「詳細」タブをクリックし、「一時」オプションを選択します。
値が計算される場合は、「更新可能」 ドロップダウン・リストから「使用しない」を選択します。
デフォルト値タイプの「式」を選択し、 「値」フィールドの「編集」ボタンをクリックします。
定義した式は、Groovyスクリプト言語を使用して評価されます(3.5.6項「ビジネス・コンポーネントでのGroovyスクリプト言語の使用方法」を参照)。Groovyにより式や変数を文字列に挿入できます。この式は、エンティティ・オブジェクト定義の一部として保存されます。
図4-14に示すように、「式の編集」ダイアログで、表示されているフィールドに式を入力します。
参照する属性には、エンティティ・オブジェクトで定義されている任意の属性を使用できます。エンティティ・オブジェクトで定義されていない式内の属性を参照するには、適切なコンテキスト・オブジェクトを使用する必要があります。
この属性が依存する属性を指定します。
図4-14では、QuantityShipped
およびPrice
属性が選択されているので、いずれかの属性が変更されると属性は再計算されます。
「OK」をクリックして、式を保存します。
適切な再計算設定を選択します。
「常に」(デフォルト)を選択すると、行のいずれかの属性が変更されるたびに式の評価が行われます。「なし」を選択すると、行が作成された場合にのみ式の評価が行われます。
必要に応じて、式の再計算を実行するタイミングの条件を指定できます。
たとえば、「式値のリフレッシュ」フィールドに次のような式を入力すると、QuantityShipped
属性またはPrice
属性が変更された場合に属性が再計算されます。
return (adf.object.isAttributeChanged("QuantityShipped") || adf.object.isAttributeChanged("Price"));
注意: 値式またはオプションの再計算式がベース・エンティティ・オブジェクトの属性を参照する場合は、これを依存性として定義する必要があります。式を定義したときに「式の編集」ダイアログで依存性を指定しなかった場合は、、「依存性」タブ(「属性」ページ)でそれを実行できます。「依存性」タブで、「使用可能」リストで属性を特定し、それぞれを「選択済」リストに移動します。 |
一時属性がGroovy式に準拠している場合は、例4-12
に示すように<TransientExpression>タグがエンティティ・オブジェクトのXMLファイルの適切な属性内に追加されます。
一時属性は、データ値のプレースホルダです。一時属性の「更新可能」プロパティを「新規の間」または「常に」に変更すると、エンド・ユーザーは属性値を入力できるようになります。一時属性に計算値を表示する場合は通常、「更新可能」プロパティを「なし」に設定し、値を計算するカスタムJavaコードを記述します。
エンティティ・オブジェクトに一時属性を追加した後、この属性計算属性にするには、次のようにする必要があります。
概要エディタの「Java」ページでカスタム・エンティティ・オブジェクト・クラスを有効にし、アクセッサ・メソッドの生成を選択します。
一時属性のアクセッサ・メソッドの内部で、計算済値を戻すJavaコードを記述します。
「属性」ページの「依存性」タブで、一時属性の依存属性をそれぞれ指定します。
たとえば、ビュー行クラスを生成した後、一時属性の計算値を戻すJavaコードは、例4-13
のように属性のgetterメソッド(FullNameなど)にあります。
例4-13 一時属性のgetterメソッド
// 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をリフレッシュする適切なイベントを起動するには依存性が必要です。依存属性を指定するには、「属性」ページの「依存性」タブで、「使用可能」リストで属性を特定し、それぞれを「選択済」リストに移動します。
モデル・レイヤーから呼び出されたビジネス・イベントは、ビジネス・プロセスを起動する際や外部システム同期化をトリガーする際に役立ちます。
ビジネス・イベントはエンティティ・レベルで宣言的に定義します。また、それらのイベントを呼び出す条件を指定できます。指定の基準を満たすビジネス・イベントは、変更されたデータが正常にコミットされたときに呼び出されます。ビジネス・イベントは、エンティティ・オブジェクトが正常に作成、更新または削除されたときにMediatorに発行されます。
ビジネス・イベントを実装するには、次の処理を実行します。
4.12.4項「ビジネス・イベントの作成方法」の説明に従ってイベント定義を作成します。
4.12.7項「ビジネス・イベントの公開ポイントの定義方法」の説明に従って、イベント定義をイベント・ポイントにマップし、そのイベント定義を公開します。
イベント定義には、イベント・システムMediatorで公開および呼び出されるイベントが記述されます。イベント定義は、表4-2に示す要素とともに、エンティティ・オブジェクトのXMLファイルに格納されます。
イベント・ポイントは、イベントを呼び出せる場所です。コミットが正常に行われた時点で、表4-3に示すいずれかのイベント・ポイントをトランザクションのエンティティごとにMediatorに呼び出せます。
表4-3 Mediatorに呼び出されたイベント・ポイントの例
DML型 | イベント名 | イベントの説明 |
---|---|---|
CREATE |
EntityCreated |
新しいエンティティが作成されました。 |
UPDATE |
EntityUpdated |
既存のエンティティが更新されました。 |
DELETE |
EntityDeleted |
既存のエンティティが削除されました。 |
イベントはデフォルトで呼び出されることはなく、すべてのイベントがカスタムです。イベントを作成する場合、名前とDML操作を適宜指定できます。
イベント・ポイントごとに、どのイベント定義を特定のイベント・ポイントで呼び出すかを指定する必要があります。つまり、各イベント定義をイベント・ポイントに宣言的にマッピングする必要があります。
イベント・デリバリは現在のデータベース・トランザクションの外部で発生し、非同期です。そのため、次がサポートされています。
イベント配信がトランザクションの一部であるトランザクション・イベント配信は、このフレームワークではサポートされていません。
サブスクライバがイベントの受信を確認するまで公開元がその後の処理を待機する同期化イベントは、このフレームワークではサポートされていません。
ビジネス・イベントを作成するには、概要エディタの「ビジネス・イベント」ページを使用します。
始める前に:
ビジネス・イベントの動作方法に関する知識が役立つ場合があります。詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・イベントを作成する手順は、次のとおりです。
「アプリケーション」ウィンドウで、ビジネス・イベントを定義するエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「ビジネス・イベント」ナビゲーション・タブをクリックします。
「ビジネス・イベント」ページで、「イベント定義」セクションを拡張し、「新規」アイコンをクリックします。
「ビジネス・イベント定義の作成」ダイアログで、EmployeeContactInfoChanged
など、このイベントを説明する名前を指定します。
ペイロード表で、「新規」および「削除」をクリックし、このイベントの適切な属性を選択します。
または、セルをダブルクリックして、適切な属性を選択します。
注意: エンティティ属性列には、サポートされているタイプの属性のみ表示されます。ClobDomain 属性はサポートされていますが、非常に大きいCLOBデータはパフォーマンスに影響を与えることがあります。 |
「送信された値」フィールドで、値を「常に」送信するか、「変更された場合のみ」送信するか選択します。
「変更された場合のみ」オプションを選択すると、ペイロードでは属性がオプションとみなされるため、最高のパフォーマンスが得られます。デフォルトの「常に」のままにすると、値が変更されているかどうかを問わず、ペイロードは属性を必要とします。ペイロードの効率性の詳細は、4.12.6項「ペイロードに関する必知事項」を参照してください。
矢印ボタンを使用して、属性の順序を並べ替えます。
属性が表示される順序により、生成されたXSDでの順序が定義されます。XSDを使用してファブリック・メディエータとBPELプロセスを作成するため、最も頻繁にアクセスする属性を最上位に配置できます。
「OK」をクリックします。
定義するビジネス・イベントごとにこの手順を繰り返します。イベントを公開するには、4.12.7項「ビジネス・イベントの公開ポイントの定義方法」を参照してください。
ビジネス・イベントを作成すると、エンティティ・オブジェクトのXMLファイルがイベント定義により更新されます。例4-14は、ビジネス・イベントのXMLコードの例を示しています。またJDeveloperでは、必須属性やオプション属性を指定できるイベント・スキーマの関連XSDファイルが生成されます。必須属性は、「送信された値」-「常に」に設定されたもので、オプション属性は、「送信された値」を「変更された場合のみ」に変更されたものです。
例4-14 ビジネス・イベントのXMLコード
<EventDef Name="CustBusEvent1"> <Payload> <PayloadItem AttrName="Id"/> <PayloadItem AttrName="PaymentOptionId"/> <PayloadItem AttrName="PaymentTypeEO.Id" SendOnlyIfChanged="true"/> </Payload> </EventDef>
例4-15は、ビジネス・イベントのXSDイベント・スキーマの例を示しています。
例4-15 ビジネス・イベントの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>
例4-16は、エンティティ・オブジェクトのEDLイベント定義の例を示しています。
例4-16 エンティティ・オブジェクトの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>
ビジネス・イベントのペイロードは、関連付けられたエンティティ・オブジェクトの属性で構成されます。ビジネス・イベントのペイロード属性は、イベントの作成者によって定義されます。自動的に最適化されるわけではありません。イベントを定義するときに、属性を「常に」または「変更された場合のみ」送信するとしてマークできます。作成時に起動されるイベントでは、新しい値のみが送信されます。更新または削除時に起動されるイベントでは、新規および既存の値が送信されますが、「送信された値」設定に基づいて該当の属性のみです。最高のパフォーマンスを得るには、削除イベントでは主キー属性のみを含める必要があります。
コンポジット・シナリオ(発注書と明細項目など)をサポートするために、子エンティティは親エンティティで定義されたイベントを呼び出すことができ、子エンティティで定義されたイベントには親エンティティからの属性を含めることができます。子エンティティが親エンティティのイベントを呼び出す場合、子エンティティによるイベントの呼出し回数に関係なく、トランザクションごとに特定のトップレベル・エンティティについて1つのイベントのみが呼び出されます。
エンティティ・サブタイプのケースでは(たとえば、Staff
エンティティ・オブジェクトはPersons
エンティティのサブタイプです)、ADFビジネス・コンポーネントではビジネス・イベントのオーバーライドはサポートされません。ビジネス・イベントのサブスクライバは、イベント名を使用してイベントをリスンするため、イベントをオーバーライドすると、イベントのサブスクライバは自分に対するものではないペイロード・データを受信してしまう可能性があります。したがって、この機能はサポートされていません。
ビジネス・イベントを定義する際は、ClobDomain
属性はサポートされていますが、非常に大きなCLOBデータはパフォーマンスに影響が与える可能性があることを考慮してください。
ビジネス・イベントの公開ポイントを定義するには、エンティティ・オブジェクトの概要エディタの「ビジネス・イベント」ページを使用します。
始める前に:
ビジネス・イベントがアプリケーションで使用される方法に関する知識が役立つ場合があります。詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
4.12.4項「ビジネス・イベントの作成方法」の説明に従って、イベント定義が公開前に作成されている必要があります。
ビジネス・イベントの公開ポイントを定義する方法:
「アプリケーション」ウィンドウで、公開するビジネス・イベント含むエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「ビジネス・イベント」ナビゲーション・タブをクリックします。
「ビジネス・イベント」ページで、「イベント公開」セクションを拡張し、「イベント公開の編集」アイコンをクリックします。
「イベント公開の編集」ダイアログで、「新規」をクリックし、新しいイベントを作成します。
イベント列で新しいセルをダブルクリックし、適切なイベントを選択します。
「イベント・ポイント」列で対応するセルをクリックし、適切なイベント・ポイント・アクションを選択します。
オプションで、呼出し条件Raise Conditions表を使用して、イベントを呼び出す条件を定義できます。
「OK」をクリックします。
この章でこれまで説明してきたように、エンティティ・オブジェクトのデータベース対話機能および多くの宣言的実行時機能はすべて、カスタムJavaコードを使用せずに実現できます。これらの宣言的機能を超えてエンティティにカスタム・ビジネス・ロジックを実装する必要がある場合、カスタム・コードを必要とするエンティティに対してJava生成を有効化する必要があります。通常、カスタム・エンティティ・オブジェクトおよびエンティティ定義クラスで記述、使用およびオーバーライドする一般的なコードの詳細は、付録D「ADFビジネス・コンポーネントのよく使用されるメソッド」を参照してください。
ベスト・プラクティス: ADFビジネス・コンポーネントを初めて使用する開発者には、ベース・フレームワーク・クラスをオーバーライドして、すべての生成されたカスタムJavaクラスがカスタマイズされた動作を選択できるようにすることをお薦めします。この方法は、個々のエンティティ・オブジェクトおよびビュー・オブジェクトのカスタム・クラスの作成より優先されます。カスタム・コンポーネントを生成するときは、「プロジェクト・プロパティ」ダイアログの「ADFビジネス・コンポーネント」→「ベース・クラス」ページで、フレームワーク・クラスをオーバーライドするように指定します。詳細は、第16章「ビジネス・コンポーネント機能の拡張」を参照してください。 |
エンティティ・オブジェクトのカスタムJavaクラスの生成を有効にするには、概要エディタの「Java」ページを使用します。
始める前に:
カスタムJavaクラスに関する知識が役立つ場合があります。詳細は、4.13項「エンティティ・オブジェクトのカスタムJavaクラスの生成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトのカスタムJavaクラスを生成するには:
「アプリケーション」ウィンドウで、カスタムJavaクラスを生成するエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックし、「Javaオプションの編集」アイコンをクリックします。
「Javaオプションの選択」ダイアログで、生成するJavaクラスのタイプを選択します。
エンティティ・オブジェクト・クラス - 最も頻繁にカスタマイズします。このクラスは、基礎となるデータベース表の各行を示します。
エンティティ・コレクション・クラス - カスタマイズすることはまれです。
エンティティ定義クラス - カスタマイズする頻度は低くなります。このクラスは、エンティティ行を管理し、その構造を定義する関連クラスを示します。
「OK」をクリックします。
生成するカスタム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メソッドが生成されます。たとえば、OrdEO
エンティティ・オブジェクトには、対応するカスタム・クラスOrdEOImpl.java
があり、(例4-17に示すような)メソッドが生成されます。
警告: 最終的なハードコードされたメソッド名によりエンティティ行とビュー行の最適化またはカスタマイズが制限されるため、アクセッサはビジネス・ニーズ(ビュー行をJavaインタフェースとしてクライアントに公開する必要があるなど)がある場合のみ生成します。 |
例4-17 OrdEOImpl.javaのgetterメソッドとsetterメソッド
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-15のように、カスタムJavaクラスの生成を有効にした場合、これらは、エンティティ・オブジェクトの「アプリケーション・ソース」ノードの下にも子ノードとして表示されます。すべてのADFコンポーネントと同様、「アプリケーション」ウィンドウでエンティティ・オブジェクトを選択すると、「構造」ウィンドウには、エンティティの構造ビューが表示されます。カスタムJavaファイルのソース・コードを表示または操作する必要がある場合、ソース・エディタでファイルを開く方法は複数あります。
図4-15のように、Javaファイルを右クリックし、「開く」を選択します。
「構造」ウィンドウでノードの項目を右クリックし、「ソースへ移動」を選択します。
概要エディタの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
ベース・クラスによって管理される疎配列構造に格納されます。
多くの場合、このプライベート実装に関する詳細は、エンティティ・オブジェクトを使用する開発者は理解する必要がないため、重要ではありません。ただし、エンティティ・オブジェクトのカスタムJavaクラスを有効にする場合、エンティティ・オブジェクト・クラスで管理される生成コードの一部にこの実装の詳細が関わってきます。この場合、コードの使用目的を理解している方が賢明です。たとえば、Orders
エンティティ・オブジェクトのカスタムJavaクラスの場合、属性またはアクセッサ属性ごとに対応する生成済の整数定数があります。JDeveloperでは、これらの定数の値がXMLドキュメント内の属性の順序を正しく反映しているかどうかが確認されます。
また、自動的に管理される、エンティティ・オブジェクト・クラスで強く型付けされたgetterメソッドおよびsetterメソッドでは、これらの属性定数は例4-18のように使用されます。
例4-18 カスタム・エンティティJavaクラスの属性定数を使用する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 value to set the DateOrdered */ public void setDateOrdered(Date value) { setAttributeInternal(DATEORDERED, value); }
エンティティ属性定数に関連する管理コードのもう1つの側面は、getAttrInvokeAccessor()
およびsetAttrInvokeAccessor()
メソッドです。これらのメソッドにより、数値索引を使用した属性アクセスのパフォーマンスが最適化され、汎用処理の実行時にEntityImpl
ベース・クラスの汎用コードが属性値にアクセスするときの通常のパフォーマンスと同じになります。getAttrInvokeAccessor()
メソッドの例を例4-19に示します。もう1つのsetAttrInvokeAccessor()
メソッドの場合も同様です。
例4-19 カスタム・エンティティJavaクラスのgetAttrInvokeAccessor()メソッド
// 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> タグの順序がゼロベースの順序に反映されていることを確認する必要があります。 |
カスタム・エンティティ・クラスを参照する強く型付けされたエンティティを使用する場合と汎用エンティティ参照で作業する場合の違いをより明確に評価するには、次の例を検討してください。例4-20は、カスタム・アプリケーション・モジュール・クラス(AppModuleImpl.java
)内の強く型付けされたエンティティ参照のバージョンの1つを示しています。重要な違いは、次のとおりです。
属性アクセスは、強く型付けされた属性アクセッサを使用して実行されます。
アソシエーションのアクセッサ属性により、アソシエーションの反対側にある強く型付けされたエンティティ・クラスが戻されます。
カスタム・エンティティ・クラスのgetDefinitionObject()
メソッドを使用すると、完全修飾されたエンティティ定義名を文字列として使用しないようにできます。
カスタム・エンティティ・クラスのcreatePrimaryKey()
メソッドにより、エンティティのKey
オブジェクトの作成を簡略化します。
例4-20 強く型付けされたカスタム・エンティティ・オブジェクト・クラスを使用したプログラム的なエンティティの例
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"); } }
エンティティ・オブジェクトを直接操作する場合、UIベースのクライアントまたはプログラム的クライアントは常に必要ではありません。アプリケーション・モジュールにアクセスし、そのデータ・モデル内のビュー・オブジェクトを直接操作する際、外部クライアント・プログラム以外には使用する必要がない場合もあります。第5章「ビュー・オブジェクトを使用したSQL問合せの定義」では、強力なアプリケーションを構築するために、ビュー・オブジェクトの柔軟なSQL問合せ機能に、エンティティ・オブジェクトのビジネス・ロジックとデータベースとの自動対話機能とを組み合せる簡単な方法について説明します。この組合せにより、現在のエンド・ユーザーのタスク上のニーズに対応するよう設計され、再使用可能なドメイン・ビジネス・オブジェクト・レイヤー内で一元管理されたビジネス・ロジックを共有する、完全に更新可能なアプリケーション・モジュールのデータ・モデルを作成できます。
ただし、まず重要なのは、ビュー・オブジェクトとエンティティ・オブジェクトを組み合せた能力の利用方法について学習する前に、これらのオブジェクト自体の使用方法について理解することです。これらのオブジェクトについて詳細に学習することにより、これらを単体で使用する必要があるときと、独自のアプリケーションでこれらを組み合せる必要があるときについて理解が深まります。
クライアントはエンティティ・オブジェクトを直接操作できないため、エンティティ・オブジェクトをプログラム的に操作するために記述するコードは、通常カスタム・アプリケーション・モジュール・クラス内または別のエンティティ・オブジェクトのカスタム・クラス内のカスタム・コードになります。
エンティティ行にアクセスするには、エンティティ定義と呼ばれる関連オブジェクトを使用します。実行時には、エンティティ・オブジェクトごとに対応するエンティティ定義を利用して、エンティティの構造を記述し、それにしたがってエンティティ・オブジェクトのインスタンスを管理します。アプリケーション・モジュールを作成し、そのカスタムJavaクラスを有効化した後に、特定の注文を戻すメソッドを作成する必要があるとします。これは、例4-21
に示すretrieveOrderById()メソッドのようになります。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
主キーによるエンティティ・オブジェクトを検索するには:
エンティティ定義を検索します。
エンティティ・オブジェクト(Orders
など)のエンティティ定義を取得するには、EntityDefImpl
クラスからインポートされた静的なgetDefinitionObject()
メソッドにその完全修飾名を渡します。oracle.jbo.server
パッケージ内のEntityDefImpl
クラスにより、各エンティティ・オブジェクトのエンティティ定義が実装されます。
キーを作成します。
検索する主キー属性が含まれるKey
オブジェクトを作成します。たとえば、Orders
エンティティ・オブジェクトの場合、メソッドに引数として渡される単一のorderId
値が含まれるキーを作成します。
キーを使用してエンティティ・オブジェクトを検索します。
エンティティ定義のfindByPrimaryKey()
メソッドを使用して、キーによってエンティティ・オブジェクトを検索し、getDBTransaction()
メソッドを使用してアプリケーション・モジュールから取得できる現在のトランザクション・オブジェクトを渡します。エンティティ・オブジェクト行を示す具体的なクラスは、oracle.jbo.server.EntityImpl
クラスです。
オブジェクトまたはそのデータの一部をコール元に戻します。
例4-21は、この基本手順を使用して開発したretrieveOrderById()
メソッドのコードの例を示しています。
例4-21 キーによるOrderエンティティ・オブジェクトの取得
/* 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つのエンティティ・オブジェクトから別のエンティティ・オブジェクトへ簡単にアクセスする方法の詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
同じアクセッサ属性を使用して同じ関連付けられたエンティティを検索するアプリケーション・モジュールの既存メソッドとの競合を防ぐため、この機能をヘルパー・メソッドにリファクタし、必要に応じてアプリケーション・モジュールの任意の場所で再使用できます。たとえば、注文を検索する機能は、retrieveOrderById()
メソッド(例4-21を参照)によってリファクタされます。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
アクセッサ属性を使用して関連付けられたエンティティ・オブジェクトにアクセスするには:
アクセッサ属性を使用して、関連付けられたエンティティを検索します。
たとえば、findOrderAndCustomer()
メソッドでは、retrieveOrderById()
ヘルパー・メソッドを使用して、IDによってOrders
エンティティ・オブジェクトを取得します。
アクセッサ属性を使用して関連付けられたエンティティにアクセスします。
属性のgetterメソッドを使用して、アソシエーション・アクセッサ名を渡し、関連の反対側にあるエンティティ・オブジェクトを取得できます。(アソシエーション・アクセッサをより直感的な名前に変更する方法の詳細は、4.3.3項「エンティティ・アソシエーションのアクセッサ名の変更方法」を参照してください。)
データの一部をコール元に戻します。
たとえば、indOrderAndCustomer()
メソッドでは、戻されたCustomer
エンティティに対してgetterメソッドを使用して、割り当てられた顧客の名前と場所を戻します。
関連するCustomer
エンティティにアクセスするためにSQLを記述する必要はありません。Orders
エンティティ・オブジェクトとCustomer
エンティティ・オブジェクト間のADFアソシエーションで取得された関連情報のみで、データ・ナビゲーションの一般的なタスクを自動化できます。
例4-22に、ヘルパー・メソッドを使用するfindOrderCustomer()
のコードを示します。
例4-22 アクセッサ属性を使用した関連付けられたエンティティへのアクセス
/* 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; } }
エンティティ行を取得した後、エンティティ行は簡単に更新または削除できます。この処理は、例4-23
に示すupdateOrderStatus()のようなメソッドを追加することにより実行できます。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ行を更新するには:
IDによってOrderを検索します。
updateOrderStatus()
メソッドでは、retrieveOrderById()
ヘルパー・メソッドを使用して、IDによってOrders
エンティティ・オブジェクトを取得します。
1つ以上の属性を新しい値に設定します。
updateOrderStatus()
メソッドでは、EntityImpl
クラスのsetAttribute()
メソッドを使用して、OrderFilled
属性の値を渡された新しい値に更新します。
トランザクションをコミットします。
アプリケーション・モジュールのgetDBTransaction()
メソッドを使用して、現在のトランザクション・オブジェクトにアクセスし、commit()
メソッドをコールしてトランザクションをコミットする必要があります。
例4-23 既存のエンティティ行の更新
/* Update the status of an existing order */ public void updateOrderStatus(long orderId, String newStatus) { OrdersImpl order = retrieveOrderById(orderId); if (order != null) { order.setOrderFilled(newStatus); } }
エンティティ行の削除の例もこれと同じですが、既存のエンティティを検索した後、トランザクションをコミットする前に、かわりに次の行を使用してエンティティを削除する点が異なります。
// Remove the entity instead! order.remove();
エンティティ定義を使用して既存のエンティティ行を検索する以外にも、エンティティ定義を使用して新しいエンティティ行を作成することもできます。Customerエンティティの場合、例4-24
のようなcreateCustomer()メソッドを記述することにより、新しい顧客の名前や説明を取得し、この顧客に割り当てられた新しい顧客IDを戻すことができます。この例は、Customer
エンティティ・オブジェクトのId
属性がすでに更新され、その型がDBSequence
型になっていること(4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照)が前提となります。この設定により、アプリケーション・スキーマの表の順序から割り当てられたトリガーの値が、対応するデータベース表から反映されるように属性値がリフレッシュされます。
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ行を作成するには:
エンティティ定義を検索します。
createCustomer()
メソッドでは、getDefinitionObject()
メソッドを使用して、Customer
エンティティのエンティティ定義を検索します。
新しいインスタンスを作成します。
createCustomer()
メソッドでは、エンティティ定義に対してcreateInstance2()
メソッドを使用して、エンティティ・オブジェクトの新しいインスタンスを作成します。
注意: このメソッド名の末尾には2 があります。正規のcreateInstance() メソッドにはprotected アクセス権があり、付録D「ADFビジネス・コンポーネントのよく使用されるメソッド」のD.3.4項「EntityImplクラス」で説明するように、カスタマイズされるように設計されています。AttributeList 型の2番目の引数は、作成時に指定する必要がある属性値の指定に使用し、リストに検出されたすべての属性の値の初期化に使用するものではありません。たとえば、このAPIを使用して構成される側の子エンティティ行の新しいインスタンスを作成する場合、構成する側の親エンティティの外部キー属性を2番目の引数としてAttributeList オブジェクトに指定します。これを指定しない場合、InvalidOwnerException が発生します。 |
属性値を設定します。
createCustomer()
メソッドでは、エンティティ・オブジェクトに対して属性のsetterメソッドを使用して、新しいエンティティ行にName
やCity
などの属性の値を割り当てます。
トランザクションをコミットします。
createCustomer()
メソッドでは、現在のトランザクション・オブジェクトに対してcommit()
をコールし、トランザクションをコミットします。
トリガーによって割り当てられた顧客IDをコール元に戻します。
createCustomer()
メソッドでは、属性のgetterメソッドを使用してId
属性の値をDBSequence
として取得し、さらにgetSequenceNumber().longValue()
をコールして、順序番号をlong
値としてコール元に戻します。
例4-24 エンティティ行の新規作成
/* 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(); }
新規行を作成する場合、トリガーにより割り当てられた値を使用する(4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照)かわりに、Oracle順序を使用して値を主キーに割り当てることができます。このメタデータ駆動型の方法を使用すると、主キーを取得するためのコードの管理を、複数のエンティティ・オブジェクトにより再使用可能なJavaファイルに一元化できます。
例4-25に、エンティティ・オブジェクトが基づくCustomEntityImpl
という簡単なフレームワーク拡張クラスを示します。このクラスのオーバーライドされたcreate()
メソッドにより、SequenceName
という名前のカスタム属性レベルのメタデータ・プロパティが存在するかどうかがテストされ、検出された場合は、その順序内の次の番号から属性のデフォルト値が移入されます。
例4-25 CustomEntityImplフレームワーク拡張クラス
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()); } } } }
始める前に:
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
Oracle順序を使用して主キー値を割り当てるには:
プロジェクト内にCustomEntityImpl.java
ファイルを作成し、例4-25のコードを挿入します。
「アプリケーション」ウィンドウで、編集するエンティティをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックし、編集する属性を選択します。
「詳細」タブをクリックし、 「タイプ」ドロップダウン・リストから「数」を選択します。
「カスタム・プロパティ」タブをクリックし、「追加」アイコンをクリックします。
名前はSequenceName
で値はデータベース・シーケンスの名前にしてカスタム・プロパティを作成します。
たとえば、Dept
エンティティでは、DEPT_TABLE_SEQ
という値を持つDeptno
属性に対して、カスタム・プロパティSequenceName
を定義できます。
監査のため、表に追加した行を表から物理的に削除できない場合があります。そのような場合は、エンド・ユーザーがユーザー・インタフェースで行を削除する際に、DELETED
列の値をN
からY
に変更し、削除済としてマークします。この処理を行うために、2つのメソッドのオーバーライドを使用して、エンティティ・オブジェクトのデフォルト動作を変更できます。
そのためには、次のタスクを実行する必要があります。
4.14.6.1項「行が削除されたときの削除済フラグの更新」の説明に従って、行が削除されたときに削除済フラグを更新します。
4.14.6.2項「削除のかわりの更新DML操作の強制」の説明に従って、エンティティ・オブジェクトを削除するのではなく更新します。
行が削除されたときに削除済フラグを更新するには、エンティティ・オブジェクトのカスタムJavaクラスを有効にし、remove()
メソッドをオーバーライドして、super.remove()
メソッドを呼び出す前に削除済フラグを設定するようにします。例4-26は、エンティティ・オブジェクトのカスタムJavaクラスが、この変更でどのようになるかを示しています。削除された行の属性を設定しようとするとDeadEntityAccessExceptionが発生するため、super.remove()
を呼び出す前
に属性を設定することが重要です。
この例では、表を変更してDELETED
列を追加し、エンティティ・オブジェクトとデータベースの同期を取って、対応するDeleted
属性を追加してあるものとします。
例4-26 エンティティの行が削除されたときの削除済フラグの更新
// In your custom Java entity class public void remove() { setDeleted("Y"); super.remove(); }
行は行セットから削除されますが、エンティティ・キャッシュでDeleted
フラグの値はY
に変更されています。この動作の実装の第2の部分は、DML操作の実行を要求されたときに、エンティティにDELETE
のかわりにUPDATE
の実行を強制することです。完全なソリューションのためには、両方の部分を実装する必要があります。
エンティティ・オブジェクトを削除のかわりに更新するには、doDML()
メソッドをオーバーライドし、operation
フラグを条件付きで変更するコードを記述します。操作フラグがDML_DELETE
の場合は、このコードでかわりにDML_UPDATE
に変更します。例4-27は、エンティティ・オブジェクトのカスタムJavaクラスが、この変更でどのようになるかを示しています。
この例では、表を変更してDELETED
列を追加し、エンティティ・オブジェクトとデータベースの同期を取って、対応するDeleted
属性を追加してあるものとします。
例4-27 削除のかわりの更新DML操作の強制
// In your custom Java entity class protected void doDML(int operation, TransactionEvent e) { if (operation == DML_DELETE) { operation = DML_UPDATE; } super.doDML(operation, e); }
オーバーライドしたdoDML()
メソッドを配置し、4.14.6.1項
で説明したオーバーライドされたremove()メソッドを補完することで、対応するエンティティ・オブジェクトの慣用名を持つビュー・オブジェクトからエンティティを削除しようとすると、行が物理的に削除されるかわりに、列DELETED
が更新されます。削除された製品がビュー・オブジェクトの問合せ結果に表示されることを防ぐため、WHERE DELETED = 'N'
の製品のみを含むようにWHERE
句を変更する必要があります。
データベース制約のため、同じトランザクション内でDML操作を実行して複数の関連するエンティティ・オブジェクトに変更を保存するときは、操作の実行順序が重要になる場合があります。外部キー参照を含む新しい行を、参照されている行を挿入する前に挿入しようとすると、データベースで制約違反が発生する可能性があります。コミット時のエンティティ・オブジェクトのデフォルトの処理順序、および必要に応じてその順序をプログラムで変更する方法を理解している必要があります。
注意: この項の例は、SummitADF_Examples アプリケーション・ワークスペースのoracle.summit.model.controlpostorder パッケージを参照しています。 |
デフォルトでは、トランザクションをコミットすると、保留中の変更リスト内のエンティティ・オブジェクトは、時間順、つまりエンティティがリストに追加された順序で処理されます。つまり、たとえば、新しい従業員(EmpEO
エンティティ・オブジェクト)を作成、その後その製品に関連する新しい部門(DeptEO
エンティティ・オブジェクト)を作成すると、最初に新しいEmpEO
が、2番目に新しいDeptEO
が挿入されます。
2つのエンティティ・オブジェクトがコンポジットで関連付けられていると、厳密な時間順序が自動的に変更されて、コンポジット関係にある親と子のエンティティ行が制約に違反しない順序で保存されることが保証されます。たとえば、コンポジットされる新しい子のエンティティ行より前に、新しい親のエンティティ行が挿入されます。
関係のあるエンティティが関連付けられてはいてもコンポジットではない場合は、コードを少し作成して、関連するエンティティが正しい順序で保存されるようにする必要があります。
例4-28
のAppModule
アプリケーション・モジュールのnewEmployeeForNewDepartment()カスタム・メソッドについて考えます。このメソッドは、一連のパラメータを受け取って、次の処理を行います。
新しい従業員を作成します。
新しい部門を作成します。
従業員が関係する部門IDを設定します。
トランザクションをコミットします。
新しい従業員IDと部門IDを保持するためのResult
Java Beanを作成します。
結果を返します。
注意: このコードでは、EmpEO.Id とDeptEO.Id には、順序に基づいて主キーを設定するためDBSequence にデータ型が設定されているものと想定しています。 |
例4-28 新規従業員の作成後、新規部門を作成し、新規IDを返す
// 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()
メソッドをオーバーライドし、例4-29に示すようなコードを記述します。この例では、DeptEO
エンティティに対する外部キーが含まれているのはEmpEO
であるため、EmpEO
を更新し、条件によっては、サービス・リクエストをポストする前に関連する新しいDeptEO
を強制的にポストするようにします。
このコードでは、ポストしているエンティティがSTATUS_NEW
状態またはSTATUS_MODIFIED
状態のいずれかであるかを検査します。その場合は、getDeptEO()
アソシエーション・アクセッサを使用して、関連する製品を取得します。関連するDeptEO
のポスト状態もSTATUS_NEW
である場合は、super.postChanges()をコールして自分のDMLを実行する前に、まず
関連する親行のpostChanges()
をコールします。
例4-29 DeptEOを先にポストするためのEmpEOImpl.javaのpostChanges()のオーバーライド
// 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); }
この状態で例を再び実行すると、newEmployeeForNewDepartment()
メソッドのコードの作成順序を変更しなくても、エンティティは、新しいDeptEO
が先で新しいEmpEO
が後という正しい順序でポストされます。しかし、まだ問題があります。まだ制約違反が発生しますが、今度は理由が異なります。
DeptEO
エンティティ・オブジェクトの主キーがユーザーによって割り当てられている場合は、例4-29のコードのみで、ポスト順序を修正して制約違反に対処できます。
注意: このプログラム的な手法(Java EEアプリケーション・レイヤーで問題を解決する)のかわりに、データベース・レイヤーで遅延可能制約を使用する方法もあります。データベース・スキーマを制御できる場合は、外部キー制約をDEFERRABLE INITIALLY DEFERRED として定義する(または変更する)ことを検討します。このデータベース設定により、データベースは制約の検査をトランザクションのコミット時まで遅延します。これが実行されると、COMMIT 時までにすべての適切な関連する行が保存されている場合、アプリケーションはDML操作を任意の順序で実行でき、親/子の順序は緩和されます。ただし、親の主キーが順序から割り当てられる場合は、外部キーの値をカスケード更新するために、4.14.7.3.3項「DBSequenceの値が設定される主キーに基づくアソシエーション」と4.14.7.3.4項「DBSequence割当ての外部キーに対する参照の更新」で説明するコードを作成する必要があります。 |
しかし、DeptEO.Id
はデータベース順序から割り当てられ、ユーザー割当てではありません。そのため、新しいDeptEO
エンティティ行がポストされる時点で、データベースが割り当てる順序値を反映するように、Id
属性が更新されます。新しいサプライヤを参照するEmpEO.DeptId
属性の外部キー値は、サプライヤID値のこの更新により親を失います。製品行が保存されるときには、S_DEPT_ID
の値と一致する行がS_DEPT
表にまだ存在しないため、制約違反が再び発生します。次の2つの項では、このような親を失うという問題に対処するための解決策について説明します。
4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」で説明されているように、エンティティ・オブジェクトの主キー属性がDBSequence
型の場合は、そのオブジェクトが作成されるトランザクションの間、キーの数値は一時的に一意の負の値になります。同じトランザクション内で関連するエンティティの値を作成すると、これらの間の関連は、この一時的な負のキー値に基づくことになります。DBSequence
の値が主キーであるエンティティ・オブジェクトをポストすると、その主キーはデータベースが割り当てる正しい順序番号を反映して更新されますが、それに関連付けられたエンティティは一時的な負の外部キー値を保持したままで親を失います。
コンポジットに基づくエンティティ・オブジェクトの場合は、親エンティティ・オブジェクトのDBSequence
値が設定されている主キーが更新されると、コンポジットされる子エンティティ行の一時的な負の外部キー値は、親の更新されたデータベース割当ての主キーを反映して、自動的に更新されます。つまり、コンポジット・エンティティの場合、親を失うという問題は発生しません。
ただし、エンティティ・オブジェクトがコンポジットではないアソシエーションによって関連付けられている場合は、コードを作成して、一時的に負の値を参照する関連エンティティ行を、更新されたデータベース割当ての主キー値で更新する必要があります。次の項では、必要なコードについて説明します。
この例のDeptEO
のようなエンティティがDBSequence
値の主キーを持ち、それと関連付けられた(ただしコンポジットではない)他のエンティティによって外部キーとして参照される場合は、例4-30
に示すように、この新しいDeptEO行を参照する可能性のあるエンティティ行の行セットに対する参照を保存するように、postChanges()
メソッドをオーバーライドする必要があります。現在のDeptEO
行のステータスがNew
の場合は、getEmp()
アソシエーション・アクセッサから戻るRowSet
の値を、super.postChanges()
をコールする前に、newEmployeesBeforePost
メンバー・フィールドに割り当てます。
例4-30 新しいDepartmentを参照するエンティティ行に対する参照の保存
// 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); }
この保存したRowSet
オブジェクトは、後で例4-31
で示されているオーバーライドされたrefreshFKInNewContainees()メソッドで使用します。新しいエンティティ行は、このメソッドを呼び出すことで、postChanges()
を呼び出す前にそれを参照していた他のエンティティ行を更新された主キー値でカスケード更新できます。newEmployeesBeforePost
行セット(NULLでない場合)のEmpEOImpl
行を反復処理し、それぞれのサプライヤID値に、新しくポストされたDeptEO
エンティティの、順序から割り当てられた新しいサプライヤ値を設定します。
例4-31 新しいDeptId値のエンティティ行のカスケード更新
// 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(); } }
この変更を実装すると、例4-28のコードは、データベース制約違反を起こさないで動作するようになります。
ここでは、エンティティ・オブジェクト間のアソシエーションの処理に関する高度な手法について説明します。
対応する属性の等価性のみに基づく関連より複雑なエンティティ間の関連を表す必要があるときは、アソシエーションの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)
アソシエーションの作成の詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
2つのエンティティ・ベースのビュー・オブジェクト間のビュー・リンクを作成する際、「ビュー・リンク・プロパティ」ページに、ビュー・オブジェクト・レベルとエンティティ・オブジェクト・レベルの両方でビュー・リンク・アクセッサ属性を公開するオプションがあります。デフォルトでは、ビュー・リンク・アクセッサは、リンク先ビュー・オブジェクトのビュー・オブジェクト・レベルでのみ公開されます。「エンティティ・オブジェクト: SourceEntityName」チェック・ボックスまたは「エンティティ・オブジェクト: DestinationEntityName」チェック・ボックスから適切なものを選択することで、リンク元またはリンク先のエンティティ・オブジェクトの一方または両方にビュー・リンク属性をJDeveloperにより含めることができます。これにより、エンティティ・オブジェクトが関連するビュー行のセットにアクセスするための便利な方法が提供されます。特に、行を生成するための問合せが現在の行の属性にのみ依存する場合に有効です。
エンティティ・オブジェクト・クラスで実装するカスタム・メソッドは、アプリケーション・モジュールの戻り型に依存できません。実行時には、ある場合、そのような依存性を持って実行されるメソッドがClassCastException
をスローすることがあります。戻されたアプリケーション・モジュールが、期待された型と一致しないからです。そのため、実装するカスタム・メソッドには、次に示すように、特定のアプリケーション・モジュールやビュー・オブジェクトの実装を取得するコードを記述しないことをお薦めします。
((MyAM)getTransaction().getRootApplicationModule()).getMyVO
特に、次のシナリオにおいて、上のコードがClassCastException
で失敗します。
コード内のエンティティ・オブジェクトが、別のビュー・オブジェクト(メソッドで参照しているビュー・オブジェクト以外)のコンテキストで使用されている場合。アプリケーションでは、複数のビュー・オブジェクト定義が同じエンティティ・オブジェクトにマップされることがあるため、ビュー・オブジェクトへの依存性を持つメソッドが、ADFビジネス・コンポーネント・ランタイムで一貫性を持って実行されることは保証できません。
ルート・アプリケーション・モジュールの下に、手動でアプリケーション・モジュールをネストする場合。この場合、ネストされたアプリケーション・モジュールが同じTransaction
オブジェクトを共有するため、前述のコードによって、期待されるアプリケーション・モジュールの型が戻される保証はありません。
ADFビジネス・コンポーネント・フレームワークの実装がリリースを追うごとに変化する場合。たとえば、以前のリリースでは、アプリケーションがADFタスク・フローを使用して定義した宣言トランザクションを制御するために、フレームワークで内部的なルート・アプリケーション・モジュールが作成されていました。
複数のエンティティ・オブジェクトで似た属性値に対して同じ妥当性検査を繰り返し行っている場合は、この検証をカプセル化した独自のデータ型を作成することで、時間と労力を節約できます。たとえば、ビジネス・ドメイン・レイヤーに、電子メール・アドレスを表す文字列を格納する大量のエンティティ・オブジェクト属性がある場合を考えてみてください。ビジネス・ドメイン・レイヤーのどこでもエンド・ユーザーが有効な電子メール・アドレスを入力できるように保証する方法としては、次のようなものがあります。
各属性に対して基本のString
データ型を使用
Javaコードを使用して属性レベルのメソッド・バリデータを追加し、属性ごとにString
値の形式が有効な電子メール・アドレスであることを確認
しかし、このような方法は、大規模なアプリケーションでは急速に冗漫になります。ADFビジネス・コンポーネントで提供されている代替手段を使用すると、電子メール・アドレスを表す独自のEmailAddress
データ型を作成できます。電子メール・アドレスの値に関するすべての妥当性検査をこの新しいカスタム・データ型にまとめた後は、アプリケーション内にある電子メール・アドレスを表すすべての属性の型として、EmailAddress
を使用できます。このようにすると、属性値の意図が他の開発者に対していっそう明確になり、検証処理が1か所にまとまるためアプリケーションのメンテナンスが簡単になります。ADFビジネス・コンポーネントでは、これを開発者作成のデータ型ドメインと呼びます。
ドメインとは、String
、Number
、Date
などの基本データ型を拡張するJavaクラスであり、関連する妥当性検査に候補値が合格することを保証するコンストラクタ時の検証が追加されています。基本データ型検証、書式設定、カスタム・メタデータ・プロパティなどの横断的動作でカスタム・データ型を定義する手段を、ドメインを属性のJava型として使用する任意のエンティティ・オブジェクトまたはビュー・オブジェクトによって継承される方法で提供します。
注意: この項の例は、SummitADF_Examples アプリケーション・ワークスペースのoracle.summit.model.domains パッケージを参照しています。 |
ドメインを作成するには、ドメイン作成ウィザードを使用します。このウィザードは、「ADFビジネス・コンポーネント」カテゴリの「新規ギャラリ」で使用できます。
始める前に:
ドメインの使用に関する知識があると役立ちます。詳細は、4.15項「ドメインを使用したカスタム検証済データ型の作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ドメインを作成するには:
「アプリケーション」ウィンドウで、ドメインを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから、「ドメイン」を選択し、「OK」をクリックします。
ドメイン作成ウィザードの「名前」ページで、ドメインの名前とドメインが存在するパッケージを指定します。単純なJava型に基づくドメインを作成する場合は、「Oracleオブジェクト型のドメイン」を選択しないでおきます。
「次へ」をクリックします。
「設定」ページで、ドメインのベース型と、マップするデータベース列の型を指定します。
たとえば、8文字の短い電子メール・アドレスを保持するShortEmailAddress
という名前のドメインを作成する場合は、ベース型にString
を設定し、「データベース列の型」にVARCHAR2(8)
を設定します。このパネルでは、他の共通属性も設定できます。
「終了」をクリックします。
ドメインを作成すると、プロジェクトのソース・パスのサブディレクトリに、選択したパッケージ名に対応する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
をスローします。
翻訳可能な例外メッセージをスローするには、例4-32で示されているようなメッセージ・バンドル・クラスを作成できます。このクラスは、ドメイン・クラス自体と同じパッケージに作成します。メッセージ・バンドルは、{
MessageKeyString
、
TranslatableMessageString
}
というペアの配列を返します。
例4-32 ドメイン例外メッセージのカスタム・メッセージ・バンドル・クラス
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コンポーネントのカスタム・クライアント・インタフェースのメソッドの引数または戻り型でドメインを使用できます。
例4-33は、簡単なShortEmailAddress
ドメイン・クラスのvalidate()
メソッドを示しています。このメソッドは、mData
値にアットマークまたはドットが含まれないていないことを確認し、含まれている場合は、翻訳可能なエラー・メッセージの適切なメッセージ・バンドルとメッセージ・キーを参照するDataCreationException
をスローします。
例4-33 カスタム検証を含む簡単なShortEmailAddress文字列ベース・ドメイン型
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
パッケージの組込み型に基づく他の簡単なドメインは、例4-34に示すように、ベース型を拡張します。この例では、偶数を表すEvenNumber
という名前のNumberベースのドメインに対するvalidate()
メソッドが示されています。
例4-34 カスタム検証を含む簡単なEvenNumber Numberベース・ドメイン型
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オブジェクト型のドメインを手動で作成するには:
「アプリケーション」ウィンドウで、ドメインを作成するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから、「ドメイン」を選択し、「OK」をクリックします。
ドメイン作成ウィザードの「名前」ページで、「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バインディング・レイヤーでのオブジェクト型ドメインの処理のサポートは完全ではないため、宣言的にデータバインドされたユーザー・インタフェースでオブジェクト・ドメイン値属性を使用することは容易ではありません。 |
44.2.1項「コンポーネントをADFライブラリJARにパッケージ化する方法」で説明されているように、ビジネス・コンポーネントのADFライブラリJARを作成すると、プロジェクトのソース・パスの*.commonサブディレクトリにあるドメイン・クラスとメッセージ・バンドル・ファイルは、*CSCommon.jar
にパッケージ化されます。これらは、サポートする必要のある中間層アプリケーション・サーバーと最終的なリモート・クライアントの両方に共通のクラスです。
ドメインではカスタム・メタデータ・プロパティを定義できます。そのようなドメインに基づくエンティティ・オブジェクトまたはビュー・オブジェクトの属性は、属性自体で定義されている場合と同じように、これらのカスタム・プロパティを継承します。エンティティ・オブジェクトまたはビュー・オブジェクトの属性で同じカスタム・プロパティが定義されている場合は、その設定が、ドメインから継承する値より優先されます。
JDeveloperでは、ドメイン定義レベルで適用されている宣言的な設定を施行します。この設定は、ドメイン型に基づく属性のエンティティ・オブジェクトまたはビュー・オブジェクトでは制限を緩和できません。たとえば、ドメインの「更新可能」プロパティを「新規の間」に設定した場合、そのドメインをエンティティ・オブジェクト属性のJava型として使用するときに、「更新可能」を「なし」(より厳しい制限)に設定することはできますが、「常に」に設定することはできません。同様に、ドメインを「永続的」に定義した場合、後でそれを一時的なものにすることはできません。アプリケーションで使用するときは、ドメインの宣言的プロパティはできるかぎり緩く設定し、後で必要に応じて厳しくできるようにします。
履歴タイプは、ある時点に固有のデータの追跡に使用されます。JDeveloperにはいくつかの履歴タイプが付属していますが、独自のものも作成できます。標準の履歴タイプとその使用方法の詳細は、4.10.12項「履歴列を使用して作成および変更した日付を追跡する方法」を参照してください。
付属の履歴タイプに限定されることなく、「プリファレンス」ダイアログの「履歴タイプ」ページを使用してカスタム履歴タイプを追加または削除してから、カスタムJavaコードを記述して目的の動作を実装できます。カスタム履歴タイプを処理するコードは、再利用できるようアプリケーション全体にわたるエンティティ・ベース・クラスで記述します。
図4-16は、タイプIDが11
のlast update login
カスタム・タイプです。last_update_login
がFND_LOGINS
表の外部キーであると仮定します。
始める前に:
履歴タイプに関する知識が役立つ場合があります。詳細は、4.16項「新規履歴タイプの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
カスタム履歴列タイプを作成するには:
メイン・メニューから、「ツール」→「プリファレンス」を選択します。
「プリファレンス」ダイアログで、「ADFビジネス・コンポーネント」を展開し、「履歴タイプ」をクリックします。
「履歴タイプ」ページの「プリファレンス」ダイアログで、「新規」をクリックします。
「新規履歴タイプ」ダイアログで、名前(空白は使用可)の文字列値および数値IDを入力します。
「タイプID」は11 - 126の整数である必要があります。0 - 10の数値は内部用に予約されています。表示文字列は、次に概要エディタを使用する際、「変更履歴のトラッキング」ドロップダウン・リストに表示されます。図4-17は、「プリファレンス」ダイアログでの新規履歴タイプを示します。
エンティティ・オブジェクトのJavaクラス・ファイル(またはそれを基礎としている拡張クラス)を開き、例4-35の定義と同じ定義を追加します。
例4-36
のようなコードで、EntityImpl
ベース・クラスのgetHistoryContextForAttribute(AttributeDefImpl attr)メソッドをオーバーライドします。
履歴タイプは通常、アプリケーションの存続期間中は値の監査に使用されるため、削除する必要はありません。ただし、削除する必要がある場合は、次の作業を実行します。
「プリファレンス」ダイアログで、JDeveloper履歴タイプ・リストから目的の履歴タイプを削除します。
エンティティ・オブジェクトのJavaクラス・ファイル(またはそれを基礎としている拡張クラス)のgetHistoryContextForAttribute
メソッドで履歴タイプをサポートするために実装したカスタム・コードを削除します。
エンティティ属性メタデータの履歴タイプのすべての慣用名を削除します。この履歴タイプを使用するように定義した属性があれば、編集する必要があります。
始める前に:
履歴タイプに関する知識が役立つ場合があります。詳細は、4.16項「新規履歴タイプの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
JDeveloper履歴タイプ・リストから目的の履歴タイプを削除するには:
メイン・メニューから、「ツール」→「プリファレンス」を選択します。
「プリファレンス」ダイアログで、「ADFビジネス・コンポーネント」を展開し、「履歴タイプ」をクリックします。
「履歴タイプ」ページの「プリファレンス」ダイアログで、削除する履歴タイプを選択して「削除」をクリックします。
基礎となる表に対する挿入、更新、および削除のアクセスをカプセル化する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
表に対する挿入、更新、削除権限をカプセル化する、例4-37で示されるような簡単なPRODUCTS_API
パッケージを使用します。
例4-37 S_PRODUCT表に対する簡単なPL/SQLパッケージ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パッケージに基づいてエンティティ・オブジェクトを作成するには、次の手順を実行します。
4.17.1項「ビューを基にしてエンティティ・オブジェクトを作成する方法」の説明に従ってビューを基にしたエンティティ・オブジェクトを作成します。
4.17.3項「PL/SQLベースのエンティティに関する詳細をベース・クラスに集中化する方法」の説明に従ってエンティティ・オブジェクトのベース・クラスを作成します。
4.17.4項「DML操作のストアド・プロシージャ呼出しを実装する方法」の説明に従って適切なストアド・プロシージャ呼出しを実装します。
必要に応じて、4.17.5項「選択処理およびロック処理を追加する方法」の説明に従って選択機能とロック機能を処理します。
注意: この項の例は、SummitADF_Examples アプリケーション・ワークスペースのoracle.summit.model.wrapplsql パッケージを参照しています。
例ではビューベースのエンティティ・オブジェクトではなく表ベースのエンティティ・オブジェクトが使用されていますが、構成の詳細は同一です。 |
ビューを基にしてエンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
始める前に:
エンティティ・オブジェクトでPS/SQL APIを使用できる方法に関する知識が役立つ場合があります。詳細は、4.17項「PL/SQLパッケージAPIに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
4.2.2項「エンティティの作成ウィザードで単一のエンティティ・オブジェクトを作成する方法」の説明に従ってエンティティ・オブジェクト作成ウィザードを起動してウィザードを実行する必要がありますが、次の手順で示す例外があります。
ビューを基にしてエンティティ・オブジェクトを作成するには:
「名前」ページで、エンティティの名前を入力します。
「スキーマ・オブジェクトの選択」ダイアログの 「オブジェクト・タイプ」セクションで、「ビュー」チェック・ボックスを選択します。
「問合せ」をクリックした際に現在のスキーマで使用できるデータベース・ビューを表示できます。
「使用可能なオブジェクト」リストで目的のデータベース・ビューを選択します。
「属性の設定」ページで、「属性の選択」ドロップダウン・リストを使用して、主キーとして機能する属性を選択し、その属性の「主キー」チェック・ボックスを選択します。
注意: ビューを基にしてエンティティを定義するときは、データ・ディクショナリにデータベース・ビューに関連する制約がないため、主キー属性を自動的に決定することはできません。 |
ビューに基づくエンティティ・オブジェクトは、デフォルトで、基礎となるデータベース・ビューに対して次のすべての文を直接実行します。
SELECT
文(findByPrimaryKey()
の場合)
SELECT FOR UPDATE
文(lock()
の場合)
INSERT
、UPDATE
、DELETE
文(doDML()
の場合)
ストアド・プロシージャ呼出しを使用するには、(4.17.3項「PL/SQLベースのエンティティに関する詳細をベース・クラスに集中化する方法」
の説明に従って)doDML()操作をオーバーライドし、必要に応じて(4.17.4項「DML操作のストアド・プロシージャ呼出しを実装する方法」
の説明に従って)lock()
およびfindByPrimaryKey()の処理をオーバーライドする必要があります。
PL/SQL APIに基づくエンティティ・オブジェクトが複数ある場合は、一般的な詳細をベース・フレームワーク拡張クラスに抽象化するのがよい方法です。その際、第16章「ビジネス・コンポーネントの高度な手法」に説明されているいくつかの概念を使用します。まず、ベースEntityImpl
クラスを拡張するPLSQLEntityImpl
クラスを作成します。このクラスは、PL/SQLベースの各エンティティがベース・クラスとして使用できます。例4-38に示すように、ベース・クラスのdoDML()
メソッドをオーバーライドし、操作に基づいて異なるヘルパー・メソッドを呼び出します。
注意: エンティティにすでに拡張したエンティティ実装クラスを使用している場合、PLSQLEntityImpl クラスを使用してさらに拡張できます。たとえば、zzEntityImpl というフレームワーク拡張クラスがある場合、zzEntityImpl クラスを拡張するPLSQLEntityImpl クラスを作成します。 |
例4-38 操作に基づいて異なるプロシージャを呼び出すための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() メソッドで操作のオーバーライドのみを行います。 |
これらの呼出しを実装する作業を簡単にするには、(16.5項「ストアド・プロシージャとストアド・ファンクションの呼出し」
で説明した)callStoredProcedure()ヘルパー・メソッドをPLSQLEntityImpl
クラスに追加することもできます。このようにすると、このクラスを拡張するPL/SQLベースのエンティティ・オブジェクトはすべて、ヘルパー・メソッドを利用できます。
DML操作に対するストアド・プロシージャ呼出しを実装するには、エンティティ・オブジェクトのカスタムJavaクラスを作成して、その中の操作をオーバーライドする必要があります。
始める前に:
PL/SQLパッケージAPIに基づくエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.17項「PL/SQLパッケージAPIに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
メソッドのオーバーライドでカスタムJavaクラスを作成するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックし、「Javaオプションの編集」アイコンをクリックします。
「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。
「ベース・クラスのオーバーライド」ダイアログで、「行」フィールドにPLSQLEntityImpl
クラスのパッケージとクラスを入力するか、「参照」をクリックして検索して選択します。
「エンティティ・オブジェクト・クラスの生成」を選択して「OK」をクリックします。
「アプリケーション」ウィンドウで、生成されたエンティティ・オブジェクト・クラスをダブルクリックします。
メイン・メニューから、「ソース」→「メソッドのオーバーライド」を選択します。
「メソッドのオーバーライド」ダイアログで、callInsertProcedure()
、callUpdateProcedure()
およびcallDeleteProcedure()
の各メソッドを選択し、次に「OK」をクリックします。
ソース・エディタで、必要なコードを入力し、これらのプロシージャをオーバーライドします。
例4-39は、これらのオーバーライドされるヘルパー・メソッドに記述するサンプル・コードを示しています。
例4-39 ヘルパー・メソッドを利用した挿入、更新、削除プロシージャの呼出し
// 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()
機能を実行する必要がある場合、4.17.5項「選択処理およびロック処理を追加する方法」に説明されている方法を実行できます。
必要に応じて、ストアド・プロシージャを呼び出して、エンティティ・オブジェクトのlock()
およびfindByPrimaryKey()
機能を処理できます。PRODUCTS_API
パッケージを更新し、例4-40で示されている2つのプロシージャを追加したものとします。lock_product
プロシージャとselect_product
プロシージャはどちらも、IN
パラメータとして主キー属性を受け取り、OUT
パラメータを使用して残りの属性の値を返します。
例4-40 S_PRODUCT表用に追加するロックおよび選択プロシージャ
/* 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);
選択処理およびロック処理を追加するには、次のタスクを実行する必要があります。
4.17.5.1項「ロックと選択を処理するためのPLSQLEntityImplベース・クラスの更新」の説明に従って、ロックと選択を処理するためにベース・クラスを更新します。
4.17.5.2項「Productエンティティに対するロックおよび選択の実装」の説明に従って、ロックと選択の動作を実装するためにエンティティ・オブジェクト実装クラスを更新します。
4.17.5.3項「RowInconsistentException後のエンティティ・オブジェクトのリフレッシュ」
の説明に従って、RowInconsistentException
が発生した後、エンティティ・オブジェクト実装クラスのlock()メソッドをオーバーライドし、エンティティ・オブジェクトをリフレッシュします。
挿入、更新、削除用に追加したものと似たヘルパー・メソッドを使用して、lock()
およびfindByPrimaryKey()
のオーバーライドを処理するように、PLSQLEntityImpl
ベース・クラスを拡張できます。実行時には、lock()
操作とfindByPrimaryKey()
操作はどちらも、doSelect(boolean lock)
という名前の下位レベルのエンティティ・オブジェクト・メソッドを呼び出します。lock()
操作はパラメータをtrue
値にしてdoSelect()
を呼び出しますが、findByPrimaryKey()
操作はfalse
を渡して呼び出します。
例4-41は、必要に応じてサブクラスがオーバーライドできる2つのヘルパー・メソッドに委譲するようオーバーライドされたPLSQLEntityImpl
のdoSelect()
メソッドを示します。
例4-41 ロック・パラメータに基づいて異なるプロシージャを呼び出すための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
引数を持っているため、16.5.4項「他の種類のストアド・プロシージャの呼出し方法」で説明したように、JDBCのCallableStatement
オブジェクトを使用してこれらの呼出しを実行する必要があります。
例4-42は、ProductImpl
エンティティ・オブジェクト実装クラスのselect_product
プロシージャを呼び出すために使用するコードを示します。このコードが実行する基本的な手順は次のとおりです。
PL/SQLブロックが呼び出すCallableStatement
を作成します。
1から始まるバインド変数位置で、OUT
パラメータと型を登録します。
IN
パラメータの値を設定します。
文を実行します。
更新された列の値を取得します。
更新された属性値を行に移入します。
文を閉じます。
例4-42 主キーで行を選択するためのストアド・プロシージャの呼出し
// 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) { } } } }
例4-43は、lock_product
プロシージャを呼び出すためのコードを示しています。基本的には例4-42と同じ手順を行いますが、次の2点のみ異なります。
OUT
パラメータから更新された可能性のある列の値を取得した後、PLSQLEntityImpl
から継承されたcompareOldAttrTo()
ヘルパー・メソッドを使用して、行ロック試行の結果としてRowInconsistentException
をスローする必要があるかどうかを判定します。
catch (SQLException e)
ブロックでは、データベースがエラーをスローしたかどうかを検査しています。
ORA-00054: resource busy and acquire with NOWAIT specified
例外がスローされている場合は、lock()
機能のデフォルトのエンティティ・オブジェクトの実装がこのような状況で行うのと同じように、再びADFビジネス・コンポーネントのAlreadyLockedException
をスローします。
例4-43 主キーで行をロックするためのストアド・プロシージャの呼出し
// 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) { } } } }
これらのメソッドを配置すると、すべてのデータベース操作についてPL/SQLパッケージをラップするエンティティ・オブジェクト(この場合、PRODUCTS_API
パッケージのProduct
エンティティ・オブジェクト)ができます。ビュー・オブジェクトのデータ問合せ機能およびエンティティ・オブジェクトのデータ検証と保存機能を明確に分離するため、通常のエンティティ・オブジェクトを使用するのと同じように、このエンティティ・オブジェクトを利用できます。このエンティティ・オブジェクトをエンティティ慣用名として使用する異なるビュー・オブジェクトを、必要な数だけ作成できます。
RowInconsistentException
が発生した後で、lock()
メソッドをオーバーライドして、エンティティ・オブジェクトをリフレッシュすることができます。例4-44に、エンティティ・オブジェクト実装クラスに追加してRowInconsistentException
を捕捉しエンティティ・オブジェクトをリフレッシュできるコードを示します。
次のいずれかに基づくエンティティ・オブジェクトを作成する必要がある場合があります。
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
句をサポートしていないスキーマ・オブジェクトの種類に関する知識があると役立ちます。詳細は、4.18項「結合ビューまたはリモートDBLinkに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
この種のエンティティ・オブジェクトでRETURNING句の使用を無効にするには:
エンティティ・オブジェクトのカスタム・エンティティ定義クラスを有効にします。
カスタム・エンティティ定義クラスで、createDef()
メソッドをオーバーライドしてsetUseReturningClause(false)
を呼び出します。
Refresh on Insert属性がエンティティ・オブジェクトの主キーである場合は、「一意キー」プロパティを設定して、エンティティの他の属性を代替一意キーとして指定する必要があります。
4.18.1項「RETURNING句の使用を無効にする方法」
の説明に従って、RETURNING句の使用を無効にすると、実行時には、エンティティ・オブジェクトが別のSELECT文を使用してRefresh on Insert動作およびRefresh on Update
動作を実装し、挿入または更新の後でリフレッシュするための値を取得します。
継承はオブジェクト指向開発の強力な機能であり、適切に使用すれば開発とメンテナンスを簡単にできます。16.8項「継承を使用する拡張コンポーネントの作成」で説明されているように、ADFビジネス・コンポーネントでは、継承を使用して既存のコンポーネントを拡張する新しいコンポーネントを作成し、新しいプロパティまたは動作を追加したり、親コンポーネントの動作を変更したりできます。継承は、再利用可能なビジネス・ドメイン・レイヤーで異なる種類のエンティティをモデリングする際に便利です。
注意: この項の例は、SummitADF_Examples ワークスペースのoracle.summit.model.polymorphic パッケージの拡張です。 |
アプリケーションのデータベース・スキーマでは、同じ表の行に論理的に異なる種類のビジネス情報が格納される場合があります。このような表には、通常、各行に格納される情報の種類を決定する値を含む列があります。たとえば、アプリケーションのS_CUSTOMER
表では、国内および国際の両方の顧客に関する情報が同一の表に格納されます。また、CUSTOMER_TYPE_CODE
列の値によって、その行が表すS_CUSTOMER
の種類が決まります。
Summit ADFサンプル・アプリケーションの実装にはこの区別は含まれていませんが、アプリケーションの改訂では次のことが必要になることが考えられます。
国内顧客に固有または国際顧客に固有の追加のデータベース利用の属性を管理する
国内顧客または国際顧客によって違いがあるすべてのユーザー向けに共通の動作を実装する
国内顧客のみまたは国際顧客のみに固有な新機能を実装する
図4-18は、Customers
、Domestics
、Internationals
の各エンティティ・オブジェクトを個別に作成し、異なる種類のビジネス情報をアプリケーション内でより形式的に区別できるようにした場合の、ビジネス・ドメイン・レイヤーの様子を示しています。国内および国際は顧客の特別な種類であるため、それに対応するエンティティ・オブジェクトはベース・エンティティ・オブジェクトのCustomers
を拡張します。このベースのCustomers
エンティティ・オブジェクトには、すべての種類のユーザーに共通する属性とメソッドがすべて含まれています。図のperformCustomerFunction()
メソッドは、共通メソッドの1つを表しています。
その後、Domestics
およびInternationals
エンティティ・オブジェクトの場合は、その種類のユーザーに対して一意の特定の追加属性とメソッドを追加できます。たとえば、Domestics
には型がString
の追加のState
属性があり、国内顧客が所在する州を追跡できます。また、国内顧客に固有のperformDomesticFunction()
メソッドもあります。同様に、Internationals
エンティティ・オブジェクトには、顧客が英語を話すかどうかを追跡するLanguage
属性が追加されています。performInternationalFunction()
は国際顧客に固有のメソッドです。
異なる種類の顧客を、ビジネス・ドメイン・レイヤーの継承階層において異なるエンティティ・オブジェクトとしてモデル化することで、共通するデータや動作の共有が簡単になり、ユーザーを区別するアプリケーションの部分を実装できます。
継承階層にエンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用して各エンティティを作成します。
継承階層にエンティティ・オブジェクトを作成するには、次の作業を行います。
4.19.2.1項「識別子列と個別の値の識別」の説明に従って、識別子列と値を識別します。
4.19.2.2項「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性のサブセットを識別します。
4.19.2.3項「継承階層へのベース・エンティティ・オブジェクトの作成」の説明に従って、ベース・エンティティ・オブジェクトを作成します。
4.19.2.4項「継承階層へのサブタイプ・エンティティ・オブジェクトの作成」の説明に従って、サブタイプ・エンティティ・オブジェクトを作成します。
異なる種類の情報を含む表に基づいて継承階層にエンティティ・オブジェクトを作成する前にまず、行の種類を区別するために使用されている表の列を識別する必要があります。
たとえば、S_CUSTOMER
表では、これがCUSTOMER_TYPE_CODE
列になることがあります。このような列は表の行を異なるグループに分離つまり識別するのに役立つため、識別子列と呼ばれます。
次に、表の識別子列で有効な値を決定します。値がわからない場合は、JDeveloperの「SQLワークシート」で簡単なSQL文を実行して答えを得ることもできます。ワークシートを使用するには次のようにします。
JDeveloperで開いたアプリケーションで、メイン・メニューから 「ウィンドウ」→「データベース」→「データベース」を選択します。
ワークスペース・ノードを開いて、接続を選択します。
データベース接続を右クリックし、「SQLワークシートを開く」を選択します。
図4-19は、「SQLワークシート」で、S_CUSTOMER
表のCUSTOMER_TYPE_CODE
列に対してSELECT DISTINCT
問合せを実行した結果を示しています。これは、CUSTOMER_TYPE_CODE
識別子の値DOMESTIC
およびINTERNATIONAL
に基づいて行が2つのグループに分類されることを示しています。
表に格納されている異なるビジネス・エンティティの種類の数がわかると、個別の項目をモデル化するために作成する必要のあるエンティティ・オブジェクトの数もわかります。通常は、項目の種類ごとに1つのエンティティ・オブジェクトを作成します。次に、階層のベースとして機能するエンティティを識別するため、各項目の種類に関係する属性のサブセットを特定する必要があります。
たとえば、State
とLanguage
を除いてすべての属性はすべてのユーザーに関連することと次のことを調べると仮定します。
State
は国内顧客に固有である。
Language
は国際顧客に固有である。
この情報からCustomers
エンティティ・オブジェクトを階層のベースとし、Domestics
およびInternationals
エンティティ・オブジェクトがそれぞれCustomers
を拡張して固有の属性を追加するように決定できます。
継承階層にベース・エンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、4.19.2.1項「識別子列と個別の値の識別」の説明に従って、識別子列と値を判別し、4.19.2.2項「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性を判別する必要があります。
ベース・エンティティ・オブジェクトを作成する手順
「アプリケーション」ウィンドウで、エンティティ・オブジェクトを追加するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
エンティティ・オブジェクト作成ウィザードの「名前」ページで、エンティティの名前とパッケージを指定し、エンティティのベースとなるスキーマ・オブジェクトを選択します。
この例では、エンティティ・オブジェクトをCustomers
と名付け、これがS_CUSTOMER
表に基づくようにします。
「属性」ページで、ベース・エンティティ・オブジェクトに関係のない属性(ある場合)を「エンティティ属性」リストで選択し、「削除」をクリックして削除します。
この例では、State
属性とLanguage
属性をリストから削除します。
「属性の設定」ページで、「属性の選択」ドロップダウン・リストを使用して、継承されるエンティティ・オブジェクトのファミリの識別子として機能する属性を選択し、「多相化識別子」チェック・ボックスを選択してそのことを示します。重要なこととして、このベース・エンティティ・タイプの行を識別するため、この識別子属性に対する「デフォルト値」を指定する必要もあります。
この例では、CustomerTypeCode
属性を選択して識別子属性としてマークし、「デフォルト値」に値CUSTOMER
を設定します。
注意: 識別子属性の「デフォルト値」を空白のままにしてもかまいません。空白のデフォルト値は、識別子列の値がIS NULL である行がこのベース・エンティティ・タイプとして扱われることを意味します。 |
次に、「終了」をクリックします。
継承階層にサブタイプ・エンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
4.19.2.1項「識別子列と個別の値の識別」の説明に従って識別子列と値を調べます。
4.19.2.2項「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性を調べます。
4.19.2.3項「継承階層へのベース・エンティティ・オブジェクトの作成」の説明に従って、新しいエンティティ・オブジェクトが拡張する元となる親エンティティ・オブジェクトを作成します。
親エンティティで識別子属性がすでに識別されていることを確認します。
識別されていない場合は、継承された子を作成する前に、概要エディタを使用して親エンティティの識別子属性で「多相化識別子」プロパティを設定します。
階層にサブタイプ・エンティティ・オブジェクトを作成するには:
「アプリケーション」ウィンドウで、エンティティ・オブジェクトを追加するプロジェクトを右クリックし、「新規」→「ギャラリから」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADFビジネス・コンポーネント」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
エンティティ・オブジェクト作成ウィザードの「名前」ページで、エンティティの名前とパッケージを指定し、「拡張」フィールドの隣の「参照」ボタンをクリックして、作成しているエンティティが拡張される元となる親エンティティを選択します。
この例では、新しいエンティティをDomestics
と名付け、「拡張」
フィールドでCustomersエンティティ・オブジェクトを選択します。
「属性」ページの「エンティティ属性」リストに、基礎となる表からベース・エンティティ・オブジェクトに含まれない属性が表示されます。このエンティティ・オブジェクトに含めない属性を選択し、「削除」をクリックします。
この例では、Domestics
エンティティを作成しているため、Language
属性を削除し、State
属性はそのままにします。
「オーバーライド」をクリックして識別子属性を選択し、属性メタデータをカスタマイズしてDomesticsエンティティ・サブタイプに個別の「デフォルト値」
を提供できるようにします。
この例では、CustomerTypeCode
属性をオーバーライドします。
「属性の設定」ページで、「属性の選択」ドロップダウン・リストを使用して識別子属性を選択します。「デフォルト値」フィールドを変更し、作成しているエンティティ・サブタイプを定義する識別子属性に個別のデフォルト値を設定します。
この例では、CustomerTypeCode
属性を選択し、「デフォルト値」を値DOMESTIC
に変更します。
「終了」をクリックします。
注意: 同じ手順を繰り返して、Customers を拡張するInternationals エンティティ・オブジェクトを定義し、Language 属性を追加し、CustomerTypeCode識別子属性の「デフォルト値」 を値INTERNATIONAL に変更できます。 |
継承階層内のエンティティ・オブジェクトにメソッドを追加するには、エンティティ・オブジェクトでカスタムJavaクラスを有効にし、ソース・エディタを使用してメソッドを追加します。階層内のすべてのエンティティ・オブジェクトに共通するメソッドをベース・エンティティに追加し、サブタイプに固有のメソッドをサブタイプに追加します。また、必要に応じてサブタイプにおいてベース・エンティティ・オブジェクトのメソッドをオーバーライドできます。
階層内のすべてのエンティティ・オブジェクトに共通するメソッドを追加するには、ベース・エンティティ・オブジェクトの実装クラスにメソッドを追加します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、4.19.2項「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
4.13項「エンティティ・オブジェクトのカスタムJavaクラスの生成」の説明に従い、ベース・エンティティ・オブジェクトのカスタムJava実装クラスを作成します。
階層内の全エンティティ・オブジェクトに共通するメソッドを追加するには:
「アプリケーション」ウィンドウで、ベース・エンティティ・オブジェクト実装クラス(CustomersImpl.java
など)をダブルクリックします。
ソース・エディタで、メソッドを追加します。
たとえば、ベースCustomers
エンティティ・オブジェクトのCustomersImpl
クラスに次のメソッドを追加します。
// In CustomersImpl.java public void performCustomerFunction() { System.out.println("## performCustomerFunction as Customer"); }
これはベース・エンティティ・オブジェクトのクラスなので、ここで実装するメソッドは、階層のすべてのサブタイプ・エンティティ・オブジェクトによって継承されます。
階層内のすべてのエンティティ・オブジェクトに共通するサブタイプ・エンティティ・オブジェクトをオーバーライドするには、ベース・エンティティ・オブジェクトから継承した共通メソッドをサブタイプ・エンティティ・オブジェクトの実装クラスで修正します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、4.19.2項「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
4.19.3.1項「階層内の全エンティティ・オブジェクトに共通するメソッドの追加」の説明に従って、ベース・エンティティ・オブジェクトの共通メソッドを作成します(サブタイプ・エンティティ・オブジェクトがオーバーライドします)。
4.13項「エンティティ・オブジェクトのカスタムJavaクラスの生成」の説明に従い、サブタイプ・エンティティ・オブジェクトのカスタムJava実装クラスを作成します。
サブタイプ・エンティティ・オブジェクトのメソッドをオーバーライドするには:
「アプリケーション」ウィンドウで、サブタイプ・エンティティ・オブジェクト実装クラス(DomesticsImpl.java
など)をダブルクリックします。
サブタイプ・エンティティ・オブジェクト実装クラスをソース・エディタで開き、メイン・メニューから「ソース」→「メソッドのオーバーライド」を選択します。
「メソッドのオーバーライド」ダイアログで、オーバーライドするメソッド(たとえば、performCustomerFunction()
メソッド)を選択して、「OK」をクリックします。
ソース・エディタで、オーバーライドするメソッドの実装をカスタマイズします。
たとえば、Domestics
サブタイプ・エンティティ・オブジェクトのDomesticsImpl
クラスでperformCustomerFunction()
メソッドをオーバーライドし、実装を次のように変更できます。
// In DomesticsImpl.java public void performCustomerFunction() { System.out.println("## performCustomerFunction as Domestics"); }
サブタイプ階層でエンティティ・オブジェクトのインスタンスについての作業を行う場合、複数の異なるサブタイプのインスタンスを処理することがあります。Domestics
およびInternationals
エンティティ・オブジェクトはCustomers
の特別な種類であるため、それらに共通するすべてが含まれるより汎用的なCustomersImpl
型を使用して、それらのすべてで動作するコードを記述できます。階層内のサブタイプ・ファミリであるクラスのこのような汎用処理を行うとき、Javaは、使用可能なメソッドで最も固有性の高いオーバーライドを常に呼び出します。
つまり、CustomersImpl
のインスタンスでperformCustomerFunction()
メソッドを呼び出し、実際にはさらに固有のDomesticsImpl
サブタイプがある場合、結果の出力は次のようになります。
## performCustomerFunction as Domestics
これに対し、標準のCustomersImpl
インスタンスによるデフォルトの出力結果は次のようになります。
## performCustomerFunction as Customer
階層内のサブタイプ・エンティティ・オブジェクトに特有のメソッドを追加するには、ソース・エディタを使用してサブタイプの実装クラスにメソッドを追加します。
始める前に:
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、4.19.2項「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
4.13項「エンティティ・オブジェクトのカスタムJavaクラスの生成」の説明に従い、サブタイプ・エンティティ・オブジェクトのカスタムJava実装クラスを作成します。
サブタイプ・エンティティ・オブジェクトに固有のメソッドを追加するには:
「アプリケーション」ウィンドウで、サブタイプ・エンティティ・オブジェクト実装クラス(InternationalsImpl.java
など)をダブルクリックします。
ソース・エディタで、メソッドを追加します。
たとえば、Internationals
サブタイプ・エンティティ・オブジェクトのInternationalsImpl
クラスに、performInternationalFunction()
メソッドを追加できます。
// In InternationalsImpl.java public void performInternationalFunction() { System.out.println("## performInternationalFunction called"); }
継承を使用すると、新しいベース・エンティティの導入、主キーによるサブタイプ・エンティティの検索、および多相エンティティ・オブジェクトの慣用名を持つビュー・オブジェクトの作成も行うことができます。
たとえば、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
オブジェクトをさらに固有のエンティティ・オブジェクト型にキャストして、そのサブタイプに固有の機能で作業できます。
継承階層のベース・エンティティ・オブジェクトに対応するエンティティ・オブジェクトの慣用名を持つエンティティ・ベースのビュー・オブジェクトを作成するときは、ベース・エンティティのサブタイプ階層の複数の異なるサブタイプに対応する行を問い合せるように、ビュー・オブジェクトを構成できます。ビュー・オブジェクトの各行は、識別子属性の値の対応に基づいて、適切なサブタイプ・エンティティ・オブジェクトをエンティティ行の部分として使用します。このようなビュー・オブジェクトを設定して使用する具体的な方法の詳細は、7.2.1項「多相エンティティ・オブジェクトの慣用名によるサブタイプ・ビュー・オブジェクトの作成方法」を参照してください。