Oracle® Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11g リリース2(11.1.2.3.0) B69399-02 |
|
前 |
次 |
この章では、ADFエンティティ・オブジェクトを使用して、Oracle Application Development Framework(Oracle ADF)アプリケーションでビジネス・ドメインについて記述しているJavaオブジェクトの再使用可能なビジネス・レイヤーを作成する方法について説明します。
この章の内容は次のとおりです。
エンティティ・オブジェクトは、指定したデータソース内の行を表し、関連付けられた属性の変更を簡略化するためのADF Business Componentsのコンポーネントです。重要なことは、エンティティ・オブジェクトを使用すると、ドメイン・ビジネス・ロジックをカプセル化できるため、ビジネス・ポリシーおよびビジネス・ルールを一貫性のある方法で検証できるということです。
エンティティ・オブジェクトは、データの有効性を強化するために様々な宣言的ビジネス・ロジック機能をサポートしています。通常、追加のカスタム・アプリケーション・ロジックおよびビジネス・ルールで宣言的な検証に補完することにより、各エンティティ・オブジェクトに最大の量のドメイン・ビジネス・ロジックを効率的にカプセル化します。関連付けられた一連のエンティティ・オブジェクトによって、複数のアプリケーションで活用できる再利用可能なビジネス・ドメインが形成されます。
エンティティ・オブジェクト(図4-1に示します)の主要な概念は次のとおりです。
エンティティ・オブジェクトを定義するには、エンティティ・オブジェクトが示す行が含まれるデータベース表を指定します。
エンティティ・オブジェクト間の関連を反映するアソシエーションを作成できます。
実行時には、エンティティ行は関連するエンティティ定義オブジェクトによって管理されます。
各エンティティ行は、関連する行キーによって識別されます。
エンティティ行は、データベース・トランザクションを提供するアプリケーション・モジュールのコンテキストで取得および変更します。
エンティティ・オブジェクトで作業を開始する前に、その他のADF機能を理解しておくと役に立つ場合があります。次に、関連する他の機能へのリンクを示します。
エンティティ・オブジェクトでの宣言的な検証を使用する方法の詳細は、第7章「検証とビジネス・ルールの宣言的な定義」を参照してください。
oracle.jbo
パッケージに関連するAPIのドキュメントについては、次のJavadocリファレンス・ドキュメントを参照してください。
Oracle Fusion Middleware Oracle ADF Model Java APIリファレンス
すでに使用しているデータベース・スキーマがある場合、エンティティ・オブジェクトおよびアソシエーションを作成する最も簡単な方法は、これらを既存の表からリバース・エンジニアリングする方法です。必要な場合には、エンティティ・オブジェクトを最初から作成し、後でこのエンティティ・オブジェクト用の表を生成することもできます。
1つ以上のエンティティ・オブジェクトを作成するには、「新規ギャラリ」で使用できる「表からのビジネス・コンポーネント」ウィザードを使用します。
作業を始める前に、次のようにします。
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
既存の表から1つ以上のエンティティ・オブジェクトおよびアソシエーションを作成する手順:
アプリケーション・ナビゲータで、エンティティ・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから「表からのビジネス・コンポーネント」を選択し、「OK」をクリックします。
これがプロジェクトで作成する最初のコンポーネントである場合、「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログが表示され、データベース接続を選択できます。
「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログで、データベース接続を選択するか、「新規」を選択して接続を作成します。「OK」をクリックします。
「エンティティ・オブジェクト」ページで、次の操作を実行してエンティティ・オブジェクトを作成します。
すべてのエンティティ・オブジェクトが作成されるパッケージ名を入力します。
「使用可能」リストからエンティティ・オブジェクトを作成する表を選択します。
「自動問合せ」チェック・ボックスを選択すると、使用可能な表のリストが即座に表示されます。必要に応じて、「名前フィルタ」フィールドで表名またはその一部を入力して、使用可能な表をリアルタイムでフィルタ処理できます。自動問合せ機能を使用するかわりに、「問合せ」ボタンをクリックしてオプションの表の名前フィルタに基づいてリストを取得することもできます。名前フィルタが入力されていない場合は、JDeveloperにより、選択したスキーマに対するすべての表オブジェクトが取得されます。
使用可能なデータベース・オブジェクトのうち、サブセットのみを表示する場合は、「フィルタ・タイプ」をクリックします。表、ビューまたはシノニムをフィルタ処理できます。
「使用可能」リストから表を選択した後、「選択済」リストに、この表について提案されるエンティティ・オブジェクト名とともに、関連する表名がカッコで囲まれて表示されます。
「選択済」リストでエンティティ・オブジェクト名を選択すると、「エンティティ名」フィールドを使用してデフォルトのエンティティ・オブジェクト名を変更できます。
ベスト・プラクティス: 各エンティティ・オブジェクト・インスタンスは特定の表の単一行を表しているため、エンティティ・オブジェクトの名前には、複数形の名詞ではなく、単数形の名詞(Address、Order、Personなど)を使用してください。図4-2に、 |
目的の表オブジェクトとそれに対応するエンティティ・オブジェクトの名前を選択したら、「終了」をクリックします。
アプリケーション・ナビゲータには、指定したパッケージ内のエンティティ・オブジェクトが表示されます。
ベスト・プラクティス: アソシエーションを作成したら、エンティティ・オブジェクトとは別に表示および管理できるように、すべてのアソシエーションを別のパッケージに移動します。図4-3では、アソシエーションはサブパッケージ( |
単一のエンティティ・オブジェクトを作成するには、「新規ギャラリ」から使用可能なエンティティ・オブジェクト作成ウィザードを使用します。
注意: エンティティ・オブジェクト作成ウィザードを使用すると、アソシエーションは生成されません。ただし、「表からのビジネス・コンポーネント」ウィザードを使用すると、アソシエーションは生成されますエンティティ・オブジェクトの作成ウィザードを使用して、エンティティ・オブジェクトを作成する場合、対応するアソシエーションを手動で作成する必要があります。 |
作業を始める前に、次のようにします。
エンティティ・オブジェクト作成用オプションに関する知識が役立つ場合があります。詳細は、4.2項「エンティティ・オブジェクトおよびアソシエーションの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
単一のエンティティ・オブジェクトを作成する手順:
アプリケーション・ナビゲータで、エンティティ・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
これがプロジェクトで作成する最初のコンポーネントである場合、「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログが表示され、データベース接続を選択できます。
「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログで、データベース接続を選択するか、「新規」を選択して接続を作成します。「OK」をクリックします。
「名前」ページで、次の操作を実行してエンティティ・オブジェクトを作成します。
エンティティ・オブジェクトの名前を入力します。
エンティティ・オブジェクトが作成されるパッケージ名を入力します。
「参照」(「スキーマ・オブジェクト」フィールドの横)をクリックし、エンティティ・オブジェクトを作成する表を選択します。
または、表を後で作成する場合は、現在使用されていない名前を入力できます。
「スキーマ・オブジェクト」フィールドに表の名前を手動で入力する場合、ウィザードの「属性」ページで、各属性を定義する必要があります。「次へ」をクリックします。
4.2.6項「エンティティ・オブジェクトからのデータベース表の作成方法」で説明するように、この表は手動で作成または生成できます。
目的の表オブジェクトとそれに対応するエンティティ・オブジェクトの名前の操作が完了したら、「終了」をクリックします。
既存の表からエンティティ・オブジェクトを作成する場合、最初に、次の情報を推測するためにデータ・ディクショナリからデータが取得されます。
表の列名に基づくJavaに適したエンティティ属性名(USER_ID
→UserId
など)
基礎となる列のデータ型に基づく各属性のSQLおよびJavaデータ型
各属性の長さと精度
主キーと一意キーの属性
NOT NULL
制約に基づく属性の必須フラグ
外部キーの制約に基づく新規エンティティ・オブジェクトとその他のエンティティ間の関連
注意: エンティティ・オブジェクトはデータベース行を表しているため、エンティティ行と呼ぶのが自然です。または、エンティティ行は実行時にそのデータベース行のビジネス・ロジックをカプセル化するJavaオブジェクト・インスタンスであるため、エンティティ・インスタンスという、よりオブジェクト指向の呼び方も適しています。このため、これらの2つの用語は同義です。 |
これにより、宣言的設定を表すXMLコンポーネント定義ファイルが作成され、パッケージ名と対応するディレクトリに保存されます。たとえば、Order
という名前のエンティティがgenericbcmodel.entities
パッケージに含まれる場合、JDeveloperによりそのプロジェクトのソース・パスにgenericbcmodel/entities/Order.xml
というXMLファイルが作成されます。このXMLファイルには、表の名前、各エンティティ属性の名前とデータ型、および各属性の列名が含まれます。
概要エディタでオブジェクトを開き、「ソース」タブをクリックすると、エンティティ・オブジェクトのXML記述内容を検証できます。
注意: IDEレベルのビジネス・コンポーネントのJava生成設定で指定されている場合、オプションのカスタム・エンティティ・オブジェクト・クラス( |
エンティティ・オブジェクト以外に、エンティティ・オブジェクト間の関連に関する情報を取得する名前付きアソシエーション・コンポーネントも「表からのビジネス・コンポーネント」ウィザードにより生成されます。たとえば、図4-4のデータベース・ダイアグラムを参照すると、外部キー制約名をJavaに適した名前に変換してAssoc
接尾辞を付けることにより、OrderItemsProductsFkAssoc
などのデフォルトのアソシエーション名がJDeveloperにより導出されることを確認できます。作成されたアソシエーションごとに、適切なXMLコンポーネント定義ファイルが作成され、パッケージ名と対応するディレクトリに保存されます。
注意: 「表からのビジネス・コンポーネント」ウィザードを使用すると、アソシエーションは生成されます。ただし、エンティティ・オブジェクト作成ウィザードでは、アソシエーションは生成されません。エンティティ・オブジェクトの作成ウィザードを使用して、エンティティ・オブジェクトを作成する場合、対応するアソシエーションを手動で作成する必要があります。 |
デフォルトでは、外部キーからリバース・エンジニアリングされたアソシエーションは、エンティティと同じパッケージ内に作成されます。たとえば、fodemo.storefront.entities
パッケージ内にあるエンティティへのアソシエーションOrderItemsProductsFkAssoc
に対しては、JDeveloperにより./fodemo/storefront/entities/OrderItemsProductsFkAssoc.xml
という名前のアソシエーションXMLファイルが作成されます。
表に主キー制約がない場合、JDeveloperは、エンティティ・オブジェクトの主キーを推測できません。すべてのエンティティ・オブジェクトには少なくとも1つの属性が主キーとしてマークされている必要があるため、ウィザードでは、RowID
という名前の属性が作成され、データベースのROWID
値がこのエンティティの主キーとして使用されます。必要な場合、後でエンティティ・オブジェクトを編集し、別の属性を主キーとしてマークし、RowID
属性を削除できます。エンティティ・オブジェクト作成ウィザードを使用する場合、その他の属性を主キーとして設定していないと、RowID
を主キーとして使用するよう求められます。
「表からのビジネス・コンポーネント」ウィザードまたはエンティティ・オブジェクトの作成ウィザードを使用してエンティティ・オブジェクトを作成する場合、オブジェクトは基礎となる表、シノニム、またはビューを表すことができます。フレームワークは、データ・ディクショナリ内のデータベースの主キーおよび外部キー制約を調べることにより、主キーおよび関連するアソシエーションを推測できます。
ただし、選択したスキーマ・オブジェクトがデータベース・ビューである場合、データベース・ビューにはデータベース制約がないため、主キーもアソシエーションも推測できません。この場合、「表からのビジネス・コンポーネント」ウィザードを使用すると、主キーはRowID
にデフォルト設定されます。エンティティ・オブジェクト作成ウィザードを使用する場合、少なくとも1つの属性を主キーとしてマークすることにより、主キーを手動で指定する必要があります。詳細は、4.2.3.2項「表に主キーがないときに行われる処理」を参照してください。
選択したスキーマ・オブジェクトがシノニムである場合、結果には2種類あります。シノニムが表のシノニムである場合、ウィザードおよびエディタは、表を指定した場合と同じように動作します。かわりに、シノニムがデータベース・ビューを参照している場合、ウィザードおよびエディタは、ビューを指定した場合と同じように動作します。
新しいエンティティ・オブジェクトまたはアソシエーションを作成した後、概要エディタでその設定を編集できます。エディタを起動するには、「アプリケーション・ナビゲータ」のエンティティ・オブジェクトまたはアソシエーションのポップアップ・メニューで「開く」を選択するか、オブジェクトをダブルクリックします。エディタの別のタブをクリックすることにより、オブジェクトを定義する設定や、その実行時の動作を制御する設定を調整できます。
エンティティ・オブジェクトに基づいてデータベース表を作成するには、エンティティ・オブジェクトが含まれるアプリケーション・ナビゲータでパッケージを右クリックし、ポップアップ・メニューから「データベース・オブジェクトの作成」を選択します。ダイアログが表示され、表の作成元のエンティティを選択できます。このツールを使用して、作成したエンティティ・オブジェクトの表を最初から生成することも、既存の表をドロップして再作成することもできます。
注意: この機能では、後で実行するDDLスクリプトは生成されません。データベースに対して操作が直接実行され、既存の表がドロップされます。作業に進む前に、この処理を実行するかどうかを確認するダイアログが表示されます。既存の表に基づくエンティティの場合、慎重に作業してください。 |
アソシエーションの概要エディタの「アソシエーション・プロパティ」ページにある「データベース・キー制約の使用」チェック・ボックスにより、エンティティ・オブジェクトの表の作成時に、関連する外部キー制約を生成するかどうかを制御します。このオプションを選択しても、実行時には影響しません。
すでにエンティティ・オブジェクトを作成した表も、自分で(または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 Business Componentsフレームワークでは、「有効日開始」と「有効日終了」で適切な値を決定します。
例4-1は、有効日が指定されたエンティティを作成したときに生成されるサンプルのXMLエントリの一部を示しています。有効日が指定されたオブジェクトの使用方法の詳細は、5.4項「有効日付範囲を使用したビュー・オブジェクト行の制限」を参照してください。
例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"/>
「表からのビジネス・コンポーネント」ウィザードを使用すると、同時に多くのビジネス・コンポーネントを簡単に作成できます。とはいえ、単にこの処理が可能だという理由でこのウィザードを使用して、データベース・スキーマ内のすべての表についてエンティティ・オブジェクトを即座に作成する必要はありません。アプリケーションですべての表が必要な場合は、この方法が適しています。ただし、このウィザードは必要であればいつでも使用できるため、アプリケーションに必要なことがわかっている表についてエンティティ・オブジェクトを作成することをお薦めします。
9.4項「ネストされたアプリケーション・モジュールの定義」では、ビジネス・サービスのユース・ケース駆動型設計方法について説明されており、アプリケーションのビジネス・ロジックのニーズをサポートするために必要なエンティティ・オブジェクトを理解する上で役立ちます。エンティティ・オブジェクトは、必要に応じて後でいつでも追加できます。
データベース表に外部キー制約が定義されていない場合、作成されたエンティティ・オブジェクト間のアソシエーションはJDeveloperにより推測されません。一部のADF Business Componentsランタイム機能はエンティティ・アソシエーションが存在しているかどうかに依存するため、外部キーが存在しない場合は、エンティティ・アソシエーションを手動で作成することをお薦めします。
アソシエーションを作成するには、「新規ギャラリ」から使用できる新規アソシエーション作成ウィザードを使用します。
作業を始める前に、次のようにします。
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
アソシエーションを作成するには、次のようにします。
アプリケーション・ナビゲータで、アソシエーションを作成するプロジェクトを右クリックし、「新規」をクリックします。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択したら、「アソシエーション」を選択し、「OK」をクリックします。
「名前」ページで、次の操作を実行してエンティティ・オブジェクトを作成します。
アソシエーションが作成されるパッケージ名を入力します。
アソシエーション・コンポーネントの名前を入力します。
「次へ」をクリックします。
「エンティティ・オブジェクト」ページで、ソースおよびリンク先のエンティティ属性を選択します。
アソシエーションに含まれ、マスターとして機能するエンティティ・オブジェクトの1つからソース属性を選択します。
アソシエーションに含まれるその他のエンティティ・オブジェクトから、対応するリンク属性を選択します。
たとえば、図4-5では、OrderEO
エンティティ・オブジェクトからソース・エンティティ属性としてOrderId
属性が選択されています。OrderItemEO
の各行には、これらの行を特定のOrderEO
行に関連付けるオーダーIDが含まれるため、OrderItemEO
エンティティ・オブジェクトでこのOrderId
外部キー属性をリンク先属性として選択します。
「追加」をクリックし、対応する属性ペアを、下にあるソース属性とリンク先属性のペアの表に追加します。
デフォルトでは、ソース属性とリンク先属性の両方で「バインド済」チェックボックスが選択されています。このチェックボックスでは、(選択する側に応じて)ソース・エンティティからターゲット・エンティティ、またはターゲット・エンティティからソース・エンティティに移動する際に内部で作成されるアソシエーションSQL文に値をバインドするかどうかを指定できます。
値が定数であるためにエンティティを取得するためのアソシエーションSQL文に含めない一時エンティティ属性である関連内の属性のチェック・ボックスは、通常は選択を解除します。
アソシエーションを定義するために複数の属性ペアが必要な場合、これらのステップを繰り返し、ソース/ターゲット属性ペアをさらに追加できます。
最後に、「カーディナリティ」ドロップダウンに表示されているアソシエーションのカーディナリティが正しいことを確認します。デフォルトは1対多関係です。「次へ」をクリックします。
たとえば、OrderEO
行と対応するOrderItemEO
行間の関連は1対多であるため、デフォルト設定のままでかまいません。
「アソシエーションSQL」ページで、ソース・エンティティ・オブジェクトの特定のインスタンスに関連するリンク先エンティティ・オブジェクトにアクセスするために実行時に使用するアソシエーションSQL述語をプレビューできます。
一方向関連を表すアソシエーションを作成する場合は、「アソシエーション・プロパティ」ページで、ソース・エンティティ・オブジェクトまたはリンク先エンティティ・オブジェクトの「アクセッサの公開」チェック・ボックスの選択を解除します。ビジネス検証ロジックを作成する場合、双方向関連ナビゲーションの方が便利なため、通常これらのデフォルトのチェック・ボックス設定のままにしておきます。
たとえば、図4-6は、どちら側のエンティティ・オブジェクトも必要に応じて反対側の関連エンティティ行にアクセスできる双方向関係を表しています。つまり、この例では、OrderEO
エンティティ・オブジェクトのインスタンスを操作している場合、関連するOrderItemEO
行のコレクションに簡単にアクセスできます。また、OrderItemEO
エンティティ・オブジェクトのインスタンスを使用して、これが属するOrder
にも簡単にアクセスできます。
目的のアソシエーションを定義したら、「終了」をクリックします。
アソシエーションを作成すると、適切なXMLコンポーネント定義ファイルが作成され、パッケージ名と対応するディレクトリに保存されます。たとえば、oracle.fodemo.storefront.entities.associations
サブパッケージ内にOrderItemsOrdersFkAssoc
という名前のアソシエーションを作成した場合、アソシエーションXMLファイルがOrderItemsOrdersFkAssoc.xml
という名前で./oracle/fodemo/storefront/entities/associations
ディレクトリに作成されます。実行時には、エンティティ・オブジェクトはこのアソシエーション情報を使用して、一連の関連エンティティの操作を自動化します。
「アソシエーション・プロパティ」ページのアクセッサ名のデフォルト設定について検討し、これらの名前をより直感的なものに変更した方が適切かどうかを決定する必要があります。このデフォルト設定により、実行時に関連の反対側のエンティティにプログラム的にアクセスするときに使用するアクセッサ属性の名前が定義されます。デフォルトでは、これらのアクセッサ名は、反対側のエンティティ・オブジェクトの名前になります。エンティティのアクセッサ名は、エンティティ・オブジェクト属性とその他のアクセッサ間で一意である必要があるため、1つのエンティティが複数の方法で別のエンティティに関連付けられている場合、デフォルトのアクセッサ名は、名前を一意にするために数値接尾辞を使用して変更されます。
既存のアソシエーションでは、「アソシエーション・プロパティ」ダイアログを使用してアクセッサの名前を変更できます。
作業を始める前に、次のようにします。
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
アソシエーション内のエンティティ・アクセッサの名前を変更する手順:
「アプリケーション・ナビゲータ」で、アソシエーションをダブルクリックします。
概要エディタで、「関連」ナビゲーション・タブをクリックします。
「関連」ページで、「アクセッサ」カテゴリを展開し、「編集」アイコンをクリックします。
「アソシエーション・プロパティ」ダイアログに、アソシエーションのアクセッサの現在の設定が表示されます。
必要に応じて名前を変更し、「OK」をクリックして変更内容を適用し、ダイアログを閉じます。
アソシエーションは、通常はプロジェクトの最初に構成し、それ以降はそれほど頻繁に変更しないコンポーネントであるため、アソシエーションを別のパッケージに移動し、エンティティ・オブジェクトを見やすくできます。コンポーネントの名前変更と別のパッケージへの移動は両方とも、JDeveloperのリファクタ機能を使用して直接実行します。
作業を始める前に、次のようにします。
アソシエーションを作成する理由に関する知識が役立つ場合があります。詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
別のパッケージにビジネス・コンポーネントのセットを移動する手順:
アプリケーション・ナビゲータで、移動するコンポーネントを選択します。
選択したコンポーネントを右クリックし、「リファクタ」→「移動」を選択します。
「ビジネス・コンポーネントを移動」ダイアログで、コンポーネントを移動するパッケージの名前を入力するか、「参照」をクリックしてパッケージに移動して選択します。
「OK」をクリックして変更内容を適用し、ダイアログを閉じます。
コンポーネントの名前を変更する手順:
「アプリケーション・ナビゲータ」で、名前を変更するコンポーネントを右クリックし、「リファクタ」→「名前の変更」を選択します。
「名前の変更」ダイアログで、コンポーネントの新しい名前を入力し、「OK」をクリックします。
ADF Business Componentsをリファクタする場合、コンポーネントに関連するXMLファイルおよびJavaファイルがJDeveloperにより移動されるとともに、これらを参照するその他のコンポーネントが更新されます。
図4-7は、すべてのアソシエーションの名前を変更し、これらをoracle.fodemo.storefront.associations
サブパッケージに移動した後のアプリケーション・ナビゲータの様子を示しています。任意のパッケージ名を選択してアソシエーションをリファクタすることにより、サブパッケージを使用してアソシエーションとエンティティとの論理的な関連を維持しながらアソシエーションのパッケージを閉じることができるため、アプリケーション・ナビゲータに表示するファイルをより効率的に管理できます。
カスタム・ビュー・オブジェクトをエンティティ・アソシエーションのリンク元またはリンク先(あるいは両方)に関連付けることができます。
コード内でエンティティ・アソシエーションをトラバースするときに、エンティティがキャッシュにない場合は、ADF Business Componentsフレームワークによって問合せが実行され、エンティティ(複数可)がキャッシュに取り込まれます。デフォルトでは、エンティティをキャッシュに取り込むために実行される問合せは、主キーによる検索の問合せで、基礎となる表からすべての永続エンティティ属性の値を選択します。アプリケーションでプログラム的なエンティティ・アソシエーションのトラバースが多く実行される場合、すべての属性を取得することは、無理なユースケースになる場合があります。
エンティティ・アソシエーションでは、カスタムのエンティティ・ベースのビュー・オブジェクトをアソシエーション内のソース・エンティティまたは関連先エンティティ(あるいは両方)に関連付けることができます。指定するエンティティ・ベースのビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名は、それを使用するアソシエーションの端のエンティティ・タイプに一致する必要があります。
カスタム・ビュー・オブジェクトの問合せには少ない列を含めることが可能で、ORDER BY
句を含めることができるため、カスタム・ビュー・オブジェクトを使用する方が便利な場合があります。これにより、アソシエーションのトラバースによりエンティティがキャッシュに取り込まれる際に取得されるデータの量だけでなく、関連するエンティティのコレクションの順序も制御できます。
カスタム・ビュー・オブジェクトの作成の詳細は、42.8.2項「プログラムでエンティティ・ベースのビュー・オブジェクトを作成する方法」を参照してください。
アソシエーションは、Order
が参照するPerson
や、Order
に含まれるOrderItem
などのエンティティ間の関連を表しています。アソシエーションを作成する場合、表現可能な関連の種類や様々なオプションについて理解していると役に立ちます。
エンティティ・オブジェクト間の関連を使用して、次のようなソース・エンティティの状態に応じて2つのスタイルの関連を表現できます。
関連先エンティティを参照している
ネストされた論理的部分として関連先エンティティが含まれる
図4-8は、この2つのスタイルの関連を表すアプリケーション・ビジネス・レイヤーを示しています。たとえば、OrderEO
エントリはPersonEO
を参照しています。これらの関連は、第一種のアソシエーションを示し、PersonEO
またはOrderEO
エンティティ・オブジェクトは互いに独立して存在することができます。また、Order
を削除しても、これが参照していたPerson
がカスケード削除されることはありません。
一方、OrderEO
と関連するOrderItemEO
ディテールのコレクション間の関係は、単純な参照よりも強力です。OrderItemEO
エントリは、OrderEO
全体の論理部分を構成しています。つまり、OrderEO
はOrderItemEO
エントリで構成されています。OrderItemEO
エンティティ行にとって、OrderEO
から独立して存在することは意味をなさず、(削除が許可されていて)OrderEO
を削除する場合には、その構成部分もすべて削除する必要があります。このタイプの論理関係は、コンポジットと呼ばれる第二種のアソシエーションを示します。図4-8のUMLダイアグラムでは、アソシエーションで他方を構成する側に黒い菱形を使用して、より強力なコンポジット関連を示しています。
「表からのビジネス・コンポーネント」ウィザードでは、ON DELETE CASCADE
オプションを持つ外部キーに対してデフォルトでコンポジット・アソシエーションが作成されます。アソシエーション作成ウィザードまたはアソシエーションの概要エディタを使用すると、アソシエーションをコンポジット・アソシエーションとして指定できます。「アソシエーションの作成」ウィザードの「アソシエーション・プロパティ」ページ、または概要エディタの「関連」ページで、「コンポジット・アソシエーション」チェック・ボックスを選択します。コンポジット・アソシエーションがある場合、エンティティ・オブジェクトには実行時に別の動作が追加されます。動作の制御設定については、4.10.13項「コンポジット動作の構成方法」を参照してください。
ビジネス・ドメイン・オブジェクトのレイヤーはチームにとって再使用可能な主要資産となるため、多くの場合、UMLモデルを使用して視覚化すると使いやすくなります。JDeveloperでは、自分やチームのメンバーが参照用として使用できるビジネス・ドメイン・レイヤーのダイアグラムを簡単に作成できるようサポートされています。
ビジネス・コンポーネントのUMLダイアグラムは、エンティティ・オブジェクトをダイアグラムにドロップしたときを示す静的な図のみではありません。むしろ、UMLダイアグラムは、現在のコンポーネント定義をUMLベースでレンダリングした図であるため、常に現在の状況を示しています。さらに、UMLダイアグラムは、視覚的なサポートとナビゲーションを提供するツールであるとともに、編集用のツールでもあります。ダイアグラムでエンティティ・オブジェクトについて概要エディタを開くには、目的のオブジェクトを右クリックしてポップアップ・メニューから「プロパティ」を選択するか、目的のオブジェクトをダブルクリックします。また、エンティティおよびエンティティ属性の名前変更や属性の追加または削除など、エンティティ・オブジェクトの編集タスクをダイアグラム上で直接実行することもできます。
エンティティ・オブジェクトのダイアグラムを作成するには、「新規ギャラリ」から使用可能な「ビジネス・コンポーネント・ダイアグラムの作成」ダイアログを使用できます。
作業を始める前に、次のようにします。
エンティティ・ダイアグラムがアプリケーションで使用される方法に関する知識が役立つ場合があります。詳細は、4.4項「ビジネス・レイヤーのエンティティ・ダイアグラムの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
既存のエンティティ・オブジェクトをモデル化するエンティティ・ダイアグラムを作成する手順:
アプリケーション・ナビゲータで、エンティティ・ダイアグラムを作成するプロジェクトを右クリックし、「新規」をクリックします。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択したら、「ビジネス・コンポーネント・ダイアグラム」を選択し、「OK」をクリックします。
このダイアログで、次の操作を実行してダイアグラムを作成します。
ダイアグラムの名前を入力します(Business Domain Objects
など)。
ダイアグラムが作成されるパッケージ名を入力します。たとえば、myproject.model.design
などのサブパッケージ内にダイアグラムを作成します。
「OK」をクリックします。
既存のエンティティ・オブジェクトをダイアグラムに追加するには、アプリケーション・ナビゲータでこれらのオブジェクトを選択し、ダイアグラムにドロップします。
ダイアグラムを作成したら、プロパティ・インスペクタを使用してダイアグラムのビジュアル・プロパティを調整できます。たとえば、次の操作が可能です。
パッケージ名の表示と非表示の切替え
フォントの変更
グリッドおよび改ページの切替え
アソシエーション名の表示(表示しないと曖昧になる場合)
ダイアグラム上でポップアップ・メニューから「ダイアグラムの公開」を選択すると、ダイアグラムのイメージをPNG
、JPG
、SVG
または圧縮されたSVG
書式で作成することもできます。
図4-9は、ビジネス・ドメイン・レイヤーの様々なエンティティ・オブジェクトをモデル化したサンプル・ダイアグラムを示しています。
ビジネス・コンポーネント・ダイアグラムを作成すると、ダイアグラムが格納されているパッケージ名と一致するプロジェクトのモデル・パスのサブディレクトリに、ダイアグラムを示すXMLファイル*.oxd_bc4j
が作成されます。
デフォルトでは、アプリケーション・ナビゲータによってプロジェクト・コンテンツ・パスの表示が統一され、ソース・パスの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 Business Componentsオブジェクトから参照するための便利なメカニズムです。プロパティ・セット内で定義されたプロパティは、翻訳可能に構成でき、そのようなケースでは、翻訳されたプロパティはプロパティ・セットが所有するメッセージ・バンドル・ファイルに格納されます。
プロパティ・セットは、コントロール・ヒントやエラー・メッセージなど、様々な機能に使用できます。各プロパティ・セットには、コントロール・ヒントなどのカスタム・プロパティを含めることができ、それらを別のオブジェクトの複数の属性に関連付けることもできます。
注意: 翻訳可能なコンテンツを含むプロパティ・セットを定義する場合は注意が必要です。異なるコンテンツでは、共通の用語を過度に使用しないようにしてください。たとえば、ある言語では「Name」という用語をオブジェクトと個人の両方に適用できる場合でも、ターゲット言語では2つの異なる用語に翻訳される場合があります。ソース言語では複数のコンテンツで同じ用語にできる場合でも、コンテンツごとに区別できる用語を使用するようにしてください。 |
プロパティ・セットは、エンティティ・オブジェクトとその属性、ビュー・オブジェクトとその属性、およびアプリケーション・モジュールで使用できます。
プロパティ・セットを定義するには、ダイアログを使用して新規のプロパティ・セットを作成し、プロパティ・インスペクタを使用してプロパティを指定します。
作業を始める前に、次のようにします。
プロパティ・セットを使用できる方法に関する知識が役立つ場合があります。詳細は、4.5項「プロパティ・セットの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
プロパティ・セットを定義する手順:
「アプリケーション・ナビゲータ」で、プロパティ・セットを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから「プロパティ・セット」を選択し、「OK」をクリックします。
「プロパティ・セットの作成」ダイアログで、プロパティ・セットの名前および場所を入力し、「OK」をクリックします。
「ビュー」メニューから「プロパティ・インスペクタ」を選択します。
「プロパティ・インスペクタ」で、プロパティ・セットに対してプロパティを定義します。
プロパティ・セットを作成すると、プロパティ・セットをエンティティ・オブジェクトやエンティティ属性に適用できるだけでなく、定義されたプロパティを使用(必要に応じてオーバーライド)することもできます。
作業を始める前に、次のようにします。
プロパティ・セットを使用できる方法に関する知識が役立つ場合があります。詳細は、4.5項「プロパティ・セットの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトまたはビュー・オブジェクトにプロパティ・セットを適用する手順:
「アプリケーション・ナビゲータ」で、目的のオブジェクト(エンティティ・オブジェクトまたはビュー・オブジェクト)をダブルクリックします。
概要エディタの「一般」ナビゲーション・タブをクリックし、「プロパティ・セット」行の横の「編集」アイコンをクリックします。
適切なプロパティ・セットを選択し、「OK」をクリックします。
プロパティ・セットを属性に適用する手順:
「アプリケーション・ナビゲータ」で、属性のあるオブジェクト(エンティティ・オブジェクトまたはビュー・オブジェクト)をダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックしてから、編集する属性を選択し、「詳細」タブをクリックします。
「プロパティ・セット」ドロップダウン・リストで、適切なプロパティ・セットを選択します。
前のバージョンのADF Business Componentsになじみのあるユーザーであれば、すでにコントロール・ヒントを使用した経験がある場合があります。コントロール・ヒントを使用すると、エンティティ・オブジェクト属性のラベル・テキスト、ツールチップおよびフォーマット・マスクのヒントを定義できます。ビジネス・ドメイン・レイヤーで定義するUIヒントは、エンティティ・ベースのビュー・オブジェクトによって継承されます。また、ビュー・オブジェクトやアプリケーション・モジュールでも同様にコントロール・ヒントを追加設定できます。
属性のコントロール・ヒントをエンティティ・オブジェクトに追加するには、概要エディタを使用します。
作業を始める前に、次のようにします。
コントロール・ヒントがエンティティ・オブジェクトで使用される方法に関する知識が役立つ場合があります。詳細は、4.6項「エンティティ・オブジェクトの属性のコントロール・ヒントの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
属性のコントロール・ヒントをエンティティ・オブジェクトに追加する手順:
アプリケーション・ナビゲータで、目的のエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックしてから、編集する属性を選択し、「UIヒント」タブをクリックします。
コントロール・ヒントを必要に応じて指定します。
例として、図4-12は、PaymentOptionEO
エンティティ・オブジェクトの属性ExpireDate
に定義されたコントロール・ヒントを示しています。定義されるヒントは、次のとおりです。
フォーマットの種類: Simple Date
「フォーマット」マスク: mm/yy
注意: Javaで定義される数値および日付のフォーマット・マスクの標準セットは、OracleデータベースのSQLおよびPL/SQL言語によって使用されるものとは異なります。詳細は、 |
エンティティ・オブジェクトの属性のコントロール・ヒントを定義すると、これらを格納するリソース・バンドル・ファイルが作成されます。定義したヒントは、関連付けられたビュー・クライアントにおいて生成されたフォームおよび表によって使用できます。ファイルのタイプとその粒度は、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」オプションで指定します。詳細は、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
ドメインのフォーマット定義を示しています。
注意:
|
例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定義に、 |
翻訳可能な文字列(バリデータ・エラー・メッセージや、エンティティ・オブジェクトまたはビュー・オブジェクトの属性コントロール・ヒントなど)を定義する場合、デフォルトでは、プロジェクト・レベルのリソース・バンドル・ファイルが作成され、そこに翻訳可能文字列が格納されます。たとえば、StoreFront
プロジェクトでエンティティ・オブジェクトのコントロール・ヒントを定義すると、パッケージには、StoreFrontBundle.
xxx
という名前のメッセージ・バンドル・ファイルが作成されます。定義したヒントは、関連付けられたビュー・クライアントにおいて生成されたフォームおよび表によって使用できます。
JDeveloperで使用されるリソース・バンドルのオプションは、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」ページのオプションで指定します。このオプションは、デフォルトで「プロパティ・バンドル」に設定され、.properties
ファイルが生成されます。このオプションおよびその他のリソース・バンドル・オプションの詳細は、4.7.1項「メッセージ・バンドル・オプションの設定方法」を参照してください。
アプリケーション・ナビゲータでオブジェクトを選択し、構造ウィンドウの対応する「ソース」ノードを参照して、エンティティ・オブジェクトのメッセージ・バンドル・ファイルを検証できます。構造ウィンドウには、アプリケーション・ナビゲータで選択したコンポーネントの実装ファイルが表示されます。
例4-4は、コントロール・ヒントの情報が表示されたメッセージ・バンドル・ファイルのサンプルを示しています。各String
配列の最初のエントリはメッセージ・キーで、2番目のエントリはこのキーに対応するロケール固有のString
値です。
例4-4プロジェクト・メッセージ・バンドルに格納されるロケールに依存したコントロール・ヒント
AddressUsageEO_OwnerTypeCode_Error_0=Invalid OwnerTypeCode. AddressUsageEO_UsageTypeCode_Error_0=Invalid UsageTypeCode. OwnerTypeCode_CONTROLTYPE=105 PaymentOptionEO_RoutingIdentifier_Error_0=Please enter a valid routing identifier. PaymentOptionsEO_PaymentTypeCode_Error_0=Invalid PaymentTypeCode. PaymentTypeCode_CONTROLTYPE=105 PaymentOption_AccountNumber=Please enter a valid Account Number MinPrice_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter CostPrice_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter UnitPrice_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter OrderEO_GiftMessage=Please supply a message shorter than 200 characters OrderEO=Please supply a gift message DiscountBaseEO_DiscountAmount=Discount must be between 0 and 40% oracle.fodemo.storefront.entities.PaymentOptionEO.ExpireDate_FMT_FORMAT=mm/yy #Date range validation for ValidFrom and ValidTo dates PaymentOptionEO_invalidDateRange_Error_0=Date range is invalid. {0} must be greater than {1}. PaymentOptionEO_DateRange_Error_0=Invalid date range.{0} should be greater than {1}. oracle.fodemo.storefront.entities.PaymentOptionEO.ValidFromDate_LABEL=Valid From Date oracle.fodemo.storefront.entities.PaymentOptionEO.ValidToDate_LABEL=Valid To Date OrderItemsVO_ImageId_Rule_0=ImageId not found oracle.fodemo.storefront.store.queries.AddressesVO.Address1_LABEL=Address oracle.fodemo.storefront.store.queries.AddressesVO.PostalCode_LABEL=Post Code or ZIP . . .
JDeveloperでコントロール・ヒントおよびその他の翻訳可能文字列の保存に使用されるリソース・バンドル・オプションは、「プロジェクト・プロパティ」ダイアログの「リソース・バンドル」ページのオプションで指定します。このオプションは、デフォルトで「プロパティ・バンドル」に設定され、.properties
ファイルが生成されます。
作業を始める前に、次のようにします。
リソース・バンドルが使用される方法に関する知識が役立つ場合があります。詳細は、4.7項「リソース・バンドルの使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
プロジェクトのリソース・バンドル・オプションの設定手順:
アプリケーション・ナビゲータで、プロジェクトを右クリックし、「プロジェクト・プロパティ」を選択します。
「リソース・バンドル」をクリックします。
プロジェクト設定またはカスタム設定のどちらを使用するかを選択します。
「カスタム設定を使用」を選択すると、現在のプロジェクトの作業のみに設定が適用されます。これらの設定はセッション間で保存されますが、プロジェクトには記録されないため、他のユーザーとは共有できません。「プロジェクト設定を使用」を選択すると、選択内容がプロジェクトに記録され、プロジェクトを使用する他のユーザーと共有できます。
次のオプションを選択または宣言して、設定を指定します。
バンドルの自動同期化
ハードコード化された変換可能な文字列に関して警告
常に説明の入力を要求
これらのオプションの詳細は、「ヘルプ」をクリックしてオンライン・ヘルプを参照してください。
リソース・バンドルの粒度を選択します。
1プロジェクト当たり1バンドル(デフォルト)
1ファイル当たり1バンドル
複数の共有バンドル(ADF Business Componentsでは使用不可)
使用するファイルのタイプを選択します。
リスト・リソース・バンドル
ListResourceBundle
クラスは、名前/値の配列でリソースを管理します。各ListResourceBundleクラスは、Javaクラス・ファイルに格納されます。ListResourceBundleクラスには、ロケール固有のオブジェクトを格納できます。
プロパティ・バンドル(デフォルト)
翻訳可能なテキストを名前/値のペアで格納するテキスト・ファイルです。プロパティ・ファイル(例4-4に示すようなファイル)には、Stringオブジェクトのみに対応する値を格納できます。他のタイプのオブジェクトを格納する必要がある場合は、かわりにListResourceBundleを使用する必要があります。
Xliffリソース・バンドル
XML Localization Interchange File Format(XLIFF)は、ローカリゼーション・データの交換に対応したXMLベースのフォーマットです。
「OK」をクリックして設定を適用し、ダイアログを閉じます。
翻訳可能な文字列(属性コントロール・ヒントなど)を定義する場合は、「テキスト・リソースの選択」ダイアログで新しい文字列を入力したり、オブジェクトのデフォルトのリソース・バンドルで定義済の文字列を選択できます。必要に応じて、異なるリソース・バンドルを使用することもできます。これは、プロジェクト間で共有される共通のリソース・バンドルを使用する場合に便利です。
作業を始める前に、次のようにします。
リソース・バンドルが使用される方法に関する知識が役立つ場合があります。詳細は、4.7項「リソース・バンドルの使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
デフォルト以外のリソース・バンドルの設定を使用する手順:
「テキスト・リソースの選択」ダイアログで、使用するバンドルを「リソース・バンドル」ドロップダウン・リストから選択します。
目的のリソース・バンドルが「リソース・バンドル」ドロップダウン・リストにない場合は、「参照」アイコンをクリックして、使用するリソース・バンドルを検索して選択します。
ダイアログには、選択したリソース・バンドルで現在定義されている文字列が表示されます。
既存の文字列を選択し、「選択」をクリックするか、新しい文字列を入力して「保存して選択」をクリックします。
新しい文字列を入力すると、選択したリソース・バンドルに書き込まれます。
ADF Business Componentsを使用して作成したアプリケーションのモデル・レイヤーを国際化するには、各コンポーネントのメッセージ・バンドル・ファイルの翻訳バージョンを生成する必要があります。たとえば、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 devguide.model.entities.common; import oracle.jbo.common.JboResourceBundle; public class ServiceRequestImplMsgBundle_it extends ServiceRequestImplMsgBundle { 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
属性を識別子として指定できます。次に、リージョンごとにビジネス・ロジック・ユニットを定義し、各リージョンで従業員の給与にRange Validatorを指定します。Employee
エンティティから行がロードされると、(EmpRegion
属性の値に基づいて)EmpSalary
属性の適切なバリデータがロードされます。
Fusion Order DemoアプリケーションのStoreFrontモジュールから別の例をあげると、PersonEO
エンティティ・オブジェクトは、識別子属性としてPersonTypeCode
を使用する、PersonTypeCodeGroup
と呼ばれるビジネス・ロジック・グループを持ちます。この属性には3つの有効な値(CUST
、STAFF
およびSUPP
)があるため、対応するビジネス・ロジック・ユニットが3つあります。
このシナリオでは、各ビジネス・ロジック・ユニットにはそのユーザー・タイプのみに関連する新規または修正済ビジネス・ロジックが含まれます。
CUST
ビジネス・ロジック・ユニットには、顧客に関連するロジックが含まれます。 たとえば、すべての顧客が電話番号を持っている必要があるため、これには電話番号をチェックするバリデータが含まれます。
STAFF
ビジネス・ロジック・ユニットには、スタッフ・メンバーに関連するロジックが含まれます。たとえば、これには信用限度を制限するバリデータが含まれます。
SUPP
ビジネス・ロジック・ユニットには、サプライヤに関連するロジックが含まれます。 たとえば、関連会社からサプライヤへのコンタクトはできないようにしているため、これにはContactByAffiliatesFlag
属性がN
に設定されていることを確認するバリデータが含まれます。
エンティティ・オブジェクトのビジネス・ロジック・グループは、概要エディタから作成します。
作業を始める前に、次のようにします。
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・グループの作成手順:
「アプリケーション・ナビゲータ」で、ビジネス・ロジック・グループを作成するエンティティをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックします。
「一般」ページで、「ビジネス・ロジック・グループ」セクションを拡張し、「追加」アイコンをクリックします。
作成ダイアログで、適切なグループ識別子属性を選択し、グループの名前を指定します。
ヒント: コードの可読性を高めるには、識別子を反映するようなグループ名にします。たとえば、グループ識別子属性が |
「OK」をクリックします。
新しいビジネス・ロジック・グループが概要エディタの表に追加されます。グループを作成すると、ビジネス・ロジック・ユニットを追加できます。
ビジネス・ロジック。ユニットは「新規ギャラリ」から作成できます。また、ビジネス・ロジック・グループを格納するエンティティのポップアップ・メニューから直接作成することもできます。
作業を始める前に、次のようにします。
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットの作成手順:
「アプリケーション・ナビゲータ」で、ビジネス・ロジック・グループを格納するエンティティを右クリックし、ポップアップ・メニューからエンティティ・ビジネス・ロジック・ユニットの作成を選択します。
「ビジネス・ロジック・ユニットの作成」ダイアログで、ベース・エンティティの名前を指定し、適切なビジネス・ロジック・グループを選択します。
ビジネス・ロジック・ユニットの名前を入力します。
各ビジネス・ロジック・ユニットの名前には、このビジネス・ロジック・グループに関連付けられているグループ識別子属性の有効値を反映する必要があります。たとえば、グループ識別子属性がPersonTypeCode
の場合、STAFF
のPersonTypeCode
値に関連付けられているビジネス・ロジック・ユニットの名前はSTAFF
にする必要があります。
ビジネス・ロジック・ユニットのパッケージを指定します。
注意: ビジネス・ロジック・ユニットのパッケージは、ベース・エンティティまたはビジネス・ロジック・グループのパッケージと同じにする必要はありません。これにより、コア・アプリケーションと別にビジネス・ロジック・ユニットを開発して配信できます。 |
「OK」をクリックします。
JDeveloperでは、概要エディタでビジネス・ロジック・ユニットを作成して開きます。「アプリケーション・ナビゲータ」のビジネス・ロジック・ユニットに表示される名前は、EntityName_BusLogicGroupName_BusLogicUnitName
の形式で、エンティティ・オブジェクトの名前とビジネス・ロジック・グループが含まれます。たとえば、PersonEO
エンティティ・オブジェクトのPersonTypeCodeGroup
ビジネス・ロジック・グループにCUST
という名前でビジネス・ロジック・ユニットを作成する場合、表示されるビジネス・ロジック・ユニット名はPersonEO_PersonTypeCodeGroup_CUST
になります。
ビジネス・ロジック・ユニットを作成すると、そのビジネス・ロジック・ユニットに対してビジネス・ロジックを再定義できます。
ビジネス・ロジック・ユニットを作成したら、これを概要エディタで開き、ベース・エンティティで行う場合(エンティティレベルのバリデータを追加するなど)と同様に、ビジネス・ロジックを追加できます。
作業を始める前に、次のようにします。
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットにエンティティ・バリデータを追加する手順:
「アプリケーション・ナビゲータ」で、ビジネス・ロジック・ユニットをダブルクリックします。
概要エディタで、「ビジネス・ルール」ナビゲーション・タブをクリックします。
「ビジネス・ルール」ページで、Entity Validatorsノードを選択し、「追加」アイコンをクリックします。
検証ルールを定義し、「OK」をクリックします。
たとえば、Fusion Order DemoアプリケーションのStoreFrontモジュールのPersonEO
エンティティ・オブジェクトは、PersonEO_PersonTypeCodeGroup_CUST
と呼ばれるビジネス・ロジック・ユニットを持ちます。このビジネス・ロジック・ユニットには、電話番号の存在をチェックするエンティティ・バリデータがあり、顧客であるすべてのユーザーが電話番号を持っていることを確認します。
概要エディタでビジネス・ロジック・ユニットの「属性」ページを表示すると、属性表の「拡張」列に、属性がビジネス・ロジック・ユニットで拡張されていることが表示されます。拡張属性は、ビジネス・ロジック・ユニットではなく、ベース・エンティティでのみ編集できます。ベース・エンティティではなくビジネス・ロジック・ユニットで変更する場合は、編集する前に、ビジネス・ロジック・ユニットで属性をオーバーライド済として定義する必要があります。
作業を始める前に、次のようにします。
ビジネス・ロジック・グループが使用される方法に関する知識が役立つ場合があります。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・ロジック・ユニットの属性をオーバーライドする手順:
「アプリケーション・ナビゲータ」で、ビジネス・ロジック・ユニットをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックします。
「属性」ページで、目的の属性を選択し、 「オーバーライド」ボタンをクリックします。
属性をオーバーライドしたら、次の表にあるタブで通常どおりに属性を編集できます。オーバーライドされた属性では、コントロール・ヒント、バリデータおよびデフォルト値の編集のみ行えます。
ビジネス・ロジック・グループを作成すると、ベース・エンティティのXMLファイルのグループに参照が追加されます。例4-6は、ビジネス・ロジック・グループのベース・エンティティのXMLファイルに追加されたコードを示しています。
例4-6 ビジネス・ロジック・グループのベース・エンティティのXMLコード
<BusLogicGroup Name="PersonTypeCodeGroup" DiscrAttrName="PersonTypeCode"/>
ビジネス・ロジック・ユニットを作成すると、エンティティ・オブジェクトのXMLファイルと同様のXMLファイルが生成されます。例4-7は、ビジネス・ロジック・ユニットのXMLコードを示しています。
注意: ビジネス・ロジック・ユニットのパッケージは、ベース・エンティティまたはビジネス・ロジック・グループのパッケージと同じにする必要はありません。これにより、コア・アプリケーションと別にビジネス・ロジック・ユニットを開発して配信できます。 |
例4-7 ビジネス・ロジック・ユニットのXMLコード
<Entity xmlns="http://xmlns.oracle.com/bc4j" Name="PersonEO_PersonTypeCodeGroup_CUST" Version="11.1.1.54.6" Extends="oracle.fodemo.storefront.entities.PersonEO" DBObjectType="table" DBObjectName="PERSONS" BindingStyle="OracleName" UseGlueCode="false" BusLogicGroupName="PersonTypeCodeGroup" BusLogicUnitName="CUST" xmlns:validation="http://xmlns.oracle.com/adfm/validation"> <DesignTime> <Attr Name="_codeGenFlag2" Value="Access"/> <AttrArray Name="_publishEvents"/> </DesignTime> <validation:ExpressionValidationBean Name="PersonEO_PersonTypeCodeGroup_CUST_Rule_0" OperandType="EXPR" Inverse="false"> <validation:MsgIds> <validation:Item Value="CUST_PHONE_REQUIRED"/> </validation:MsgIds> <validation:TransientExpression> <![CDATA[if (PhoneNumber == null && MobilePhoneNumber == null) return false; else return true;]]> </validation:TransientExpression> </validation:ExpressionValidationBean> <ResourceBundle> <PropertiesBundle PropertiesFile="oracle.fodemo.storefront.entities.common.PersonEO_PersonTypeCodeGroup_CUSTMsgBundle"/> </ResourceBundle> </Entity>
実行時にアプリケーションで行がロードされると、適用するビジネス・ロジック・ユニットがエンティティ・オブジェクトによって決定されます。
ベース・エンティティには、ビジネス・ロジック・グループのリストが格納されています。各グループは、エンティティの属性値を参照し、この値によってそれぞれのグループにロードするビジネス・ロジック・ユニットが決定されます。この評価は、ロードされる行ごとに実行されます。
ロードするビジネス・ロジック・ユニットを決定するロジックが簡単な属性値ではなく複雑な場合は、エンティティ・オブジェクトに一時属性を作成し、Groovy式を使用して一時属性の値を指定できます。
エンティティ・オブジェクトには、通常のエンタープライズ・ビジネス・アプリケーションの実装を簡略化するための様々な宣言的機能が用意されています。タスクによっては、宣言的機能のみでニーズが満たされる場合があります。エンティティ・オブジェクトの基本的な永続機能を記述する宣言的な実行時機能はこの項で説明しますが、宣言的な検証およびビジネス・ルールについては、第7章「検証とビジネス・ルールの宣言的な定義」で説明します。
注意: 必要に応じて、宣言的動作を超えて、より複雑なビジネス・ロジックまたは検証規則をビジネス・ドメイン・レイヤーに実装することができます。カスタム・コードを使用した最も一般的なエンティティ・オブジェクトの拡張方法は、第8章「プログラムによる検証とビジネス・ルールの実装」を参照してください。 |
またアプリケーションを開発する際の注意点として、プログラム的または宣言的にビジネス・ロジックを実装する場合、エンティティ・オブジェクトまたはビュー行の属性が特定の順序に従って設定されることを想定しないようにすることが重要です。順序が想定されていると、それとは異なる順序でエンド・ユーザーが属性値を入力した場合に問題が発生します。
エンティティ・オブジェクトの宣言的な実行時動作を構成するには、概要エディタを使用します。
作業を始める前に、次のようにします。
実行時動作の宣言的な構成に関する知識が役立つ場合があります。詳細は、4.9項「宣言的な実行時動作の構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトの宣言的な実行時動作を構成する手順:
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックしてエンティティ・オブジェクトの名前およびパッケージを表示し、関連付けられたスキーマ、代替キー、カスタム・プロパティおよびセキュリティなど、オブジェクトのエンティティ・レベルでの様々な構成を行います。
「代替キー」セクションでは、代替主キーとして機能できる、データベースにマッピングされたエンティティ・オブジェクト属性を選択できます。代替キーの詳細は、4.10.15項「代替キー値の定義方法」を参照してください。
「チューニング」セクションでは、単一のトランザクションで同じタイプの複数のエンティティを作成、変更または削除するときのデータベース操作をより効率的にするためのオプションを設定できます。詳細は、4.9.3項「バッチ更新の使用方法」を参照してください。
「カスタム・プロパティ」セクションでは、実行時にエンティティでアクセス可能なカスタム・メタデータを定義できます。
「セキュリティ」セクションでは、エンティティについてロール・ベースの更新可能権限を定義できます。詳細は、第35章「Fusion WebアプリケーションでのADFセキュリティの有効化」を参照してください。
「ビジネス・ロジック・グループ」セクションでは、ビジネス・ロジック・グループと追加および削除できます。詳細は、4.8項「ビジネス・ロジック・グループの定義」を参照してください。
「属性」ナビゲーション・タブをクリックして、エンティティ・オブジェクトに関連するデータを示す属性を作成または削除し、検証規則、カスタム・プロパティおよびセキュリティなど、属性の様々な構成を行います。
属性を選択し、「編集」アイコンをクリックして属性のプロパティにアクセスします。これらのプロパティの設定方法の詳細は、4.10項「属性プロパティの設定」を参照してください。
ヒント: エンティティの属性名のリストが長い場合、目的の属性を簡単に見つける方法があります。構造ウィンドウの「属性」ノードが開かれた状態で属性名を入力すると、入力した文字に応じて属性がインクリメンタル検索され、ツリー内のその名前が表示されます。 |
「ビジネス・ルール」ナビゲーション・タブをクリックして、エンティティ・オブジェクトとその属性の宣言的バリデータを定義します。詳細は、第7章「検証とビジネス・ルールの宣言的な定義」を参照してください。
「Java」ナビゲーション・タブをクリックし、カスタムJava実装に対して生成するクラスを選択します。Javaクラスは、プログラム的なビジネス・ルールの定義などに使用できます(第8章「プログラムによる検証とビジネス・ルールの実装」を参照)。
「ビジネス・イベント」ナビゲーション・タブをクリックし、エンティティ・オブジェクトが、その状態の変更を他に通知する際に使用できるイベント(およびオプションで、配信イベントに含めるエンティティ・オブジェクトの属性の一部または全部)を定義します。ビジネス・イベントの詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
「ビュー・アクセッサ」ナビゲーション・タブをクリックし、ビュー・アクセッサを作成および管理します。詳細は、10.4.1項「エンティティ・オブジェクトまたはビュー・オブジェクトのビュー・アクセッサの作成方法」を参照してください。
エンティティ・オブジェクトの実行時動作を記述および制御する宣言的設定は、XMLコンポーネント定義ファイルに格納されます。概要エディタを使用してエンティティの設定を変更する場合、JDeveloperによりコンポーネントのXML定義ファイルおよびオプションのカスタムJavaファイルが更新されます。
バッチ更新を使用すると、複数のエンティティ変更の際に発行されるDML文の数を減らすことができます。
デフォルトで、ADF Business Componentsフレームワークは、指定されたエンティティ定義型の変更済エンティティ1つにつき、1つのDML文(INSERT
、UPDATE
、DELETE
)を実行します。たとえば、Employeeエンティティ・オブジェクト型があり、アプリケーションの通常使用でその複数のインスタンスが変更されたとします。2つのインスタンスが作成され、3つの既存のインスタンスが変更され、4つの既存のインスタンスが削除された場合、それらの変更を保存するために、トランザクション・コミット時間に、フレームワークにより9つのDML文(2つのINSERT
文、3つのUPDATE
文および4つのDELETE
文)が発行されます。
トランザクションで、ある型の複数のエンティティを頻繁に更新する場合、そのエンティティ定義型に対してバッチ更新機能を使用することを検討してください。この例では、バッチ更新(しきい値は1)でフレームワークが発行するDML文は、2件の挿入を処理する1つの一括INSERT
文、3件の更新を処理する1つの一括UPDATE
文および4件の削除を処理する1つの一括DELETE
文の3つのみです。
注意: 次のいずれかの場合、バッチ更新機能は無効になります。
|
作業を始める前に、次のようにします。
実行時動作の宣言的な構成に関する知識が役立つ場合があります。詳細は、4.9項「宣言的な実行時動作の構成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティに対してバッチ更新を有効化する手順:
「アプリケーション・ナビゲータ」で、適切なエンティティをダブルクリックします。
概要エディタで、「一般」ナビゲーション・タブをクリックします。
概要エディタの「一般」ページで「チューニング」セクションを展開し、「バッチ更新を使用」チェック・ボックスを選択して、適切なしきい値を指定します。
これにより、それを超えるとOracle ADFが一括DML操作を使用して変更を処理する、バッチ処理のしきい値が指定されます。
宣言的なフレームワークによって、属性プロパティを簡単に設定できます。いずれの場合も、これらのプロパティは概要エディタの「属性」ページで設定します。
「永続的」
プロパティにより、基礎となる表内の列に属性値が対応するか、単なる一時的な値であるかを制御します。属性が永続的である場合、「データベース列」領域を使用して、属性に対応する基礎となる列の名前を変更し、精度およびスケール情報を使用してその列型を指定できます(VARCHAR2(40)
やNUMBER(4,2)
など)。エンティティ・オブジェクトは、実行時にこの情報に基づいて、属性値の最大長および精度/スケールを設定し、値が要件を満たさない場合は例外をスローします。
「表からのビジネス・コンポーネント」ウィザードと「エンティティ・オブジェクトの作成」ウィザードでは、各エンティティ・オブジェクト属性のJava型は、関連付けられている列のデータベース列型のSQL型から推測されます。
注意: プロジェクトの「型マップ」設定は、Javaデータ型の決定にも役割を果します。ビジネス・コンポーネント・プロジェクトを初期化する場合は、ビジネス・コンポーネントが作成される前に「型マップ」設定を指定します。詳細は、3.3.1項「接続、SQLプラットフォームおよびデータ型マップの選択」を参照してください。 |
(「詳細」タブの)「型」フィールドでは、エンティティ属性のJava型を目的の型に変更できます。「列の型」フィールドには、属性がマップされている、基礎となるデータベース列のSQL型が表示されます。「列名」フィールドの値により、属性がマップされる列が制御されます。
エンティティ・オブジェクトは、表4-1に示す様々な列型の表を処理できます。java.lang.String
クラスを除いて、デフォルトのJava属性タイプはすべてoracle.jbo.domain
およびoracle.ord.im
パッケージに格納されており、対応する型のOracleデータベースのデータを効率的に処理できるようサポートされています。「型」フィールドのドロップダウン・リストには、その他の多くの共通型が含まれており、これらもサポートされています。
表4-1デフォルトのエンティティ・オブジェクトの属性タイプのマッピング
Oracleの列型 | エンティティの列型 | エンティティの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)
に更新します。
「更新可能」
プロパティにより、特定の属性値をいつ更新できるかを制御します。選択できる値は次のとおりです。
常に: 属性は常に更新可能です。
なし: 属性は読取り専用です。
新規の間: エンティティ行を初めて作成するトランザクション中は属性を設定できますが、データベースへのコミットが成功した後、属性は読取り専用になります。
注意: 更新可能性の静的な宣言に加えて、エンティティの |
フィールドが必須の場合は、「必須」チェック・ボックスを選択します。必須プロパティは、実行時、(属性バリデータの実行時ではなく)エンティティ・レベルの検証が行われる際に適用されます。
「主キー」
プロパティにより、属性がエンティティを一意に識別するキーの一部であるかどうかを指定します。通常、主キーには単一属性を使用しますが、複数属性の主キーも完全にサポートされています。
実行時には、getKey()
メソッドを使用して任意のエンティティ行に関連するKey
オブジェクトにアクセスすると、このKey
オブジェクトには、このエンティティ・オブジェクトの主キー属性の値が含まれます。このエンティティ・オブジェクトに複数の主キー属性がある場合、Key
オブジェクトにはこれらの各値が含まれます。これらの値が、エンティティ・オブジェクト定義で対応する主キー属性と同じ相対順序で表示されると理解することが重要です。
たとえば、OrderItemEO
エンティティ・オブジェクトには、OrderId
およびLineItemId
という複数の主キー属性があります。概要エディタの「エンティティ属性」ページでは、OrderId
が先頭で、LineItemId
が2番目です。OrderItemEO
型のエンティティ行のKey
オブジェクトによってカプセル化された値の配列では、これらの2つの属性値がこれとまったく同じ順序になります。
「エンティティ属性」ページでは、複数の主キー属性がどのような順序で表示されているかを理解しておくことが非常に重要です。findByPrimaryKey()
を使用して複数属性の主キーを持つエンティティを検索する場合、作成したKey
オブジェクトでこれら複数の主キー属性の順序が間違っていると、エンティティ行が期待したとおりに検出されません。
さらに、主キーを新しい行に移入するには、トリガーを使用してデータベースから値を割り当てる場合もあります。詳細は、4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照してください。
値のタイプが「リテラル」に設定されている場合、「詳細」タブの「値」フィールドには、属性のデフォルト値を静的に指定できます。たとえば、ServiceRequest
エンティティ・オブジェクトのStatus
属性のデフォルト値をOpen
に設定したり、User
エンティティ・オブジェクトのUserRole
属性のデフォルト値をuser
に設定できます。
注意: 1つのエンティティ・オブジェクトに複数の属性がデフォルト設定される場合は、エンティティ・オブジェクトのXMLファイルにおいてそれらの属性が表示されている順にデフォルト設定されます。 |
属性のデフォルト値は、Groovy式またはSQL文を使用して定義できます。実行時にデフォルト値を動的に定義できるようにする場合はこの方法が便利ですが、デフォルト値が常に同じ場合は、(「詳細」タブの)「リテラル」型のフィールドを使用すると、より簡単に値を表示および管理できます。Groovyの使用の詳細は、3.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;
「詳細」タブ(概要エディタの「属性」ページ)で、「型」フィールドの値を組込みデータ型(名前はDBSequence
)に設定でき、主キーが自動的にデータベース順序によって割り当てられます。このデータ型を設定すると、「挿入時にリフレッシュ」チェック・ボックスが自動的に選択されます。
注意: 「順序」タブに表示されている順序名が設計時に使用されるのは、4.2.6項「エンティティ・オブジェクトからのデータベース表の作成方法」で説明されているデータベース表の作成機能を使用する場合のみです。ここで示す順序は、エンティティ・オブジェクトの基礎となる表とともに作成されます。 |
主キーがDBSequence
である新しいエンティティ行を作成すると、一意の負の数値が一時値として割り当てられます。この値は、このエンティティ行を作成するトランザクション中、主キーとして機能します。同じトランザクション内で一連の相関エンティティを作成する場合、その他の新しい関連エンティティ行にこの一時値を外部キー値として割り当てることができます。トランザクションのコミット時に、エンティティ・オブジェクトは、RETURNING INTO
句を使用してINSERT
操作を発行し、実際にデータベースのトリガーによって割り当てられた主キー値を取得します。コンポジット関連では、先にこの一時的な負の値を外部キーとして使用していた新しい関連エンティティでは、マスターの実際の新しい主キーを反映してこの値が更新されます。
通常は、DBSequence値を持つ主キーの「更新可能」プロパティも「なし」に設定します。一時IDはエンティティ・オブジェクトによって割り当てられ、INSERT
操作の後に実際のID値を使用してリフレッシュされます。エンド・ユーザーがこの値を更新する必要はありません。
コンポジット以外のアソシエーションでこの機能を実装する方法の詳細は、4.14.7.3.3項「DBSequenceの値が設定される主キーに基づくアソシエーション」を参照してください。
実行時には、フレームワークによりエンティティ・オブジェクトの更新の上書きが自動的に検出され、作業中に別のユーザーによって更新およびコミットされたデータであることに気付かずにユーザーがこのデータを変更できないようにします。通常、このチェックは、基礎となる行がロックされているときにデータベース内で対応する現在の列値と各永続エンティティ属性の元の値を比較することによって実行されます。行の更新前に、エンティティ・オブジェクトによって、更新される行とデータベースの現在の状態の間に一貫性があるかどうかが検証されます。行とデータベースの状態の間に一貫性がない場合、エンティティ・オブジェクトではRowInconsistentException
が発生します。
更新の上書きの検出をより効率的にするには、エンティティが変更されるたびに値が更新されるエンティティの属性を特定します。通常、このような値には、行内のバージョン番号列や更新日付列などがあります。(「詳細」タブの)「挿入時にリフレッシュ」オプションまたは「更新時にリフレッシュ」オプションを選択したので、作成したデータベース・トリガーによって更新識別子の属性値を割り当て、エンティティ・オブジェクトでこれらの値をリフレッシュできます。また、4.10.12項「履歴列を使用して作成および変更した日付を追跡する方法」で説明する履歴属性の機能を使用して、エンティティ・オブジェクトによって更新識別子の属性値の更新が管理されるように指定できます。ユーザーによる問合せ以降に行が変更されたかどうかを最も効率的な方法で検出するには、「更新識別子」オプションを選択し、更新識別子の属性値のみを比較します。
エンティティの作成日時や変更日時、作成者や変更者、エンティティの変更回数など、エンティティ・オブジェクトで履歴情報を追跡する必要がある場合、「変更履歴のトラッキング」オプションが選択されている属性を指定します(「詳細」タブ)。
属性のデータ型がNumber
、String
またはDate
であり、主キーの一部ではない場合、このプロパティを有効化して履歴監査用の属性値を自動的に管理できます。フレームワークにおける属性の処理方法は、次のような履歴属性のタイプの中からどれを指定するかによって異なります。
作成日付: この属性には、行が作成された日付のタイム・スタンプが移入されます。タイム・スタンプはデータベースから取得されます。
作成者: この属性には、行を作成したユーザーの名前が移入されます。ユーザー名は、Session
オブジェクトのgetUserPrincipleName()
メソッドを使用して取得されます。
変更日付: この属性には、行が更新/作成された日付のタイム・スタンプが移入されます。
変更者: この属性には、行を作成/更新したユーザーの名前が移入されます。
バージョン番号: この属性には、行が作成または更新されるたびに増分するlong値が移入されます。
エンティティ・オブジェクトでは、OrderItemEO
エンティティを作成するOrderEO
エンティティなど、他のエンティティを作成(または構成)する場合、コンポジット動作が示されます。この付加的な実行時動作により、ネストされた他のエンティティ・オブジェクト部分の論理コンテナとしての役割が決定されます。
エンティティ・オブジェクトの構成で常に有効な機能については、次の項を参照してください。
別の機能およびこれらの動作に影響するプロパティについては、次の項を参照してください。
構成されるエンティティ・オブジェクトが作成されると、この値が既存のエンティティを所有する親のエンティティとして識別しているか確認するため、外部キー属性の値の存在がチェックされます。作成時に外部キーが検出されなかった場合や、既存のエンティティ・オブジェクトを識別しない値が検出された場合、エンティティ・オブジェクトでは、親エンティティが識別されていない状態で親のない子行が作成されないように、InvalidOwnerException
がスローされます。
注意: 外部キー属性の値の存在がチェックされたことにより、現在のトランザクションで新しい保留エンティティが検索される他、必要に応じてデータベース内の既存のエンティティが検索されます。 |
コンポジット動作により、構成する側のエンティティ・オブジェクトと構成される側のエンティティ・オブジェクトの両方が含まれるトランザクションで実行されるデータ操作言語(DML)操作が正しい順序で実行されたかどうかを確認します。たとえば、構成する側の新しい親エンティティ・オブジェクトに対するINSERT
文は、構成される側の子エンティティ・オブジェクトに関連するDML操作の前に実行されます。
主キーが挿入時にリフレッシュするように構成されている新しいエンティティ行を保存すると、トリガーによって割り当てられた主キー値の取得後に、構成される側のエンティティの外部キー属性値が更新されて新しい主キー値が反映されます。
これ以外にも、アソシエーション作成ウィザードの「アソシエーション・プロパティ」ページまたは概要エディタの設定を介して制御可能なコンポジット関連機能が多数用意されています。図4-13は、2つのエンティティ・オブジェクトOrderItemEO
とOrderEO
の間のOrderItemsOrdersFkAssoc
アソシエーションが表示された「関連」ページを示しています。
構成される側の子が存在していても、構成する側の親の削除を有効化または禁止できます。「カスケード削除の実装」オプション(図4-13を参照)の選択が解除されている場合に、構成する側の親に構成される側の子が含まれる場合、構成する側のエンティティ・オブジェクトの削除は禁止されます。
このオプションが選択されている場合、構成する側のエンティティ・オブジェクトを、構成される側の子エンティティとともに無条件に削除できます。関連する「データベースのカスケード削除用に最適化」オプションの選択が解除されている場合、構成される側のエンティティ・オブジェクトでは、トランザクションのコミット時に通常のDELETE
文が実行され、変更が永続的になります。このオプションが選択されている場合、データベースのON DELETE CASCADE
制約によって対応する行の削除が処理されることを前提に、構成される側のエンティティではDELETE
文は実行されません。
「カスケード更新キー属性」オプション(図4-13を参照)を選択すると、構成する側のエンティティの主キー値が変更される場合に、構成される側のエンティティの外部キー属性値が自動で更新されるよう設定できます。
「最上位コンテナのロック」オプション(図4-13を参照)を選択すると、構成される側の詳細エンティティ行の追加、削除または変更時に、変更を保存する前に、構成する側のエンティティをロックするかどうかを制御できます。
「トップレベルの履歴列の更新」オプション(図4-13を参照)を選択すると、構成される側の詳細エンティティ・オブジェクトの追加、削除または変更時に、構成する側の親エンティティの「変更者」および「変更日付」履歴属性を更新するかどうかを制御できます。
場合によっては、論理的に関連付けられた様々なオブジェクトに関する情報が1つのデータベース表に格納されることがあります。たとえば、給与計算アプリケーションでは、アルバイト、正社員、契約社員すべてが、EMPLOYEE_TYPE
列を持つ1つのEMPLOYEES
表に格納されることがあります。この場合、EMPLOYEE_TYPE
列では、H
、S
、C
などの値を使用して、特定の行がそれぞれアルバイト(Hourly)、正社員(Sararied)、契約社員(Contract)のいずれを表すのかを示します。多くの属性や動作はすべての従業員で同一の場合もありますが、特定のプロパティやビジネス・ロジックは従業員のタイプによって異なることがあります。
関連するオブジェクトの間に共通情報が存在する場合は、継承階層を使用してこのような異なるタイプのエンティティ・オブジェクトを表すと便利です。たとえば、すべての従業員に共通の属性およびメソッドは、ベースとなるEmployee
エンティティ・オブジェクトの一部とし、HourlyEmployee
、SalariedEmployee
およびContractEmployee
などのサブタイプのエンティティ・オブジェクトでは、ベースとなるEmployee
オブジェクトを拡張し、別のプロパティや動作を追加します。「多相化識別子」属性設定を使用して、行のタイプを識別する属性の値を示します。継承の設定方法および使用方法は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
データベースの主キーは順序から生成されることが多く、様々な理由からユーザーへの公開を避けることが必要な場合があります。このため、多くのケースでは一意の代替キー値を使用すると便利です。たとえば、すべての顧客に一意の電子メール・アドレスを持たせるとします。顧客は電子メール・アドレスを変更する可能性があるため、主キーとしてはこの値を使用できませんが、それぞれの顧客に対してログインなどの目的で使用できる一意のフィールドを持たせる必要があります。
代替キーは、メソッドのfindByKey
クラスを介して直接行を参照する場合に便利です。代替キーは、中間層で効率的な一意性チェックを行う場合にもよく使用されます。値が一意であるかどうかの確認方法の詳細は、7.4.1項「キー値の一意性を確認する方法」を参照してください。
代替キーの定義には、エンティティ制約の作成ウィザードを使用します。
作業を始める前に、次のようにします。
属性プロパティの設定方法の知識があると役立ちます。詳細は、4.10項「属性プロパティの設定」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
代替キー値を定義する手順:
アプリケーション・ナビゲータで、エンティティ・オブジェクトを右クリックし、ポップアップ・メニューから「新規エンティティ制約」を選択します。
エンティティ制約の作成ウィザードの手順に従って、制約の名前を指定し、キーとして使用する属性(複数可)を選択します。
「プロパティ」ページで、「代替キー」を選択し、適切な「キー・プロパティ」オプションを選択します。
「キー・プロパティ」の詳細情報は、[F1]キーを押すか「ヘルプ」をクリックすると参照できます。
基礎となる表内の列にマップされる属性のみでなく、エンティティ・オブジェクトには、値ホルダーである一時属性や(JavaやGroovyなどを使用して)計算された値を表示する一時属性を組み込むことができます。たとえば、FullName
などの作成した一時属性は、FirstName
属性の値とLastName
属性の値を連結した値に基づいて計算できます。
一時属性を作成すると、エンティティ・オブジェクトのJavaクラスで計算を実行したり、属性定義でGroovy式を使用してデフォルト値を指定できます。
実行時に値を変更できるようにする必要がある場合は、Groovy式を使用します。計算された後に変更される可能性が低い値(明細項目の合計など)については、エンティティ・オブジェクトのJavaクラス内で直接計算できます。
一時属性を作成するには、概要エディタの「属性」ページを使用します。
作業を始める前に、次のようにします。
一時属性および計算属性の使用に関する知識があると役立ちます。詳細は、4.11項「エンティティ・オブジェクトへの一時属性および計算属性の追加」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトに一時属性を追加する手順:
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックし、「新規」アイコンをクリックします。
「新規エンティティ属性」ダイアログで属性の名前を入力し、「OK」をクリックします。
「詳細」タブ(概要エディタ)で、Java属性の型を設定してから、「一時」オプションを選択します。
値を計算する場合、「更新可能」を「なし」に設定します。
一時属性を追加すると、エンティティ・オブジェクトのXMLコンポーネント定義が更新され、新しい属性が反映されます。
例4-10に示すように、一時属性の<Attribute>
タグには、TableName
はなく、ColumnName
が$none$
になっています。
例4-10 一時属性のXMLコード
<Attribute Name="FullName" IsUpdateable="false" IsQueriable="false" IsPersistent="false" ColumnName="$none$" Type="java.lang.String" ColumnType="$none$" SQLType="VARCHAR" > </Attribute>
これに対し、永続的エンティティ属性には、例4-11のようにTableName
とColumnName
の両方があります。
一時属性を作成する場合、デフォルト値の指定にGroovy式を使用できます。
作業を始める前に、次のようにします。
一時属性および計算属性の知識があると役立ちます。詳細は、4.11項「エンティティ・オブジェクトへの一時属性および計算属性の追加」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
Groovy式に基づいて一時属性を作成する手順:
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「属性」ナビゲーション・タブをクリックし、「新規」アイコンをクリックします。
「新規エンティティ属性」ダイアログで属性の名前を入力し、「OK」をクリックします。
「詳細」タブ(概要エディタ)で、Java属性の型を設定してから、「一時」オプションを選択します。
値を計算する場合、「更新可能」を「なし」に設定します。
「デフォルト値タイプ」から「式」を選択し、「値」フィールドの隣の「編集」ボタンをクリックします。
定義した式は、Groovyスクリプト言語を使用して評価され(3.6項「Groovyスクリプト言語サポートの概要」を参照)、Groovyにより式や変数を文字列に挿入できます。この式は、エンティティ・オブジェクト定義の一部として保存されます。
図4-14に示すように、「式の編集」ダイアログで、表示されているフィールドに式を入力します。
参照する属性には、エンティティ・オブジェクトで定義されている任意の属性を使用できます。式にある属性のうち、エンティティ・オブジェクトで定義されていない属性は参照しないでください。
適切な再計算設定を選択します。
「常に」(デフォルト)を選択すると、行のいずれかの属性が変更されるたびに式の評価が行われます。「なし」を選択すると、行が作成された場合にのみ式の評価が行われます。
必要に応じて、式の再計算を実行するタイミングの条件を指定できます。
たとえば、「次の式に基づく」フィールドに次のような式を入力すると、Quantity
属性またはUnitPrice
属性が変更された場合に属性が再計算されます。
return (adf.object.isAttributeChanged("Quantity") || adf.object.isAttributeChanged("UnitPrice"));
この属性が依存する属性もリストできます。
図4-14では、Quantity
およびUnitPrice
属性が選択されているので、いずれかの属性が変更されると属性は再計算されます。
「OK」をクリックして、式を保存します。
次に、「OK」をクリックし、属性を作成します。
注意: 定義した値式またはオプションの再計算式がベース・エンティティ・オブジェクトの属性を参照する場合は、「依存性」タブ(「属性」ページ)でこれを依存性として定義する必要があります。「依存性」タブで、「使用可能」リストで属性を特定し、それぞれを「選択済」リストに移動します。 |
一時属性が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
など)が変更されるたびに、一時属性が再評価されるようにするには、一時属性の依存属性を指定します。「属性」ページの「依存性」タブで、「使用可能」リストで属性を特定し、それぞれを「選択済」リストに移動します。
モデル・レイヤーから呼び出されたビジネス・イベントは、ビジネス・プロセスを起動する際や、Oracle Mediator経由で外部システム同期化をトリガーする際に役立ちます。
Oracle Mediatorでは、ビジネス・イベントをアクションにマッピングする宣言的サブスクリプションがサポートされています。つまり、あるコンポーネントでビジネス・イベント(新規顧客作成など)を定義して公開した後、別のコンポーネントでこのイベントをサブスクライブすると、このビジネス・プロセスが発生したときに通知されます。次に、サブスクライブ中のコンポーネントで、そのイベントに割り当てているアクション(新規顧客への挨拶メールの送信など)を実行できます。
ビジネス・イベントはエンティティ・レベルで宣言的に定義します。また、それらのイベントを呼び出す条件を指定できます。指定の基準を満たすビジネス・イベントは、変更されたデータが正常にコミットされたときに呼び出されます。ビジネス・イベントは、エンティティ・オブジェクトが正常に作成、更新または削除されたときにMediatorに発行されます。
ビジネス・イベントを実装するには、次の処理を実行します。
4.12.4項「ビジネス・イベントの作成方法」の説明に従ってイベント定義を作成します。
4.12.4項「ビジネス・イベントを公開する方法」の説明に従って、イベント定義をイベント・ポイントにマッピングし、そのイベント定義を公開します。
4.12.8項「ビジネス・イベントをサブスクライブする方法」の説明に従って、ビジネス・イベントが公開されたら、別のコンポーネントからそのイベントをサブスクライブできます。
イベント定義には、イベント・システムMediatorで公開および呼び出されるイベントが記述されます。イベント定義は、表4-2に示す要素とともに、エンティティ・オブジェクトのXMLファイルに格納されます。
イベント・ポイントは、イベントを呼び出せる場所です。コミットが正常に行われた時点で、表4-3に示すいずれかのイベント・ポイントをトランザクションのエンティティごとにMediatorに呼び出せます。
表4-3 Mediatorに呼び出されたイベント・ポイントの例
DML型 | イベント名 | イベントの説明 |
---|---|---|
CREATE |
EntityCreated |
新しいエンティティが作成されました。 |
UPDATE |
EntityUpdated |
既存のエンティティが更新されました。 |
DELETE |
EntityDeleted |
既存のエンティティが削除されました。 |
イベントはデフォルトで呼び出されることはなく、すべてのイベントがカスタムです。イベントを作成する場合、名前とDML操作を適宜指定できます。
イベント・ポイントごとに、どのイベント定義を特定のイベント・ポイントで呼び出すかを指定する必要があります。つまり、各イベント定義をイベント・ポイントに宣言的にマッピングする必要があります。
イベント配信がトランザクションの一部であるトランザクション・イベント配信は、このフレームワークではサポートされていません。
サブスクライバがイベントの受信を確認するまで公開元がその後の処理を待機する同期化イベントは、このフレームワークではサポートされていません。
ビジネス・イベントを作成するには、概要エディタの「ビジネス・イベント」ページを使用します。
作業を始める前に、次のようにします。
ビジネス・イベントの動作方法に関する知識が役立つ場合があります。詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ビジネス・イベントを作成する手順は、次のとおりです。
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「ビジネス・イベント」ナビゲーション・タブをクリックします。
「ビジネス・イベント」ページで、「イベント定義」セクションを拡張し、「新規」アイコンをクリックします。
「ビジネス・イベント定義の作成」ダイアログで、EmployeeContactInfoChanged
など、このイベントを説明する名前を指定します。
ペイロード表で、「新規」および「削除」をクリックし、このイベントの適切な属性を選択します。
または、セルをダブルクリックして、適切な属性を選択します。
注意: エンティティ属性列には、サポートされているタイプの属性のみ表示されます。 |
「送信された値」フィールドで、値を「常に」送信するか、「変更された場合のみ」送信するか選択します。
「変更された場合のみ」オプションを選択すると、ペイロードでは属性がオプションとみなされるため、最高のパフォーマンスが得られます。デフォルトの「常に」のままにすると、値が変更されているかどうかを問わず、ペイロードは属性を必要とします。ペイロードの効率性の詳細は、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="Order.OrderId"/> <PayloadItem AttrName="LineItemId"/> <PayloadItem AttrName="ProductBase.ProductId" SendOnlyIfChanged="true"/> </Payload> </EventDef>
例4-15は、ビジネス・イベントのXSDイベント・スキーマの例を示しています。
例4-15 ビジネス・イベントのXSDイベント・スキーマ
<?xml version = '1.0' encoding = 'UTF-8'?> <xs:schema targetNamespace="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO" xmlns="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="CustBusEvent1Info"> <xs:complexType> <xs:sequence> <xs:element name="Order.OrderId" type="DecimalValuePair" minOccurs="1"/> <xs:element name="LineItemId" type="DecimalValuePair" minOccurs="1"/> <xs:element name="ProductBase.ProductId" type="DecimalValuePair" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="ValuePair" abstract="true"/> <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="/oracle/fodemo/storefront/entities/events/edl/OrderItemEO" xmlns:ns0="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO" xmlns="http://schemas.oracle.com/events/edl"> <schema-import namespace="/oracle/fodemo/storefront/entities/events/schema/OrderItemEO" location="OrderItemEO.xsd"/> <event-definition name="CustBusEvent1"> <content element="ns0:CustBusEvent1Info"/> </event-definition> </definitions>
ビジネス・イベントのペイロードは、関連付けられたエンティティ・オブジェクトの属性で構成されます。ビジネス・イベントのペイロード属性は、イベントの作成者によって定義されます。自動的に最適化されるわけではありません。イベントを定義するときに、属性を「常に」または「変更された場合のみ」送信するとしてマークできます。作成時に起動されるイベントでは、新しい値のみが送信されます。更新または削除時に起動されるイベントでは、新規および既存の値が送信されますが、「送信された値」設定に基づいて該当の属性のみです。最高のパフォーマンスを得るには、削除イベントでは主キー属性のみを含める必要があります。
コンポジット・シナリオ(発注書と明細項目など)をサポートするために、子エンティティは親エンティティで定義されたイベントを呼び出すことができ、子エンティティで定義されたイベントには親エンティティからの属性を含めることができます。子エンティティが親エンティティのイベントを呼び出す場合、子エンティティによるイベントの呼出し回数に関係なく、トランザクションごとに特定のトップレベル・エンティティについて1つのイベントのみが呼び出されます。
エンティティ・サブタイプのケースでは(たとえば、Staff
エンティティ・オブジェクトはPersons
エンティティのサブタイプです)、ADF Business Componentsではビジネス・イベントのオーバーライドはサポートされません。ビジネス・イベントのサブスクライバは、イベント名を使用してイベントをリスンするため、イベントをオーバーライドすると、イベントのサブスクライバは自分に対するものではないペイロード・データを受信してしまう可能性があります。したがって、この機能はサポートされていません。
ビジネス・イベントを定義する際は、ClobDomain
属性はサポートされていますが、非常に大きなCLOBデータはパフォーマンスに影響が与える可能性があることを考慮してください。
ビジネス・イベントを公開するには、エンティティ・オブジェクトの概要エディタの「ビジネス・イベント」ページを使用します。
作業を始める前に、次のようにします。
ビジネス・イベントがアプリケーションで使用される方法に関する知識が役立つ場合があります。詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
4.12.4項「ビジネス・イベントの作成方法」の説明に従って、イベント定義が公開前に作成されている必要があります。
ビジネス・イベントを公開する手順は、次のとおりです。
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクトをダブルクリックします。
概要エディタで、「ビジネス・イベント」ナビゲーション・タブをクリックします。
「ビジネス・イベント」ページで、「イベント公開」セクションを拡張し、「イベント公開の編集」アイコンをクリックします。
「イベント公開の編集」ダイアログで、「新規」をクリックし、新しいイベントを作成します。
イベント列で新しいセルをダブルクリックし、適切なイベントを選択します。
イベント・ポイント列で対応するセルをダブルクリックし、適切なイベント・アクション・ポイントを選択します。
オプションで、呼出し条件Raise Conditions表を使用して、イベントを呼び出す条件を定義できます。
「OK」をクリックします。
ビジネス・イベントを作成したら、そのイベントをサブスクライブし、応答することができます。
作業を始める前に、次のようにします。
ビジネス・イベントに関する知識が役立つ場合があります。詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを完了する必要があります。
4.12.7項「ビジネス・イベントを公開する方法」の説明に従ってビジネス・イベントを公開します。
ビジネス・イベントをサブスクライブするSCAプロジェクトを開きます。
ビジネス・イベントをサブスクライブする手順は、次のとおりです。
ファイル・システムを使用して、ビジネス・イベントのXSDおよびイベント定義ファイルをSCAプロジェクトのソース・パスにコピーします。
「アプリケーション・ナビゲータ」で、プロジェクトを右クリックして「新規」を選択します。
「新規ギャラリ」で、「SOA層」を展開し、「サービス・コンポーネント」を選択したら、「メディエータ」を選択し、「OK」をクリックします。
「メディエータの作成」ダイアログで、図4-15に示すように、「イベントのサブスクライブ」テンプレートを選択します。
「追加」アイコンをクリックして、イベントを追加します。
「イベント・チューザ」ダイアログで、「参照」アイコンをクリックしてイベントの定義ファイルにナビゲートして選択し、「OK」をクリックします。
「メディエータの作成」ダイアログでは、オプションで「一貫性」オプションを選択し、イベントの「フィルタ」を選択できます。
「OK」をクリックします。
結果として生成されたメディエータ(.mplan
ファイル)が概要エディタに表示されます。
「ルーティング・ルール」セクションの「追加」アイコンをクリックして、イベントへの対応方法のルールを追加できます。
この章でこれまで説明してきたように、エンティティ・オブジェクトのデータベース対話機能および多くの宣言的実行時機能はすべて、カスタムJavaコードを使用せずに実現できます。これらの宣言的機能を超えてエンティティにカスタム・ビジネス・ロジックを実装する必要がある場合、カスタム・コードを必要とするエンティティに対してJava生成を有効化する必要があります。通常、カスタム・エンティティ・オブジェクトおよびエンティティ定義クラスで記述、使用およびオーバーライドする一般的なコードの詳細は、付録D「ADF Business Componentsのよく使用されるメソッド」を参照してください。
エンティティ・オブジェクトのカスタムJavaクラスの生成を有効にするには、概要エディタの「Java」ページを使用します。
作業を始める前に、次のようにします。
カスタムJavaクラスに関する知識が役立つ場合があります。詳細は、4.13項「エンティティ・オブジェクトのカスタムJavaクラスの生成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ・オブジェクトのカスタムJavaクラスを生成する手順:
「アプリケーション・ナビゲータ」で、エンティティをダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックし、「Javaオプションの編集」アイコンをクリックします。
「Javaオプションの選択」ダイアログで、生成するJavaクラスのタイプを選択します。
エンティティ・オブジェクト・クラス: 最も頻繁にカスタマイズします。このクラスは、基礎となるデータベース表の各行を示します。
エンティティ・コレクション・クラス: カスタマイズすることはまれです。
エンティティ定義クラス: カスタマイズする頻度は低くなります。このクラスは、エンティティ行を管理し、その構造を定義する関連クラスを示します。
「OK」をクリックします。
生成するカスタムJavaクラスを1つ以上選択すると、指定したJavaファイルが作成されます。たとえば、fodemo.storefront.entities.OrderEO
という名前のエンティティ・オブジェクトの場合、そのカスタムJavaファイルのデフォルト名は、エンティティ・オブジェクト・クラスに対してはOrderEOImpl.java
、エンティティ定義クラスに対してはOrderEODefImpl.java
になります。これらのファイルは両方とも、コンポーネントのXMLコンポーネント定義ファイルと同じ./fodemo/storefront/entities
ディレクトリに作成されます。
エンティティ・オブジェクトのJava生成オプションは、概要エディタの「Java」ページに後でアクセスしてもそのまま反映されています。XML定義ファイルの場合と同様、このエディタでどのような変更を行っても、カスタムJavaクラスで生成されたコードは最新の状態に保たれます。後でなんらかの理由によりカスタムJavaファイルが必要なくなった場合は、「Java」ページで関連するオプションを無効にすると、カスタムJavaファイルを削除できます。
カスタム・エンティティ・オブジェクト・クラスの生成を有効にする場合、「アクセッサ」オプションも有効にすると、エンティティ・オブジェクトの属性ごとにgetterメソッドおよびsetterメソッドが生成されます。たとえば、OrderEO
エンティティ・オブジェクトには、対応するカスタム・クラスOrderEOImpl.java
があり、(例4-17に示すような)メソッドが生成されます。
例4-17 OrderEOImpl.javaのgetterメソッドとsetterメソッド
public DBSequence getOrderId() { ... } public void setOrderId(DBSequence value) { ... } public Date getOrderDate() { ... } public void setOrderDate(Date value) { ... } public String getOrderStatusCode() { ... } public void setOrderStatusCode(String value) { ... } public Number getCustomerId() { ... } public void setCustomerId(Number value) { ... } public String getShipToName() { ... } public void setShipToName(String value) { ... }
これらのメソッドを使用して行データを操作すると、コンパイル時にデータ型の使用方法が正しいかどうかがチェックされます。つまり、CustomerId
属性の値を取得するために次のようなコードを記述するかわりに、
Number customerId = (Number)order.getAttribute("CustomerId");
次のようなコードを記述できます。
Number customerId = order.getCustomerId();
後者の方法では、CustomerId
ではなく誤ってCustomerIdentifier
と入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error Number customerId = order.getCustomerCode();
エンティティ・オブジェクトのアクセッサ・メソッドが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both attribute name and type cast are wrong, but compiler cannot catch it String customerId = (String)order.getAttribute("CustomerCode");
これには、スペルが正しくない属性名とキャストの型が誤っている戻り値getAttribute()
が両方とも含まれています。EntityImpl
ベース・クラスが実装するRow
インタフェース上で汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
図4-16のように、カスタムJavaクラスの生成を有効にした場合、これらは、エンティティ・オブジェクトの「アプリケーション・ソース」ノードの下にも子ノードとして表示されます。すべてのADFコンポーネントと同様、アプリケーション・ナビゲータでエンティティ・オブジェクトを選択すると、構造ウィンドウには、エンティティの構造ビューが表示されます。カスタムJavaファイルのソース・コードを表示または操作する必要がある場合、ソース・エディタでこのファイルを開く方法には2通りあります。
図4-16のように、Javaファイルを右クリックし、「開く」を選択します。
構造ウィンドウでノードの項目を右クリックし、「ソースへ移動」を選択します。
JDeveloperで生成されるカスタムJavaクラスにより、エンティティ・オブジェクトのベース・クラスを拡張して、生成コードの整合性を維持しながらカスタム・コードを実装できる柔軟性があります。次の各項では、カスタムJavaクラスの詳細について説明しています。
XML専用エンティティ・オブジェクトを使用する場合、実行時には、その機能はデフォルトのADF Business Components実装クラスによって提供されます。生成される各カスタムJavaクラスによって適切なADF Business Componentsベース・クラスが拡張されるため、コードでは、デフォルトの動作を継承し、このクラスを簡単に追加またはカスタマイズできます。エンティティ・オブジェクト・クラスはEntityImpl
を拡張し、エンティティ定義クラスはEntityDefImpl
を拡張します(これらは両方ともoracle.jbo.server
パッケージ内にあります)。
開発者によっては、生成されたJavaソース・ファイルに独自のコードを追加することを躊躇する場合があります。JDeveloperによって作成および管理される各カスタムJavaソース・コード・ファイルには、独自のカスタム・コードをこのファイルに追加しても安全であることを示す、次のようなコメントがファイルの上部に記載されています。
// --------------------------------------------------------------------- // --- 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. // ---------------------------------------------------------------------
「編集」ダイアログで「OK」または「適用」ボタンをクリックしても、ファイルが知らぬ間に再生成されることはありません。かわりに、管理が必要なメソッドはスマートに更新され、独自のカスタム・コードはそのまま残されます。
ビュー・オブジェクトの実行時動作をカスタマイズする必要がある場合や、バインド変数またはビュー行属性へ強く型付けされたアクセスのみ行う場合、ビュー・オブジェクトのカスタムJavaクラスを生成できます。
ADF Business ComponentsのカスタムJava生成のデフォルト設定を構成するには、「ツール」メニューから「設定」を選択して、「ビジネス・コンポーネント」ページを開き、後で作成するビジネス・コンポーネントで使用する設定を設定します。ADF Business Componentsを初めて使用する開発者には、デフォルトではカスタムJavaクラスを生成しないよう設定することをお薦めします。カスタムJavaコードが必要な状況になった場合、そのコンポーネントに必要なカスタムJavaコードのみを有効にできます。経験を積むにつれ、どのようなデフォルト設定の組合せが最適であるかがわかるようになります。
エンティティ・オブジェクトは、XML専用のエンティティ・オブジェクト、またはカスタムJavaクラスと組み合せたXMLコンポーネント定義に基づいて機能するように設計されています。このどちらとして使用するかを選択できるように、属性値はエンティティのクラス(XML専用モードでは存在しないファイル)のプライベート・メンバー・フィールドには格納されません。かわりに、属性には、名前のみならず、エンティティのXMLコンポーネント定義ファイル内の<Attribute>
およびアソシエーション関連の<AccessorAttribute>
タグのゼロベースの順序に基づいて、このファイルの数値索引も割り当てられます。実行時には、エンティティ行の属性値は、エンティティの属性リスト内における属性の数値的位置による索引が付けられた状態で、EntityImpl
ベース・クラスによって管理される疎配列構造に格納されます。
多くの場合、このプライベート実装に関する詳細は、エンティティ・オブジェクトを使用する開発者は理解する必要がないため、重要ではありません。ただし、エンティティ・オブジェクトのカスタムJavaクラスを有効にする場合、エンティティ・オブジェクト・クラスで管理される生成コードの一部にこの実装の詳細が関わってきます。この場合、コードの使用目的を理解している方が賢明です。たとえば、OrderEO
エンティティ・オブジェクトのカスタムJavaクラスの場合、属性またはアクセッサ属性ごとに対応する生成済の整数定数があります。JDeveloperでは、これらの定数の値がXMLコンポーネント定義内の属性の順序を正しく反映しているかどうかが確認されます。
また、自動的に管理される、エンティティ・オブジェクト・クラスで強く型付けされたgetterメソッドおよびsetterメソッドでは、これらの属性定数は例4-18のように使用されます。
例4-18 カスタム・エンティティJavaクラスの属性定数を使用するgetterメソッドとsetterメソッド
// In oracle.fodemo.storefront.entities.OrderEOImpl class public Date getOrderDate() { return (Date)getAttributeInternal(ORDERDATE); // <-- Attribute enum } public void setOrderDate(Date value) { setAttributeInternal(ORDERDATE, value); // <-- Attribute enum }
エンティティ属性定数に関連する管理コードのもう1つの側面は、getAttrInvokeAccessor()
およびsetAttrInvokeAccessor()
メソッドです。これらのメソッドにより、数値索引を使用した属性アクセスのパフォーマンスが最適化され、汎用処理の実行時にEntityImpl
ベース・クラスの汎用コードが属性値にアクセスするときの通常のパフォーマンスと同じになります。getAttrInvokeAccessor()
メソッドの例を例4-19に示します。もう1つのsetAttrInvokeAccessor()
メソッドの場合も同様です。
例4-19 カスタム・エンティティJavaクラスのgetAttrInvokeAccessor()メソッド
// In oracle.fodemo.storefront.entities.OrderEOImpl class /** getAttrInvokeAccessor: generated method. Do not modify. */ 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コンポーネント定義の |
生成したカスタム・エンティティ・クラスを使用する場合とEntityImpl
汎用クラスを使用する場合の違いをよりわかりやすくするため、例4-20に、カスタム・アプリケーション・モジュール・クラス(StoreFrontService2Impl.java
)の、カスタム・エンティティ・クラス(StoreFrontServiceImpl.java
)のメソッドのバージョンを示します。重要な違いは、次のとおりです。
属性アクセスは、強く型付けされた属性アクセッサを使用して実行されます。
アソシエーションのアクセッサ属性により、アソシエーションの反対側にある強く型付けされたエンティティ・クラスが戻されます。
カスタム・エンティティ・クラスのgetDefinitionObject()
メソッドを使用すると、完全修飾されたエンティティ定義名を文字列として使用しないようにできます。
カスタム・エンティティ・クラスのcreatePrimaryKey()
メソッドにより、エンティティのKey
オブジェクトの作成を簡略化します。
例4-20 強く型付けされたカスタム・エンティティ・オブジェクト・クラスを使用したプログラム的なエンティティの例
package devguide.examples.appmodules; import oracle.fodemo.storefront.entities.OrderEOImpl; import oracle.fodemo.storefront.entities.PersonEOImpl; import oracle.fodemo.storefront.entities.ProductBaseEOImpl; import oracle.jbo.ApplicationModule; import oracle.jbo.JboException; import oracle.jbo.Key; import oracle.jbo.client.Configuration; import oracle.jbo.domain.DBSequence; import oracle.jbo.domain.Number; import oracle.jbo.server.ApplicationModuleImpl; import oracle.jbo.server.EntityDefImpl; // --------------------------------------------------------------------- // --- 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. // --------------------------------------------------------------------- /** * This custom application module class illustrates the same * example methods as StoreFrontServiceImpl.java, except that here * we're using the strongly typed custom Entity Java classes * OrderEOImpl, PersonsEOImpl, and ProductsBaseEOImpl instead of working * with all the entity objects using the base EntityImpl class. */ public class StoreFrontService2Impl extends ApplicationModuleImpl { /**This is the default constructor (do not remove). */ public StoreFrontService2Impl() { } /* * Helper method to return an Order by Id */ private OrderEOImpl retrieveOrderById(long orderId) { EntityDefImpl orderDef = OrderEOImpl.getDefinitionObject(); Key orderKey = OrderEOImpl.createPrimaryKey(new DBSequence(orderId)); return (OrderEOImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey); } /* * Find an Order by Id */ public String findOrderTotal(long orderId) { OrderEOImpl order = retrieveOrderById(orderId); if (order != null) { return order.getOrderTotal().toString(); } return null; } /* * Create a new Product and Return its new id */ public long createProduct(String name, String status, String shipCode) { EntityDefImpl productDef = ProductBaseEOImpl.getDefinitionObject(); ProductBaseEOImpl newProduct = (ProductBaseEOImpl)productDef.createInstance2(getDBTransaction(),null); newProduct.setProductName(name); newProduct.setProductStatus(status); newProduct.setShippingClassCode(shipCode); newProduct.setSupplierId(new Number(100)); newProduct.setListPrice(new Number(499)); newProduct.setMinPrice(new Number(479)); newProduct.setCreatedBy("Test Client"); newProduct.setLastUpdatedBy("Test Client"); newProduct.setCategoryId(new Number(5)); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } DBSequence newIdAssigned = newProduct.getProductId(); return newIdAssigned.getSequenceNumber().longValue(); } /* * Update the status of an existing order */ public void updateRequestStatus(long orderId, String newStatus) { OrderEOImpl order = retrieveOrderById(orderId); if (order != null) { order.setOrderStatusCode(newStatus); try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } } } /* * Access an associated Customer entity from the Order entity */ public String findOrderCustomer(long orderId) { OrderEOImpl svcReq = retrieveOrderById(orderId); if (svcReq != null) { PersonEOImpl cust = (PersonEOImpl)svcReq.getPerson(); if (cust != null) { return cust.getFirstName() + " " + cust.getLastName(); } else { return "Unassigned"; } } else { return null; } } /* * Testing method */ public static void main(String[] args) { String amDef = "devguide.model.StoreFrontService"; String config = "StoreFrontServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); /* * NOTE: This cast to use the StoreFrontServiceImpl class is OK since * this code is inside a business tier *Impl.java file and not in a * client class that is accessing the business tier from "outside". */ StoreFrontServiceImpl service = (StoreFrontServiceImpl)am; String total = service.findOrderTotal(1011); System.out.println("Status of Order # 1011 = " + total); String customerName = service.findOrderCustomer(1011); System.out.println("Customer for Order # 1011 = " + customerName); try { service.updateOrderStatus(1011,"CANCEL"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } long id = 0; try { id = service.createProduct(null, "NEW", "CLASS1"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } id = service.createProduct("Canon PowerShot G9", "NEW", "CLASS1"); System.out.println("New product created successfully with id = "+id); Configuration.releaseRootApplicationModule(am,true); } }
エンティティ・オブジェクトを直接操作する場合、UIベースのクライアントまたはプログラム的クライアントは常に必要ではありません。アプリケーション・モジュールにアクセスし、そのデータ・モデル内のビュー・オブジェクトを直接操作する際、外部クライアント・プログラム以外には使用する必要がない場合もあります。第5章「ビュー・オブジェクトを使用したSQL問合せの定義」では、強力なアプリケーションを構築するために、ビュー・オブジェクトの柔軟なSQL問合せ機能に、エンティティ・オブジェクトのビジネス・ロジックとデータベースとの自動対話機能とを組み合せる簡単な方法について説明します。この組合せにより、現在のエンド・ユーザーのタスク上のニーズに対応するよう設計され、再使用可能なドメイン・ビジネス・オブジェクト・レイヤー内で一元管理されたビジネス・ロジックを共有する、完全に更新可能なアプリケーション・モジュールのデータ・モデルを作成できます。
ただし、まず重要なのは、ビュー・オブジェクトとエンティティ・オブジェクトを組み合せた能力の利用方法について学習する前に、これらのオブジェクト自体の使用方法について理解することです。これらのオブジェクトについて詳細に学習することにより、これらを単体で使用する必要があるときと、独自のアプリケーションでこれらを組み合せる必要があるときについて理解が深まります。
クライアントはエンティティ・オブジェクトを直接操作できないため、エンティティ・オブジェクトをプログラム的に操作するために記述するコードは、通常カスタム・アプリケーション・モジュール・クラス内または別のエンティティ・オブジェクトのカスタム・クラス内のカスタム・コードになります。
エンティティ行にアクセスするには、エンティティ定義と呼ばれる関連オブジェクトを使用します。実行時には、エンティティ・オブジェクトごとに対応するエンティティ定義を利用して、エンティティの構造を記述し、それにしたがってエンティティ・オブジェクトのインスタンスを管理します。アプリケーション・モジュールを作成し、そのカスタムJavaクラスを有効化した後に、特定の注文を戻すメソッドを作成する必要があるとします。これは、例4-21
に示すretrieveOrderById()メソッドのようになります。
作業を始める前に、次のようにします。
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
主キーによるエンティティ・オブジェクトの検索手順:
エンティティ定義を検索します。
エンティティ・オブジェクト(OrderEO
など)のエンティティ定義を取得するには、EntityDefImpl
クラスからインポートされた静的なgetDefinitionObject()
メソッドにその完全修飾名を渡します。oracle.jbo.server
パッケージ内のEntityDefImpl
クラスにより、各エンティティ・オブジェクトのエンティティ定義が実装されます。
キーを作成します。
検索する主キー属性が含まれるKey
オブジェクトを作成します。たとえば、OrderEO
エンティティ・オブジェクトの場合、メソッドに引数として渡される単一のorderId
値が含まれるキーを作成します。
キーを使用してエンティティ・オブジェクトを検索します。
エンティティ定義のfindByPrimaryKey()
メソッドを使用して、キーによってエンティティ・オブジェクトを検索し、getDBTransaction()
メソッドを使用してアプリケーション・モジュールから取得できる現在のトランザクション・オブジェクトを渡します。エンティティ・オブジェクト行を示す具体的なクラスは、oracle.jbo.server.EntityImpl
クラスです。
オブジェクトまたはそのデータの一部をコール元に戻します。
例4-21は、この基本手順を使用して開発したretrieveOrderById()
メソッドのコードの例を示しています。
例4-21 キーによるOrderEOエンティティ・オブジェクトの取得
/* Helper method to return an Order by Id */ private OrderEOImpl retrieveOrderById(long orderId) { EntityDefImpl orderDef = OrderEOImpl.getDefinitionObject(); Key orderKey = OrderEOImpl.createPrimaryKey(new DBSequence(orderId)); return (OrderEOImpl)orderDef.findByPrimaryKey(getDBTransaction(),orderKey); }
注意:
|
SQLコードを必要としないアクセッサ属性に基づいて、関連付けられたエンティティへアクセスするためのメソッドを作成できます。たとえば、メソッドfindOrderCustomer()
によりオーダーを検索してから、オーダーに割り当てられた顧客を表す、関連付けられたPersonEO
エンティティ・オブジェクトにアクセスします。アソシエーションを使用して1つのエンティティ・オブジェクトから別のエンティティ・オブジェクトへ簡単にアクセスする方法の詳細は、4.3項「アソシエーションの作成および構成」を参照してください。
同じアクセッサ属性を使用して同じ関連付けられたエンティティを検索するアプリケーション・モジュールの既存メソッドとの競合を防ぐため、この機能をヘルパー・メソッドにリファクタし、必要に応じてアプリケーション・モジュールの任意の場所で再使用できます。たとえば、注文を検索する機能は、retrieveOrderById()
メソッド(例4-21を参照)によってリファクタされます。
作業を始める前に、次のようにします。
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
アクセッサ属性を使用して関連付けられたエンティティ・オブジェクトへのアクセス手順:
アクセッサ属性を使用して、関連付けられたエンティティを検索します。
たとえば、findOrderCustomer()
メソッドでは、retrieveOrderById()
ヘルパー・メソッドを使用して、IDによってOrderEO
エンティティ・オブジェクトを取得します。
アクセッサ属性を使用して関連付けられたエンティティにアクセスします。
属性のgetterメソッドを使用して、アソシエーション・アクセッサ名を渡し、関連の反対側にあるエンティティ・オブジェクトを取得できます(アソシエーション・アクセッサをより直感的な名前に変更する方法の詳細は、4.3.3項「エンティティ・アソシエーションのアクセッサ名の変更方法」を参照してください)。
データの一部をコール元に戻します。
たとえば、findOrderCustomer()
メソッドでは、戻されたPersonEO
エンティティに対してgetterメソッドを使用して、名前と姓を連結することにより、割り当てられた顧客の名前を戻します。
関連するPersonEO
エンティティにアクセスするためにSQLを記述する必要はありません。OrderEO
エンティティ・オブジェクトとPersonEO
エンティティ・オブジェクト間のADFアソシエーションで取得された関連情報のみで、データ・ナビゲーションの一般的なタスクを自動化できます。
例4-22に、ヘルパー・メソッドを使用するfindOrderCustomer()
のコードを示します。
例4-22 アクセッサ属性を使用した関連付けられたエンティティへのアクセス
/* Access an associated Customer entity from the Order entity */ public String findOrderCustomer(long orderId) { //1. Find the OrderEO object OrderEOImpl order = retrieveOrderById(orderId); if (order != null) { //2. Access the PersonEO object using the association accessor attribute PersonEOImpl cust = (PersonEOImpl)order.getPerson(); if (cust != null) { //3. Return attribute values from the associated entity object return cust.getFirstName() + " " + cust.getLastName(); } else { return "Unassigned"; } } else { return null; } }
エンティティ行を取得した後、エンティティ行は簡単に更新または削除できます。この処理は、例4-23
に示すupdateOrderStatus()のようなメソッドを追加することにより実行できます。
作業を始める前に、次のようにします。
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ行の更新手順:
IDによってOrderを検索します。
updateOrderStatus()
メソッドでは、retrieveOrderById()
ヘルパー・メソッドを使用して、IDによってOrderEO
エンティティ・オブジェクトを取得します。
1つ以上の属性を新しい値に設定します。
updateOrderStatus()
メソッドでは、EntityImpl
クラスのsetAttribute()
メソッドを使用して、Status
属性の値を渡された新しい値に更新します。
トランザクションをコミットします。
updateOrderStatus()
メソッドでは、アプリケーション・モジュールのgetDBTransaction()
メソッドを使用して、現在のトランザクション・オブジェクトにアクセスし、commit()
メソッドをコールしてトランザクションをコミットします。
例4-23 既存のエンティティ行の更新
/* Update the status of an existing order */ public void updateOrderStatus(long orderId, String newStatus) { //1. Find the order OrderEOImpl order = retrieveOrderById(orderId); if (order != null) { //2. Set its Status attribute to a new value order.setOrderStatusCode(newStatus); //3. Commit the transaction try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } } }
エンティティ行の削除の例もこれと同じですが、既存のエンティティを検索した後、トランザクションをコミットする前に、かわりに次の行を使用してエンティティを削除する点が異なります。
// Remove the entity instead! order.remove();
エンティティ定義を使用して既存のエンティティ行を検索する以外にも、エンティティ定義を使用して新しいエンティティ行を作成することもできます。製品エンティティの場合、例4-24
のようなcreateProduct()メソッドを記述することにより、新しい製品の名前や説明を取得し、この製品に割り当てられた新しい製品IDを戻すことができます。この例は、ProductBaseEO
エンティティ・オブジェクトのProductId
属性がすでに更新され、その型がDBSequence
型になっていること(4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」を参照)が前提となります。この設定により、アプリケーション・スキーマの表の順序から割り当てられたトリガーの値が、対応するデータベース表から反映されるように属性値がリフレッシュされます。
作業を始める前に、次のようにします。
エンティティ・オブジェクトとアソシエーションで動作するためにプログラム的な方法を使用するタイミングに関する知識が役立つ場合があります。詳細は、4.14項「エンティティ・オブジェクトおよびアソシエーションのプログラム的操作」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
エンティティ行の作成手順:
エンティティ定義を検索します。
createProduct()
メソッドでは、getDefinitionObject()
メソッドを使用して、Product
エンティティのエンティティ定義を検索します。
新しいインスタンスを作成します。
createProduct()
メソッドでは、エンティティ定義に対してcreateInstance2()
メソッドを使用して、エンティティ・オブジェクトの新しいインスタンスを作成します。
注意: このメソッド名の末尾には |
属性値を設定します。
createProduct()
メソッドでは、エンティティ・オブジェクトに対して属性のsetterメソッドを使用して、新しいエンティティ行にName
やStatus
などの属性の値を割り当てます。
トランザクションをコミットします。
createProduct()
メソッドでは、現在のトランザクション・オブジェクトに対してcommit()
をコールし、トランザクションをコミットします。
トリガーによって割り当てられた製品IDをコール元に戻します。
createProduct()
メソッドでは、属性のgetterメソッドを使用してProductId
属性の値をDBSequence
として取得し、さらにgetSequenceNumber().longValue()
をコールして、順序番号をlong
値としてコール元に戻します。
例4-24 エンティティ行の新規作成
/* Create a new Product and Return its new id */ public long createProduct(String name, String status, String shipCode) { //1. Find the entity definition for the Product entity EntityDefImpl productDef = ProductBaseEOImpl.getDefinitionObject(); //2. Create a new instance of a Product entity ProductBaseEOImpl newProduct = (ProductBaseEOImpl)productDef.createInstance2(getDBTransaction(),null); //3. Set attribute values newProduct.setProductName(name); newProduct.setProductStatus(status); newProduct.setShippingClassCode(shipCode); newProduct.setSupplierId(new Number(100)); newProduct.setListPrice(new Number(499)); newProduct.setMinPrice(new Number(479)); newProduct.setCreatedBy("Test Client"); newProduct.setLastUpdatedBy("Test Client"); newProduct.setCategoryId(new Number(5)); //4. Commit the transaction try { getDBTransaction().commit(); } catch (JboException ex) { getDBTransaction().rollback(); throw ex; } //5. Access the database-trigger-assigned ProductId value and return it DBSequence newIdAssigned = newProduct.getProductId(); 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操作を実行して複数の関連するエンティティ・オブジェクトに変更を保存するときは、操作の実行順序が重要になる場合があります。外部キー参照を含む新しい行を、参照されている行を挿入する前に挿入しようとすると、データベースで制約違反が発生する可能性があります。コミット時のエンティティ・オブジェクトのデフォルトの処理順序、および必要に応じてその順序をプログラムで変更する方法を理解している必要があります。
注意: この項の例では、Fusion Order Demoアプリケーションの |
デフォルトでは、トランザクションをコミットすると、保留中の変更リスト内のエンティティ・オブジェクトは、時間順、つまりエンティティがリストに追加された順序で処理されます。たとえば、新しいProduct
を作成し、次にその製品に関連する新しいSupplier
を作成すると、新しいProduct
が先に挿入され、その後で新しいSupplier
が挿入されます。
2つのエンティティ・オブジェクトがコンポジットで関連付けられていると、厳密な時間順序が自動的に変更されて、コンポジット関係にある親と子のエンティティ行が制約に違反しない順序で保存されることが保証されます。たとえば、コンポジットされる新しい子のエンティティ行より前に、新しい親のエンティティ行が挿入されます。
関係のあるエンティティが関連付けられてはいてもコンポジットではない場合は、コードを少し作成して、関連するエンティティが正しい順序で保存されるようにする必要があります。
例4-28のPostModule
アプリケーション・モジュールのnewProductForNewSupplier()
カスタム・メソッドについて考えます。このメソッドは、一連のパラメータを受け取って、次の処理を行います。
新しいProduct
を作成します。
新しいSupplier
を作成します。
サービス・リクエストが関係する製品IDを設定します。
トランザクションをコミットします。
Result
Java Beanを作成し、新しい製品IDとサプライヤIDを保持します。
結果を返します。
注意: このコードでは、 |
例4-28 新しいProductとSupplierの作成および新しいIDの戻り
// In PostModuleImpl.java public Result newProductForNewSupplier(String supplierName, String supplierStatus, String productName, String productStatus, Number listPrice, Number minPrice, String shipCode) { oracle.jbo.domain.Date today = new Date(Date.getCurrentDate()); Number objectId = new Number(0); // 1. Create a new product ProductsBaseImpl newProduct = createNewProduct(); // 2. Create a new supplier SuppliersImpl newSupplier = createNewSupplier(); newSupplier.setSupplierName(supplierName); newSupplier.setSupplierStatus(supplierStatus); newSupplier.setCreatedBy("PostingModule"); newSupplier.setCreationDate(today); newSupplier.setLastUpdatedBy("PostingModule"); newSupplier.setLastUpdateDate(today); newSupplier.setObjectVersionId(objectId); // 3. Set the supplier id to which the product pertains newProduct.setSupplierId(newSupplier.getSupplierId().getSequenceNumber()); newProduct.setProductName(productName); newProduct.setProductStatus(productStatus); newProduct.setListPrice(listPrice); newProduct.setMinPrice(minPrice); newProduct.setShippingClassCode(shipCode); newProduct.setCreatedBy("PostingModule"); newProduct.setCreationDate(today); newProduct.setLastUpdatedBy("PostingModule"); newProduct.setLastUpdateDate(today); newProduct.setObjectVersionId(objectId); // 4. Commit the transaction getDBTransaction().commit(); // 5. Construct a bean to hold new supplier id and product id Result result = new Result(); result.setProductId(newProduct.getProductId().getSequenceNumber()); result.setSupplierId(newSupplier.getSupplierId().getSequenceNumber()); // 6. Return the result return result; } private ProductsBaseImpl createNewProduct(){ EntityDefImpl productDef = ProductsBaseImpl.getDefinitionObject(); return (ProductsBaseImpl) productDef.createInstance2(getDBTransaction(), null); }private SuppliersImpl createNewSupplier(){ EntityDefImpl supplierDef = SuppliersImpl.getDefinitionObject(); return (SuppliersImpl) supplierDef.createInstance2(getDBTransaction(), null); }
このメソッドをアプリケーション・モジュールのクライアント・インタフェースに追加し、テスト用のクライアント・プログラムからテストすると、エラーが発生します。
oracle.jbo.DMLConstraintException: JBO-26048: Constraint "PRODUCT_SUPPLIER_FK" violated during post operation: "Insert" using SQL Statement "BEGIN INSERT INTO PRODUCTS( SUPPLIER_NAME,SUPPLIER_STATUS,PRODUCT_NAME, PRODUCT_STATUS,LIST_PRICE,MIN_PRICE, SHIPPING_CLASS_CODE) VALUES (?,?,?,?,?,?,?) RETURNING PRODUCT_ID INTO ?; END;". ## Detail 0 ## java.sql.SQLException: ORA-02291: integrity constraint (FOD.PRODUCT_SUPPILER_FK) violated - parent key not found
このエラーの内容は、PRODUCTS
行を挿入しようとしたが、その外部キーSUPPLIER_ID
の値に対応する行がSUPPLIERS
表に存在しないというものです。このエラーは次のような理由で発生します。
コードがSupplier
より前にProduct
を作成した。
エンティティ・オブジェクトProducts
とSuppliers
が、関連付けられてはいるがコンポジットではない。
新しいエンティティ行を保存するDML操作が時間順に行われるため、新しいSupplier
より前に新しいProduct
が挿入される。
まだ有効でないサプライヤIDを持つ製品を追加しようとする問題は、例のコード行の順序を変更し、Supplier
を先に作成してからProduct
を作成すると解決できます。目先の問題にはこれで対処できますが、別のアプリケーション開発者が間違った順序でエンティティを作成する可能性が残っています。
さらによい解決策は、エンティティ・オブジェクト自体がポスト順序を処理するようにして、作成順序に関係なくポストが正しく動作するようにすることです。そのためには、関連するエンティティ・オブジェクトを参照する外部キー属性を含むエンティティのpostChanges()
メソッドをオーバーライドし、例4-29に示すようなコードを記述します。この例では、Supplier
エンティティに対する外部キーが含まれているのはProduct
であるため、Product
を更新し、条件によっては、サービス・リクエストをポストする前に関連する新しいSupplier
を強制的にポストするようにします。
このコードでは、ポストしているエンティティがSTATUS_NEW
状態またはSTATUS_MODIFIED
状態のいずれかであるかを検査します。その場合は、getSupplier()
アソシエーション・アクセッサを使用して、関連する製品を取得します。関連するSupplier
のポスト状態もSTATUS_NEW
である場合は、super.postChanges()
を呼び出して自分のDMLを実行する前に、まず関連する親行のpostChanges()
を呼び出します。
例4-29 Supplierを先にポストするためのProductsBaseImplのpostChanges()のオーバーライド
// In ProductsBaseImpl.java public void postChanges(TransactionEvent e) { /* If current entity is new or modified */ if (getPostState() == STATUS_NEW || getPostState() == STATUS_MODIFIED) { /* Get the associated supplier for the product */ SuppliersImpl supplier = getSupplier(); /* If there is an associated supplier */ if (supplier != null) { /* And if its post-status is NEW */ if (supplier.getPostState() == STATUS_NEW) { /* * Post the supplier first, before posting this * entity by calling super below */ supplier.postChanges(e); } } } super.postChanges(e); }
この状態で例を再び実行すると、newProductForNewSupplier()
メソッドのコードの作成順序を変更しなくても、エンティティは、新しいSupplier
が先で新しいProduct
が後という正しい順序でポストされます。しかし、まだ問題があります。まだ制約違反が発生しますが、今度は理由が異なります。
Suppliers
エンティティ・オブジェクトの主キーがユーザーによって割り当てられている場合は、例4-29のコードのみで、ポスト順序を修正して制約違反に対処できます。
注意: このプログラム的な手法(Java EEアプリケーション・レイヤーで問題を解決する)のかわりに、データベース・レイヤーで遅延可能制約を使用する方法もあります。データベース・スキーマを制御できる場合は、外部キー制約を |
しかし、Suppliers.SupplierId
はデータベース順序から割り当てられ、ユーザー割当てではありません。そのため、新しいSuppliers
エンティティ行がポストされる時点で、データベースが割り当てる順序値を反映するように、SupplierId
属性が更新されます。新しいサプライヤを参照するProducts.SupplierId
属性の外部キー値は、サプライヤID値のこの更新により親を失います。製品行が保存されるときには、SUPPLIER_ID
の値と一致する行がSUPPLIERS
表にまだ存在しないため、制約違反が再び発生します。次の2つの項では、このような親を失うという問題に対処するための解決策について説明します。
4.10.10項「トリガーによってデータベース順序から割り当てられた主キー値の取得方法」で説明されているように、エンティティ・オブジェクトの主キー属性がDBSequence
型の場合は、そのオブジェクトが作成されるトランザクションの間、キーの数値は一時的に一意の負の値になります。同じトランザクション内で関連するエンティティの値を作成すると、これらの間の関連は、この一時的な負のキー値に基づくことになります。DBSequence
の値が主キーであるエンティティ・オブジェクトをポストすると、その主キーはデータベースが割り当てる正しい順序番号を反映して更新されますが、それに関連付けられたエンティティは一時的な負の外部キー値を保持したままで親を失います。
コンポジットに基づくエンティティ・オブジェクトの場合は、親エンティティ・オブジェクトのDBSequence
値が設定されている主キーが更新されると、コンポジットされる子エンティティ行の一時的な負の外部キー値は、親の更新されたデータベース割当ての主キーを反映して、自動的に更新されます。つまり、コンポジット・エンティティの場合、親を失うという問題は発生しません。
ただし、エンティティ・オブジェクトがコンポジットではないアソシエーションによって関連付けられている場合は、コードを作成して、一時的に負の値を参照する関連エンティティ行を、更新されたデータベース割当ての主キー値で更新する必要があります。次の項では、必要なコードについて説明します。
この例のSuppliers
のようなエンティティがDBSequence
値の主キーを持ち、それと関連付けられた(ただしコンポジットではない)他のエンティティによって外部キーとして参照される場合は、例4-30
に示すように、この新しいSuppliers行を参照する可能性のあるエンティティ行の行セットに対する参照を保存するように、postChanges()
メソッドをオーバーライドする必要があります。現在のSupplier
行のステータスがNew
の場合は、getProduct()
アソシエーション・アクセッサから返るRowSet
の値を、super.postChanges()
を呼び出す前に、newProductsBeforePost
メンバー・フィールドに代入します。
例4-30 この新しいSupplierを参照するエンティティ行に対する参照の保存
// In SuppliersImpl.java RowSet newProductsBeforePost = null; public void postChanges(TransactionEvent TransactionEvent) { /* Only bother to update references if Product is a NEW one */ if (getPostState() == STATUS_NEW) { /* * Get a rowset of products related * to this new supplier before calling super */ newProductsBeforePost = (RowSet)getProductsBase(); } super.postChanges(TransactionEvent); }
この保存したRowSet
オブジェクトは、後で例4-31で示されているオーバーライドされたrefreshFKInNewContainees()
メソッドで使用します。新しいエンティティ行は、このメソッドを呼び出すことで、postChanges()
を呼び出す前にそれを参照していた他のエンティティ行を更新された主キー値でカスケード更新できます。newProductsBaseBeforePost
行セット(NULLでない場合)のProductsBaseImpl
行を反復処理し、それぞれのサプライヤID値に、新しくポストされたSupplier
エンティティの、順序から割り当てられた新しいサプライヤ値を設定します。
例4-31 新しいSupplierId値のエンティティ行のカスケード更新
// In SuppliersImpl.java protected void refreshFKInNewContainees() { if (newProductsBeforePost != null) { Number newSupplierId = getSupplierId().getSequenceNumber(); /* * Process the rowset of products that referenced * the new supplier prior to posting, and update their * SupplierId attribute to reflect the refreshed SupplierId value * that was assigned by a database sequence during posting. */ while (newProductsBeforePost.hasNext()) { ProductsBaseImpl svrReq = (ProductsBaseImpl)newProductsBeforePost.next(); product.setSupplierId(newSupplierId); } 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により含めることができます。これにより、エンティティ・オブジェクトが関連するビュー行のセットにアクセスするための便利な方法が提供されます。特に、行を生成するための問合せが現在の行の属性にのみ依存する場合に有効です。
デフォルトでは、エンティティ・アソシエーション・アクセッサの行セットを取得するたびに、エンティティ・オブジェクトは新しいRowSet
オブジェクトを作成し、ユーザーが行を操作できるようにします。これは、そのたびに問合せを再実行して結果を生成するという意味ではなく、RowSet
オブジェクトの新しいインスタンスを作成してデフォルトのイテレータを先頭行の前のスロットにリセットするだけです。行セットをデータベースの行で強制的に更新するには、executeQuery()
メソッドを呼び出します。
行セットの作成には若干のオーバーヘッドが伴うため、同じアソシエーション・アクセッサ属性に対して大量の呼出しを行うコードの場合は、アソシエーションのリンク元エンティティ・オブジェクトに対してアソシエーション・アクセッサ行セットの保持を有効にすることを検討する余地があります。アソシエーション・アクセッサのソースであるエンティティ・オブジェクトについて、概要エディタを使用してアソシエーション・アクセッサ行セットの保存を有効にできます。エンティティ・オブジェクトに対して、概要エディタの「一般」ページの「チューニング」セクションで、「アソシエーション・アクセッサ行セットの保存」を選択します。
エンティティ・オブジェクトに対してカスタムJavaエンティティ・コレクション・クラスを有効にすることもできます。他のカスタム・エンティティJavaクラスと同様に、この設定は、エンティティ・オブジェクトの概要エディタの「Java」ページで開く「Javaオプションの選択」ダイアログを選択して実行します。ダイアログでは、「エンティティ・コレクション・クラスの生成」を選択します。次に、JDeveloperによって自動的に作成されるYourEntityColl
Impl
クラスで、init()
メソッドをオーバーライドし、setAssociationAccessorRetained()
メソッドを呼び出してパラメータとしてtrue
を渡す行を、super.init()
の後に追加します。これは、そのエンティティ・オブジェクトのすべてのアソシエーション・アクセッサ属性に適用されます。
エンティティ・オブジェクトに対してこの機能を有効にすると、アソシエーション・アクセッサの行セットが毎回再作成されないため、デフォルトの行セット・イテレータの現在行も維持されるという副作用があります。つまり、デフォルト行セット・イテレータの現在行をリセットして先頭行の前のスロットに戻すには、アソシエーション・アクセッサから取得した行セットに対して、reset()
メソッドを明示的に呼び出す必要があります。
ただし、アクセッサの保持を有効にすると、アクセッサ行セットの行を反復処理する前にいつでもreset()
を呼び出すようにしないと、微妙で検出が困難なエラーがアプリケーションで発生する可能性があることに注意してください。たとえば、アソシエーション・アクセッサ行セットの行を例4-32に示すように反復処理し、合計を計算するものとします。
例4-32 行セットの誤った反復
// In your custom Java entity classRowSet rs = (RowSet)getProducts(); while (rs.hasNext()) { ProductImpl r = (ProductImpl)rs.next(); // Do something important with attributes in each row }
アクセッサ行セットを最初に処理するときには、このコードは正しく動作します。しかし、行セット(およびそのデフォルトの行セット・イテレータ)が保持されるため、2回目以降の行セットへのアクセスでは、現在行はすでに行セットの末尾にあり、rs.hasNext()
がfalse
になるためwhileループはスキップされます。この機能を有効にする場合は、アクセッサの反復コードは例4-33に示すように記述する必要があります。
複数のエンティティ・オブジェクトで似た属性値に対して同じ妥当性検査を繰り返し行っている場合は、この検証をカプセル化した独自のデータ型を作成することで、時間と労力を節約できます。たとえば、ビジネス・ドメイン・レイヤーに、電子メール・アドレスを表す文字列を格納する大量のエンティティ・オブジェクト属性がある場合を考えてみてください。ビジネス・ドメイン・レイヤーのどこでもエンド・ユーザーが有効な電子メール・アドレスを入力できるように保証する方法としては、次のようなものがあります。
各属性に対して基本のString
データ型を使用
Javaコードを使用して属性レベルのメソッド・バリデータを追加し、属性ごとにString値の形式が有効な電子メール・アドレスであることを確認
しかし、このような方法は、大規模なアプリケーションでは急速に冗漫になります。ADF Business Componentsで提供されている代替手段を使用すると、電子メール・アドレスを表す独自のEmailAddress
データ型を作成できます。電子メール・アドレスの値に関するすべての妥当性検査をこの新しいカスタム・データ型にまとめた後は、アプリケーション内にある電子メール・アドレスを表すすべての属性の型として、EmailAddress
を使用できます。このようにすると、属性値の意図が他の開発者に対していっそう明確になり、検証処理が1か所にまとまるためアプリケーションのメンテナンスが簡単になります。ADF Business Componentsでは、これを開発者作成のデータ型ドメインと呼びます。
ドメインとは、String
、Number
、Date
などの基本データ型を拡張するJavaクラスであり、関連する妥当性検査に候補値が合格することを保証するコンストラクタ時の検証が追加されています。基本データ型検証、書式設定、カスタム・メタデータ・プロパティなどの横断的動作でカスタム・データ型を定義する手段を、ドメインを属性のJava型として使用する任意のエンティティ・オブジェクトまたはビュー・オブジェクトによって継承される方法で提供します。
注意: この項の例では、Fusion Order Demoアプリケーションの |
ドメインを作成するには、ドメイン作成ウィザードを使用します。このウィザードは、「ADF Business Components」カテゴリの「新規ギャラリ」で使用できます。
作業を始める前に、次のようにします。
ドメインの使用に関する知識があると役立ちます。詳細は、4.15項「ドメインを使用したカスタム検証済データ型の作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
ドメインを作成する手順:
アプリケーション・ナビゲータで、ドメインを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから、「ドメイン」を選択し、「OK」をクリックします。
ドメイン作成ウィザードの「名前」ページで、ドメインの名前とドメインが存在するパッケージを指定します。単純なJava型に基づくドメインを作成する場合は、「Oracleオブジェクト型のドメイン」を選択しないでおきます。
「次へ」をクリックします。
「設定」ページで、ドメインのベース型と、マップするデータベース列の型を指定します。
たとえば、8文字の短い電子メール・アドレスを保持するShortEmailAddress
という名前のドメインを作成する場合は、ベース型にString
を設定し、「データベース列の型」にVARCHAR2(8)
を設定します。このパネルでは、他の共通属性も設定できます。
「終了」をクリックしてドメインを作成します。
ドメインを作成すると、プロジェクトのソース・パスのサブディレクトリに、選択したパッケージ名に対応するXMLコンポーネント定義が作成されます。たとえば、devguide.advanced.domains
パッケージにShortEmailAddress
ドメインを作成すると、./devguide/advanced/domains
サブディレクトリにShortEmailAddress.xml
ファイルが作成されます。ドメインには常に対応するJavaクラスがあり、ドメインが存在するパッケージのcommon
サブパッケージに作成されます。つまり、devguide.advanced.domains.common
パッケージにShortEmailAddress.java
クラスが作成されます。ドメインのJavaクラスは、組込みデータ型と同じように動作する適切なコードを備えて自動的に生成されます。
ドメインは様々に異なる型として作成でき、標準の属性ではなく異なる特性を持ちます。ここでは、ドメインを使用する際に理解しておく必要のある事項について説明します。
プロジェクトでドメインを作成すると、エンティティ・オブジェクトおよびビュー・オブジェクトのウィザードとダイアログで「属性」の「型」ドロップダウン・リストに、使用可能なデータ型の1つとして表示されます。特定の属性の型としてドメインを使用するには、リストから選択します。
注意: エンティティ・ベースのビュー・オブジェクトのエンティティにマップされた属性は、対応する基礎となるエンティティ・オブジェクト属性からデータ型を継承します。したがって、エンティティ属性でドメイン・タイプを使用する場合、一致するビュー・オブジェクト属性も同様になります。ビュー・オブジェクトの一時属性またはSQL導出属性の場合は、基礎のエンティティから継承しないため、ドメインを使用するように型を直接設定できます。 |
通常、ドメインについてユーザーが行う必要のある唯一のコーディング作業は、生成されるvalidate()
メソッドの内部のカスタム・コードを記述することです。validate()
メソッドの実装では、設定している候補値の妥当性検査を行い、検証が失敗した場合はoracle.jbo
パッケージでDataCreationException
をスローします。
翻訳可能な例外メッセージをスローするには、例4-34で示されているようなメッセージ・バンドル・クラスを作成できます。このクラスは、ドメイン・クラス自体と同じcommon
パッケージに作成します。メッセージ・バンドルは、{
MessageKeyString
、
TranslatableMessageString
}
というペアの配列を返します。
例4-34 ドメイン例外メッセージのカスタム・メッセージ・バンドル・クラス
package devguide.advanced.simpledomains.common; 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-35は、簡単なShortEmailAddress
ドメイン・クラスのvalidate()
メソッドを示しています。このメソッドは、mData
値にアットマークまたはドットが含まれないていないことを確認し、含まれている場合は、翻訳可能なエラー・メッセージの適切なメッセージ・バンドルとメッセージ・キーを参照するDataCreationException
をスローします。
例4-35カスタム検証を含む簡単なShortEmailAddress文字列ベース・ドメイン型
public class ShortEmailAddress extends Object implements DomainInterface, Serializable { private String mData; // . . . /**Implements domain validation logic and throws a JboException on error. */ protected void validate() { 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-36に示すように、ベース型を拡張します。この例では、偶数を表すEvenNumber
という名前のNumberベースのドメインに対するvalidate()
メソッドが示されています。
例4-36カスタム検証を含む簡単なEvenNumber Numberベース・ドメイン型
public class EvenNumber extends Number { // . . . /** * Validates that value is an even number, or else * 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データベースでは、データベースでユーザー定義型を作成できます。たとえば、次のDDL文を使用してPOINT_TYPE
という名前の型を作成できます。
create type point_type as object ( x_coord number, y_coord number );
POINT_TYPE
のようなユーザー定義型を使用する場合は、その型に基づくドメインを作成したり、オブジェクト型の列を含む表をリバース・エンジニアリングしてドメインを自動的に作成したりできます。ドメイン作成ウィザードを使用して、Oracleオブジェクト型のドメインを手動で作成できます。
Oracleオブジェクト型のドメインを手動で作成する手順:
アプリケーション・ナビゲータで、ドメインを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから、「ドメイン」を選択し、「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バインディング・レイヤーでのオブジェクト型ドメインの処理のサポートは完全であるため、宣言的にデータバインドされたユーザー・インタフェースでオブジェクト・ドメイン値属性を使用することは容易ではありません。 |
アプリケーション・ナビゲータでドメインを選択した後、次のいずれかの方法で実装クラスに素早く移動できます。
「アプリケーション・ナビゲータ」でドメインを右クリックして、「ドメイン・クラスに移動」を選択します。
構造ウィンドウで、ドメイン・クラスをダブルクリックします。
38.3.1項「コンポーネントをADFライブラリJARにパッケージ化する方法」で説明されているように、ビジネス・コンポーネント・アーカイブを作成すると、プロジェクトのソース・パスの*.commonサブディレクトリにあるドメイン・クラスとメッセージ・バンドル・ファイルは、*CSCommon.jar
にパッケージ化されます。これらは、サポートする必要のある中間層アプリケーション・サーバーと最終的なリモート・クライアントの両方に共通のクラスです。
ドメインではカスタム・メタデータ・プロパティを定義できます。そのようなドメインに基づくエンティティ・オブジェクトまたはビュー・オブジェクトの属性は、属性自体で定義されている場合と同じように、これらのカスタム・プロパティを継承します。エンティティ・オブジェクトまたはビュー・オブジェクトの属性で同じカスタム・プロパティが定義されている場合は、その設定が、ドメインから継承する値より優先されます。
JDeveloperでは、ドメイン定義レベルで適用されている宣言的な設定を施行します。この設定は、ドメイン型に基づく属性のエンティティ・オブジェクトまたはビュー・オブジェクトでは制限を緩和できません。たとえば、ドメインの「更新可能」プロパティを「新規の間」に設定した場合、そのドメインをエンティティ・オブジェクト属性のJava型として使用するときに、「更新可能」を「なし」(より厳しい制限)に設定することはできますが、「常に」に設定することはできません。同様に、ドメインを「永続的」に定義した場合、後でそれを一時的なものにすることはできません。アプリケーションで使用するときは、ドメインの宣言的プロパティはできるかぎり緩く設定し、後で必要に応じて厳しくできるようにします。
履歴タイプは、ある時点に固有のデータの追跡に使用されます。JDeveloperにはいくつかの履歴タイプが付属していますが、独自のものも作成できます。標準の履歴タイプとその使用方法の詳細は、4.10.12項「履歴列を使用して作成および変更した日付を追跡する方法」を参照してください。
付属の履歴タイプに限定されることなく、「設定」ダイアログの「履歴タイプ」ページを使用してカスタム履歴タイプを追加または削除してから、カスタムJavaコードを記述して目的の動作を実装できます。カスタム履歴タイプを処理するコードは、再利用できるようアプリケーション全体にわたるエンティティ・ベース・クラスで記述します。
図4-17は、タイプIDが11
のlast update login
カスタム・タイプです。last_update_login
がFND_LOGINS
表の外部キーであると仮定します。
作業を始める前に、次のようにします。
履歴タイプに関する知識が役立つ場合があります。詳細は、4.16項「新規履歴タイプの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
カスタム履歴列タイプを作成する手順:
「ツール」メニューで「設定」を選択します。
「設定」ダイアログで、「ADF Business Components」を展開し、「履歴タイプ」をクリックします。
「履歴タイプ」ページで「新規」をクリックします。
「新規履歴タイプ」ダイアログで、名前(空白は使用可)の文字列値および数値IDを入力します。
「タイプID」は11~126の整数である必要があります。0~10の数値は内部用に予約されています。表示文字列は、次に概要エディタを使用する際、「変更履歴のトラッキング」ドロップダウン・リストに表示されます。図4-18は、「設定」ダイアログでの新規履歴タイプを示します。
EntityImpl.java
ファイルを開いて、例4-37のような定義を追加します。
例4-38のようなコードで、EntityImpl
ベース・クラスのgetHistoryContextForAttribute(AttributeDefImpl attr)
メソッドをオーバーライドします。
履歴タイプは通常、アプリケーションの存続期間中は値の監査に使用されるため、削除する必要はありません。ただし、削除する必要がある場合は、次の作業を実行します。
「設定」ダイアログで、JDeveloper履歴タイプ・リストから目的の履歴タイプを削除します。
ベースのEntityImpl.getHistoryContextForAttribute
メソッドの履歴タイプをサポートするため実装したカスタム・コードがあれば削除します。
エンティティ属性メタデータの履歴タイプのすべての慣用名を削除します。この履歴タイプを使用するように定義した属性があれば、編集する必要があります。
作業を始める前に、次のようにします。
履歴タイプに関する知識が役立つ場合があります。詳細は、4.16項「新規履歴タイプの作成」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
JDeveloper履歴タイプ・リストから目的の履歴タイプを削除する手順:
「ツール」メニューで「設定」を選択します。
「設定」ダイアログで、「ADF Business Components」を展開し、「履歴タイプ」をクリックします。
「履歴タイプ」ページで、削除する履歴タイプを選択して「削除」をクリックします。
基礎となる表に対する挿入、更新、および削除のアクセスをカプセル化するPL/SQLパッケージがある場合は、その表を表すエンティティ・オブジェクトに対するデフォルトのDML処理イベントをオーバーライドし、かわりにPL/SQL APIの中でプロシージャを呼び出すことができます。通常、このようなPL/SQLパッケージは、付随するデータベース・ビューと組み合せて使用されます。クライアント・プログラムは、データベース・ビューを使用して基になる表からデータを読み取り、PL/SQLパッケージのプロシージャを使用してデータを表に書き戻します。
たとえば、このようなビューとパッケージの組合せに基づいてProduct
エンティティ・オブジェクトを作成するとします。
Fusion Order DemoスキーマのPRODUCTS
表を使用し、次のDDL文を使用して作成されるPRODUCTS_V
という名前のデータベース・ビューについて考えます。
create or replace view products_v as select product_id,name,image,description from products;
さらに、基になるPRODUCTS
表に対する挿入、更新、削除権限をカプセル化する、例4-39で示されるような簡単なPRODUCTS_API
パッケージを使用します。
例4-39 PRODUCTS表に対する簡単なPL/SQLパッケージAPI
create or replace package products_api is procedure insert_product(p_prod_id number, p_name varchar2, p_supplier_id number, p_list_price number, p_min_price number, p_ship_code varchar2); procedure update_product(p_prod_id number, p_name varchar2, p_supplier_id number, p_list_price number, p_min_price number, p_ship_code varchar2); procedure delete_product(p_prod_id number); end products_api;
この組合せのデータベース・ビューとPL/SQLパッケージに基づいてエンティティ・オブジェクトを作成するには、次の手順を実行します。
4.17.1項「ビューを基にしてエンティティ・オブジェクトを作成する方法」の説明に従ってビューを基にしたエンティティ・オブジェクトを作成します。
4.17.3項「PL/SQLベースのエンティティに関する詳細をベース・クラスに集中化する方法」の説明に従ってエンティティ・オブジェクトのベース・クラスを作成します。
4.17.4項「DML操作のストアド・プロシージャ呼出しを実装する方法」の説明に従って適切なストアド・プロシージャ呼出しを実装します。
必要に応じて、4.17.5項「選択処理およびロック処理を追加する方法」の説明に従って選択機能とロック機能を処理します。
注意: この項の例では、Fusion Order Demoアプリケーションの |
ビューを基にしてエンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
作業を始める前に、次のようにします。
エンティティ・オブジェクトでPS/SQL APIを使用できる方法に関する知識が役立つ場合があります。詳細は、4.17項「PL/SQLパッケージAPIに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
4.2.2項「エンティティの作成ウィザードで単一のエンティティ・オブジェクトを作成する方法」の説明に従ってエンティティ・オブジェクト作成ウィザードを起動してウィザードを実行する必要がありますが、次の手順で示す例外があります。
ビューを基にしてエンティティ・オブジェクトを作成する手順:
「名前」ページで、Product
のような名前をエンティティに付与します。
「スキーマ・オブジェクトの選択」ダイアログで、「ビュー」チェック・ボックスを「オブジェクト・タイプ」セクションで選択します。
「問合せ」をクリックした際に現在のスキーマで使用できるデータベース・ビューを表示できます。
「使用可能なオブジェクト」リストで目的のデータベース・ビューを選択します。
「属性の設定」ページで、「属性の選択」ドロップダウン・リストを使用して、主キーとして機能する属性を選択し、その属性の「主キー」チェック・ボックスを選択します。
注意: ビューを基にしてエンティティを定義するときは、データ・ディクショナリにデータベース・ビューに関連する制約がないため、主キー属性を自動的に決定することはできません。 |
ビューに基づくエンティティ・オブジェクトは、デフォルトで、基礎となるデータベース・ビューに対して次のすべての文を直接実行します。
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に基づくエンティティ・オブジェクトが複数ある場合は、一般的な詳細をベース・フレームワーク拡張クラスに抽象化するのがよい方法です。その際、第12章「ビジネス・コンポーネントの高度な手法」に説明されているいくつかの概念を使用します。まず、ベースEntityImpl
クラスを拡張するPLSQLEntityImpl
クラスを作成します。このクラスは、PL/SQLベースの各エンティティがベース・クラスとして使用できます。例4-40に示すように、ベース・クラスのdoDML()
メソッドをオーバーライドし、操作に基づいて異なるヘルパー・メソッドを呼び出します。
注意: エンティティにすでに拡張したエンティティ実装クラスを使用している場合、 |
例4-40 操作に基づいて異なるプロシージャを呼び出すための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 non-default processing */ protected void callInsertProcedure(TransactionEvent e) { super.doDML(DML_INSERT, e); } /* Override in a subclass to perform non-default processing */ protected void callUpdateProcedure(TransactionEvent e) { super.doDML(DML_UPDATE, e); } /* Override in a subclass to perform non-default processing */ protected void callDeleteProcedure(TransactionEvent e) { super.doDML(DML_DELETE, e); }
このインフラストラクチャを配置した後、PLSQLEntityImpl
クラスに基づくエンティティ・オブジェクトでは、「ソース」→「メソッドのオーバーライド」メニュー項目を使用して、callInsertProcedure()
、callUpdateProcedure()
およびcallDeleteProcedure()
の各ヘルパー・メソッドをオーバーライドして、その特定のエンティティに適したストアド・プロシージャ呼出しを実行できます。
注意: これらのヘルパー・メソッドをサブクラスでオーバーライドしない場合、スーパークラスで定義されているデフォルトの処理が実行されます。代替処理を提供する |
これらの呼出しを実装する作業を簡単にするには、(12.6項「ストアド・プロシージャとストアド・ファンクションの呼出し」で説明した)callStoredProcedure()
ヘルパー・メソッドをPLSQLEntityImpl
クラスに追加することもできます。このようにすると、このクラスを拡張するPL/SQLベースのエンティティ・オブジェクトはすべて、ヘルパー・メソッドを利用できます。
DML操作に対するストアド・プロシージャ呼出しを実装するには、エンティティ・オブジェクトのカスタムJavaクラスを作成して、その中の操作をオーバーライドする必要があります。
作業を始める前に、次のようにします。
PL/SQLパッケージAPIに基づくエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.17項「PL/SQLパッケージAPIに基づくエンティティ・オブジェクト」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
メソッドのオーバーライドでカスタムJavaクラスを作成する手順:
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクト(Products
など)をダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックします。
概要エディタの「Java」ページで、「Javaオプションの編集」アイコンをクリックします。
「Javaオプションの選択」ダイアログで、「クラスの拡張」をクリックします。
「ベース・クラスのオーバーライド」ダイアログで、「行」フィールドにPLSQLEntityImpl
クラスのパッケージとクラスを入力するか、「参照」をクリックして検索して選択します。
次に、「エンティティ・オブジェクト・クラスの生成」を選択して「OK」をクリックします。
「アプリケーション・ナビゲータ」で、生成されたエンティティ・オブジェクト・クラス(ProductsImpl.java
など)をダブルクリックします。
「ソース」メニューで「メソッドのオーバーライド」を選択します。
「メソッドのオーバーライド」ダイアログで、callInsertProcedure()
、callUpdateProcedure()
およびcallDeleteProcedure()
の各メソッドを選択し、次に「OK」をクリックします。
次に、必要なコードを入力してこられのプロシージャをオーバーライドします。
例4-41は、これらのオーバーライドされるヘルパー・メソッドに記述するサンプル・コードを示しています。
例4-41 ヘルパー・メソッドを利用した挿入、更新、削除プロシージャの呼出し
// In ProductsImpl.java protected void callInsertProcedure(TransactionEvent e) { callStoredProcedure("products_api.insert_product(?,?,?,?,?,?)", new Object[] { getProductId(), getProductName(), getSupplierId(), getListPrice(), getMinPrice(), getShippingClassCode() }); } protected void callUpdateProcedure(TransactionEvent e) { callStoredProcedure("products_api.update_product(?,?,?,?,?,?)", new Object[] { getProductId(), getProductName(), getSupplierId(), getListPrice(), getMinPrice(), getShippingClassCode() }); } protected void callDeleteProcedure(TransactionEvent e) { callStoredProcedure("products_api.delete_product(?)", new Object[] { getProductId() }); }
この段階で、Products
エンティティ・オブジェクトに対してProducts
という名前でデフォルトのエンティティ・ベースのビュー・オブジェクトを作成し、そのインスタンスをProductsModule
アプリケーション・モジュールに追加すると、Oracle ADFモデル・テスターで、Products
ビュー・オブジェクト・インスタンスの行の挿入、更新、削除を簡単にテストできます。
通常は、挿入、更新、および削除の操作をオーバーライドするのみで十分です。データベース・ビューに対してfindByPrimaryKey()
のSELECT
文およびlock()
のSELECT FOR UPDATE
文を実行するデフォルトの動作は、ほとんどの基本的な種類のビューで動作します。
ただし、ビューが複雑でSELECT FOR UPDATE
をサポートしない場合、または他のストアド・プロシージャAPIを使用してfindByPrimaryKey()
機能およびlock()
機能を実行する必要がある場合、4.17.5項「選択処理およびロック処理を追加する方法」に説明されている方法を実行できます。
必要に応じて、ストアド・プロシージャを呼び出して、エンティティ・オブジェクトのlock()
およびfindByPrimaryKey()
機能を処理できます。PRODUCTS_API
パッケージを更新し、例4-42で示されている2つのプロシージャを追加したものとします。lock_product
プロシージャとselect_product
プロシージャはどちらも、IN
パラメータとして主キー属性を受け取り、OUT
パラメータを使用して残りの属性の値を返します。
例4-42 PRODUCTS表用に追加するロックおよび選択プロシージャ
/* Added to PRODUCTS_API package */ procedure lock_product(p_prod_id number, p_name OUT varchar2, p_supplier_id OUT number, p_list_price OUT number, p_min_price OUT number, p_ship_code OUT varchar2); procedure select_product(p_prod_id number, p_name OUT varchar2, p_supplier_id OUT number, p_list_price OUT number, p_min_price OUT number, p_ship_code 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-43は、必要に応じてサブクラスがオーバーライドできる2つのヘルパー・メソッドに委譲するようオーバーライドされたPLSQLEntityImpl
のdoSelect()
メソッドを示します。
例4-43 ロック・パラメータに基づいて異なるプロシージャを呼び出すための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 non-default processing */ protected void callLockProcedureAndCheckForRowInconsistency() { super.doSelect(true); } /* Override in a subclass to perform non-default 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
に置くと、エンティティ・オブジェクト実装クラス(ProductsImpl
など)のcallSelectProcedure()
およびcallLockProcedureAndCheckForRowInconsistency()
ヘルパー・メソッドをオーバーライドできます。select_product
プロシージャとlock_product
プロシージャはOUT
引数を持っているため、12.6.4項「他の種類のストアド・プロシージャの呼出し方法」で説明したように、JDBCのCallableStatement
オブジェクトを使用してこれらの呼出しを実行する必要があります。
例4-44は、ProductsImpl
エンティティ・オブジェクト実装クラスのselect_product
プロシージャを呼び出すために使用するコードを示します。このコードが実行する基本的な手順は次のとおりです。
PL/SQLブロックが呼び出すCallableStatement
を作成します。
1から始まるバインド変数位置で、OUT
パラメータと型を登録します。
IN
パラメータの値を設定します。
文を実行します。
更新された列の値を取得します。
更新された属性値を行に移入します。
文を閉じます。
例4-44 主キーで行を選択するためのストアド・プロシージャの呼出し
// In ProductsImpl.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, NUMBER); st.registerOutParameter(4, NUMBER); st.registerOutParameter(5, NUMBER); st.registerOutParameter(6, VARCHAR2); // 3. Set the IN parameter value st.setObject(1, getProductId()); // 4. Execute the statement st.executeUpdate(); // 5. Retrieve the possibly updated column values String possiblyUpdatedName = st.getString(2); String possiblyUpdatedSupplierId = st.getString(3); String possiblyUpdatedListPrice = st.getString(4); String possiblyUpdatedMinPrice = st.getString(5); String possiblyUpdatedShipCode = st.getString(6); // 6. Populate the possibly updated attribute values in the row populateAttribute(PRODUCTNAME, possiblyUpdatedName, true, false, false); populateAttribute(SUPPLIERID, possiblyUpdatedSupplierId, true, false, false); populateAttribute(LISTPRICE, possiblyUpdatedListPrice, true, false, false); populateAttribute(MINPRICE, possiblyUpdatedMinPrice, true, false, false); populateAttribute(SHIPPINGCLASSCODE, possiblyUpdatedShipCode, 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-45は、lock_product
プロシージャを呼び出すためのコードを示しています。基本的には例4-44と同じ手順を行いますが、次の2点のみ異なります。
OUT
パラメータから更新された可能性のある列の値を取得した後、PLSQLEntityImpl
から継承されたcompareOldAttrTo()
ヘルパー・メソッドを使用して、行ロック試行の結果としてRowInconsistentException
をスローする必要があるかどうかを判定します。
catch (SQLException e)
ブロックでは、データベースがエラーをスローしたかどうかを検査しています。
ORA-00054: resource busy and acquire with NOWAIT specified
例外がスローされている場合は、lock()
機能のデフォルトのエンティティ・オブジェクトの実装がこのような状況で行うのと同じように、再びADF Business ComponentsのAlreadyLockedException
をスローします。
例4-45 主キーで行をロックするためのストアド・プロシージャの呼出し
// 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, NUMBER); st.registerOutParameter(4, NUMBER); st.registerOutParameter(5, NUMBER); st.registerOutParameter(6, VARCHAR2); st.setObject(1, getProductId()); st.executeUpdate(); String possiblyUpdatedName = st.getString(2); String possiblyUpdatedSupplierId = st.getString(3); String possiblyUpdatedListPrice = st.getString(4); String possiblyUpdatedMinPrice = st.getString(5); String possiblyUpdatedShipCode = st.getString(6); compareOldAttrTo(PRODUCTNAME, possiblyUpdatedName); compareOldAttrTo(SUPPLIERID, possiblyUpdatedSupplierId); compareOldAttrTo(LISTPRICE, possiblyUpdatedListPrice); compareOldAttrTo(MINPRICE, possiblyUpdatedMinPrice); compareOldAttrTo(SHIPPINGCLASSCODE, possiblyUpdatedShipCode); } 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
パッケージのProducts
エンティティ・オブジェクト)ができます。ビュー・オブジェクトのデータ問合せ機能およびエンティティ・オブジェクトのデータ検証と保存機能を明確に分離するため、通常のエンティティ・オブジェクトを使用するのと同じように、このエンティティ・オブジェクトを利用できます。このエンティティ・オブジェクトをエンティティ慣用名として使用する異なるビュー・オブジェクトを、必要な数だけ作成できます。
RowInconsistentException
が発生した後で、lock()
メソッドをオーバーライドして、エンティティ・オブジェクトをリフレッシュすることができます。例4-46に、エンティティ・オブジェクト実装クラスに追加して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動作を実装し、挿入または更新の後でリフレッシュするための値を取得します。
継承はオブジェクト指向開発の強力な機能であり、適切に使用すれば開発とメンテナンスを簡単にできます。12.9項「継承を使用する拡張コンポーネントの作成」で説明されているように、ADF Business Componentsでは、継承を使用して既存のコンポーネントを拡張する新しいコンポーネントを作成し、新しいプロパティまたは動作を追加したり、親コンポーネントの動作を変更したりできます。継承は、再利用可能なビジネス・ドメイン・レイヤーで異なる種類のエンティティをモデリングする際に便利です。
注意: この項の例では、Fusion Order Demoアプリケーションの |
アプリケーションのデータベース・スキーマでは、同じ表の行に論理的に異なる種類のビジネス情報が格納される場合があります。このような表には、通常、各行に格納される情報の種類を決定する値を含む列があります。たとえば、Fusion Order DemoアプリケーションのPERSONS
表では、顧客、サプライヤ、スタッフに関する情報が、同一の表に格納されます。また、PERSON_TYPE_CODE
列の値(STAFF
、CUST
またはSUPP
)によって、その行が表すPERSON
の種類が決まります。
Fusion Order Demoの実装にはこの区別は含まれていませんが、アプリケーションの改訂では次のことが必要になることが考えられます。
サプライヤまたはスタッフに固有の、データベースで保持される追加属性の管理
サプライヤまたはスタッフとは異なる、全ユーザーに共通の動作の実装
サプライヤまたはスタッフのみに固有の新機能の実装
図4-19は、Persons
、Staff
、Supplier
の各エンティティ・オブジェクトを個別に作成し、異なる種類のビジネス情報をアプリケーション内でより形式的に区別できるようにした場合の、ビジネス・ドメイン・レイヤーの様子を示しています。サプライヤとスタッフは個人(person)の特別な種類であるため、対応するエンティティ・オブジェクトは、ベースのPersons
エンティティ・オブジェクトを拡張しています。このベースのPersons
エンティティ・オブジェクトには、すべての種類のユーザーに共通する属性とメソッドがすべて含まれています。図のperformPersonFunction()
メソッドは、共通メソッドの1つを表しています。
Supplier
およびStaff
エンティティ・オブジェクトに対しては、その種類のユーザーに固有の属性やメソッドを追加できます。たとえば、サプライヤの現在の契約が期限切れになる時期を追跡するDate
型のContractExpires
属性が、Supplier
に追加されています。また、サプライヤに固有のperformSupplierFunction()
メソッドもあります。同様に、Staff
エンティティ・オブジェクトには、この個人に社員割引の資格があるかどうかを追跡するDiscountEligible
属性が追加されています。performStaffFunction()
は、スタッフに固有のメソッドです。
異なる種類の個人を、ビジネス・ドメイン・レイヤーの継承階層において異なるエンティティ・オブジェクトとしてモデル化することで、共通するデータや動作の共有が簡単になり、ユーザーを区別するアプリケーションの部分を実装できます。
継承階層にエンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用して各エンティティを作成します。
ここで説明する例では、次のDDL文を実行してFODアプリケーションのPERSONS
表を変更し、2つの新しい列を追加してあるものとします。
alter table persons add ( discount_eligible varchar2(1), contract_expires date );
継承階層にエンティティ・オブジェクトを作成するには、次の作業を行います。
4.19.2.1項「識別子列と個別の値の識別」の説明に従って、識別子列と値を識別します。
4.19.2.2項「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性のサブセットを識別します。
4.19.2.3項「継承階層へのベース・エンティティ・オブジェクトの作成」の説明に従って、ベース・エンティティ・オブジェクトを作成します。
4.19.2.4項「継承階層へのサブタイプ・エンティティ・オブジェクトの作成」の説明に従って、サブタイプ・エンティティ・オブジェクトを作成します。
異なる種類の情報を含む表に基づいて継承階層にエンティティ・オブジェクトを作成する前にまず、行の種類を区別するために使用されている表の列を識別する必要があります。
たとえば、Fusion Order DemoアプリケーションのPERSONS
表では、これはPERSON_TYPE_CODE
列です。このような列は表の行を異なるグループに分離つまり識別するのに役立つため、識別子列と呼ばれます。
次に、表の識別子列で有効な値を決定します。値がわからない場合は、JDeveloperの「SQLワークシート」で簡単なSQL文を実行して答えを得ることもできます。ワークシートを使用するには次のようにします。
アプリケーションをJDeveloperで開いて、「データベース・ナビゲータ」を「ビュー」メニューから選択します。
ワークスペース・ノードを開いて、接続を選択します。
この例では、「AdvancedEntityExamples」ノードを展開し、FOD
接続を選択します。
データベース接続を右クリックし、「SQLワークシートを開く」を選択します。
この例では、データベース接続はFODです。
図4-20は、「SQLワークシート」で、PERSONS
表のPERSON_TYPE_CODE
列に対してSELECT DISTINCT
問合せを実行した結果を示しています。これは、PERSON_TYPE_CODE
識別子の値SUPP
、STAFF
、およびCUST
に基づいて行が3つのグループに分類されることを示しています。
表に格納されている異なるビジネス・エンティティの種類の数がわかると、個別の項目をモデル化するために作成する必要のあるエンティティ・オブジェクトの数もわかります。通常は、項目の種類ごとに1つのエンティティ・オブジェクトを作成します。次に、階層のベースとして機能するエンティティを識別するため、各項目の種類に関係する属性のサブセットを特定する必要があります。
たとえば、ContractExpires
とDiscountEligible
を除いてすべての属性はすべてのユーザーに関連することと次のことを調べると仮定します。
ContractExpires
はサプライヤに固有
DiscountEligible
はスタッフに固有。
この情報から、Persons
エンティティ・オブジェクトを階層のベースにし、Supplier
およびStaff
エンティティ・オブジェクトはPersons
を拡張してそれぞれの固有属性を追加することにします。
継承階層にベース・エンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
作業を始める前に、次のようにします。
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、4.19.2.1項「識別子列と個別の値の識別」の説明に従って、識別子列と値を判別し、4.19.2.2項「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性を判別する必要があります。
ベース・エンティティ・オブジェクトを作成する手順
アプリケーション・ナビゲータで、エンティティ・オブジェクトを追加するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
エンティティ・オブジェクト作成ウィザードの「名前」ページで、エンティティの名前とパッケージを指定し、エンティティのベースとなるスキーマ・オブジェクトを選択します。
この例では、エンティティ・オブジェクトにPersonsという名前を設定し、PERSONS
表をベースにします。
「属性」ページで、ベース・エンティティ・オブジェクトに関係のない属性(ある場合)を「エンティティ属性」リストで選択し、「削除」をクリックして削除します。
この例では、DiscountEligible
属性とContractExpires
属性をリストから削除します。
「属性の設定」ページで、「属性の選択」ドロップダウン・リストを使用して、継承されるエンティティ・オブジェクトのファミリの識別子として機能する属性を選択し、「多相化識別子」チェック・ボックスを選択してそのことを示します。重要なこととして、このベース・エンティティ・タイプの行を識別するため、この識別子属性に対する「デフォルト値」を指定する必要もあります。
この例では、PersonTypeCode
属性を選択して識別子属性としてマークし、「デフォルト値」に値cust
を設定します。
注意: 識別子属性の「デフォルト値」を空白のままにしてもかまいません。空白のデフォルト値は、識別子列の値が |
次に、「終了」をクリックしてエンティティ・オブジェクトを作成します。
継承階層にサブタイプ・エンティティ・オブジェクトを作成するには、エンティティ・オブジェクト作成ウィザードを使用します。
作業を始める前に、次のようにします。
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
4.19.2.1項「識別子列と個別の値の識別」の説明に従って識別子列と値を調べます。
4.19.2.2項「エンティティの種類に関連する属性のサブセットの識別」の説明に従って、エンティティ・オブジェクトごとに属性を調べます。
4.19.2.3項「継承階層へのベース・エンティティ・オブジェクトの作成」の説明に従って、新しいエンティティ・オブジェクトが拡張する元となる親エンティティ・オブジェクトを作成します。
親エンティティで識別子属性がすでに識別されていることを確認します。
識別されていない場合は、継承された子を作成する前に、概要エディタを使用して親エンティティの識別子属性で「多相化識別子」プロパティを設定します。
階層にサブタイプ・エンティティ・オブジェクトを作成する手順:
アプリケーション・ナビゲータで、エンティティ・オブジェクトを追加するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で、「ビジネス層」を展開し、「ADF Business Components」を選択してから「エンティティ・オブジェクト」を選択し、「OK」をクリックします。
エンティティ・オブジェクト作成ウィザードの「名前」ページで、エンティティの名前とパッケージを指定し、「拡張」フィールドの隣の「参照」ボタンをクリックして、作成しているエンティティが拡張される元となる親エンティティを選択します。
この例では、新しいエンティティの名前をStaff
にし、「拡張」フィールドでPersons
エンティティ・オブジェクトを選択します。
「属性」ページの「エンティティ属性」リストに、基礎となる表からベース・エンティティ・オブジェクトに含まれない属性が表示されます。このエンティティ・オブジェクトに含めない属性を選択し、「削除」をクリックします。
この例では、ここではStaff
エンティティを作成するので、ContractExpires
属性を削除して、DiscountEligible
属性を残します。
「オーバーライド」をクリックして識別子属性を選択し、属性メタデータをカスタマイズしてStaff
サブタイプに個別の「デフォルト値」を提供できるようにします。
この例では、PersonTypeCode
属性をオーバーライドします。
「属性の設定」ページで、「属性の選択」ドロップダウン・リストを使用して識別子属性を選択します。「デフォルト値」フィールドを変更し、作成しているエンティティ・サブタイプを定義する識別子属性に個別のデフォルト値を設定します。
この例では、PersonTypeCode
属性を選択し、「デフォルト値」を値staff
に変更します。
「終了」をクリックしてサブタイプ・エンティティ・オブジェクトを作成します。
注意: 同じ手順を繰り返して、 |
継承階層内のエンティティ・オブジェクトにメソッドを追加するには、エンティティ・オブジェクトでカスタムJavaクラスを有効にし、ソース・エディタを使用してメソッドを追加します。階層内のすべてのエンティティ・オブジェクトに共通するメソッドをベース・エンティティに追加し、サブタイプに固有のメソッドをサブタイプに追加します。また、必要に応じてサブタイプにおいてベース・エンティティ・オブジェクトのメソッドをオーバーライドできます。
階層内のすべてのエンティティ・オブジェクトに共通するメソッドを追加するには、ベース・エンティティ・オブジェクトの実装クラスにメソッドを追加します。
作業を始める前に、次のようにします。
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、4.19.2項「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成する必要もあります。
階層内の全エンティティ・オブジェクトに共通するメソッドを追加する手順:
「アプリケーション・ナビゲータ」で、ベース・エンティティ・オブジェクト実装クラス(PersonsImpl.java
など)をダブルクリックします。
ベース・エンティティ・オブジェクトにカスタムJava実装クラスがない場合は、作成する必要があります。
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクト(Persons
など)をダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックします。
概要エディタの「Java」ページで、「Javaオプションの編集」アイコンをクリックします。
次に、「エンティティ・オブジェクト・クラスの生成」を選択して「OK」をクリックします。
ソース・エディタで、メソッドを追加します。
たとえば、Persons
ベース・エンティティ・オブジェクトのPersonsImpl
クラスに次のメソッドを追加できます。
// In PersonsImpl.java public void performPersonFunction() { System.out.println("## performPersonFunction as Customer"); }
これはベース・エンティティ・オブジェクトのクラスなので、ここで実装するメソッドは、階層のすべてのサブタイプ・エンティティ・オブジェクトによって継承されます。
階層内のすべてのエンティティ・オブジェクトに共通するサブタイプ・エンティティ・オブジェクトをオーバーライドするには、ベース・エンティティ・オブジェクトから継承した共通メソッドをサブタイプ・エンティティ・オブジェクトの実装クラスで修正します。
作業を始める前に、次のようにします。
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、次のタスクを実行する必要があります。
また、4.19.2項「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成します。
4.19.3.1項「階層内の全エンティティ・オブジェクトに共通するメソッドの追加」の説明に従って、ベース・エンティティ・オブジェクトの共通メソッドを作成します(サブタイプ・エンティティ・オブジェクトがオーバーライドします)。
サブタイプ・エンティティ・オブジェクトのメソッドをオーバーライドする手順:
「アプリケーション・ナビゲータ」で、サブタイプ・エンティティ・オブジェクト実装クラス(StaffImpl.java
など)をダブルクリックします。
サブタイプ・エンティティ・オブジェクトにカスタムJava実装クラスがない場合は、作成する必要があります。
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクト(Staff
など)をダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックします。
概要エディタの「Java」ページで、「Javaオプションの編集」アイコンをクリックします。
次に、「エンティティ・オブジェクト・クラスの生成」を選択して「OK」をクリックします。
サブタイプ・エンティティ・オブジェクトの実装クラスをソース・エディタで開き、「メソッドのオーバーライド」を「ソース」メニューから選択します。
「メソッドのオーバーライド」ダイアログで、オーバーライドするメソッド(たとえば、performPersonFunction()
メソッド)を選択して、「OK」をクリックします。
ソース・エディタで、オーバーライドするメソッドの実装をカスタマイズします。
たとえば、Staff
サブタイプ・エンティティ・オブジェクトのStaffImpl
クラスでperformPersonFunction()
メソッドをオーバーライドし、実装を次のように変更できます。
// In StaffImpl.java public void performPersonFunction() { System.out.println("## performPersonFunction as Staff"); }
サブタイプ階層でエンティティ・オブジェクトのインスタンスについての作業を行う場合、複数の異なるサブタイプのインスタンスを処理することがあります。Staff
エンティティ・オブジェクトとSupplier
エンティティ・オブジェクトはPersons
の特別な種類であるため、これらすべてに共通の汎用性の高いPersonsImpl
型を使用して、すべてのエンティティで動作するコードを作成できます。階層内のサブタイプ・ファミリであるクラスのこのような汎用処理を行うとき、Javaは、使用可能なメソッドで最も固有性の高いオーバーライドを常に呼び出します。
つまり、PersonsImpl
のインスタンスでperformPersonFunction()
メソッドを呼び出し、実際にはさらに固有のStaffImpl
サブタイプがある場合、結果の出力は次のようになります。
## performPersonFunction as Staff
これに対し、標準のPersonsImpl
インスタンスによるデフォルトの出力結果は次のようになります。
## performPersonFunction as Customer
階層内のサブタイプ・エンティティ・オブジェクトに特有のメソッドを追加するには、ソース・エディタを使用してサブタイプの実装クラスにメソッドを追加します。
作業を始める前に、次のようにします。
継承階層のエンティティ・オブジェクトに関する知識が役立つ場合があります。詳細は、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」を参照してください。
他のエンティティ・オブジェクト機能を使用して追加できる追加機能に関する知識が役立つ場合もあります。詳細は、4.1.2項「エンティティ・オブジェクトの追加機能」を参照してください。
また、4.19.2項「継承階層内にエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトとサブタイプを階層に作成する必要もあります。
サブタイプ・エンティティ・オブジェクトに特有のメソッドを追加する手順:
「アプリケーション・ナビゲータ」で、サブタイプ・エンティティ・オブジェクト実装クラス(SupplierImpl.java
など)をダブルクリックします。
ベース・エンティティ・オブジェクトにカスタムJava実装クラスがない場合は、作成する必要があります。
「アプリケーション・ナビゲータ」で、エンティティ・オブジェクト(Supplier
など)をダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックします。
概要エディタの「Java」ページで、「Javaオプションの編集」アイコンをクリックします。
次に、「エンティティ・オブジェクト・クラスの生成」を選択して「OK」をクリックします。
ソース・エディタで、メソッドを追加します。
たとえば、Supplier
エンティティ・オブジェクトのSuppierImpl
クラスに、performSupplierFunction()
メソッドを追加できます。
// In SupplierImpl.java public void performSupplierFunction() { System.out.println("## performSupplierFunction called"); }
継承を使用すると、新しいベース・エンティティの導入、主キーによるサブタイプ・エンティティの検索、および多相エンティティ・オブジェクトの慣用名を持つビュー・オブジェクトの作成も行うことができます。
InheritanceAndPolymorphicQueries
サンプル・プロジェクトで、Persons
エンティティ・オブジェクトは、PERSONS
表の実際の行に対応するとともに、階層でベース・エンティティの役割も果たしていました。つまり、そのすべての属性は、階層内の全エンティティ・オブジェクトに共通でした。
しかし、顧客には固有であっても、スタッフまたはサプライヤには共通しないプロパティがPersons
エンティティ・オブジェクトに必要な場合が発生することがあります。たとえば、一般的に顧客は顧客満足度調査に参加可能だが、スタッフやサプライヤは参加しません。この要件に対処するためのLastSurveyDate
属性は、Persons
エンティティには必要ですが、それを継承するStaff
およびSupplier
エンティティ・オブジェクトには無意味です。
このような場合は、階層内でベース・エンティティとして機能する新しいエンティティ・オブジェクト(BasePersons
など)を導入できます。それは、Persons
、Staff
、およびSupplier
の全エンティティ・オブジェクトに共通するすべての属性を保持します。そして、この表に出現する具体的な行に対応する3つの各エンティティは、BasePersons
から継承する属性と、個々のサブタイプに固有の属性を保持できます。BasePersons
型では、PersonTypeCode
属性を識別子属性としてマークするかぎり、「デフォルト値」を空白にしておいてかまいません(または、表のPERSON_TYPE_CODE
列に出現しない他の値にします)。BasePersons
エンティティのインスタンスはアプリケーションで使用されないため、識別子のデフォルト値は問題ではありません。
エンティティ定義でfindByPrimaryKey()
メソッドを使用するときは、それを呼び出すエンティティ・オブジェクト型のエンティティ・キャッシュのみが検索されます。InheritanceAndPolymorphicQueries
サンプル・プロジェクトが意味するのは、PersonsImpl.getDefinitionObject()
をコールしてPersons
エンティティ・オブジェクトのエンティティ定義にアクセスする場合、そこでfindByPrimaryKey()
をコールすると、たまたま顧客であるキャッシュ内のエントリのみが検索結果となります。これが求める動作である場合もあります。
しかし、主キーで継承階層内のサブタイプも含めてエンティティ・オブジェクトを検索する場合は、かわりにEntityDefImpl
クラスのfindByPKExtended()
メソッドを使用できます。
たとえば、Persons
エンティティ・オブジェクトのサブタイプを作成した場合、この代替検索メソッドを使用すると、エンティティ・オブジェクトが顧客、サプライヤまたはスタッフのいずれであっても、主キーで検索できます。その後は、Javaのinstanceof
演算子を使用して発見された型を検査し、PersonsImpl
オブジェクトをさらに固有のエンティティ・オブジェクト型にキャストして、そのサブタイプに固有の機能で作業できます。
継承階層のベース・エンティティ・オブジェクトに対応するエンティティ・オブジェクトの慣用名を持つエンティティ・ベースのビュー・オブジェクトを作成するときは、ベース・エンティティのサブタイプ階層の複数の異なるサブタイプに対応する行を問い合せるように、ビュー・オブジェクトを構成できます。ビュー・オブジェクトの各行は、識別子属性の値の対応に基づいて、適切なサブタイプ・エンティティ・オブジェクトをエンティティ行の部分として使用します。このようなビュー・オブジェクトを設定して使用する具体的な方法については、42.6.2項「多相エンティティ・オブジェクトの慣用名によるビュー・オブジェクトの作成方法」を参照してください。