ヘッダーをスキップ
Oracle Application Development Framework Forms/4GL開発者のための開発者ガイド
10g(10.1.3.0)
B40013-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

7 エンティティ・ベースのビュー・オブジェクトを使用した更新可能なデータ・モデルの構築

この章では、エンティティ・オブジェクトと自動的に連携して完全に更新可能なデータ・モデルを有効にする更新可能なビュー・オブジェクトを作成する方法について説明します。

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

7.1 エンティティ・ベースのビュー・オブジェクトの概要

エンティティ・ベースのビュー・オブジェクトは、更新可能な行をサポートしています。ビュー・オブジェクトは、クライアントが直面している当面のタスクに必要なデータのみを問い合せ、ビジネス・ドメイン・レイヤー内の1つ以上のエンティティ・オブジェクトと連携し、ビュー行の変更を自動的に検証および保存します。読取り専用ビュー・オブジェクトと同様に、エンティティ・ベースのビュー・オブジェクトは、SQL問合せをカプセル化します。また、このビュー・オブジェクトは、マスター/ディテール階層にリンクしたり、アプリケーション・モジュールのデータ・モデルで使用できます。

この章の終わりには、図7-1に示す概念を理解できるようになります。

この章では、アプリケーション・モジュールのデータ・モデル内のエンティティ・ベースのビュー・オブジェクトのインスタンスを使用して、再使用可能なビジネス・ドメイン・オブジェクトが持つ明確なオブジェクト指向のカプセル化能力にSQLの完全なデータ形成能力を組み合せることにより、クライアントがビジネス・ドメイン・レイヤー情報を検索、更新、挿入および削除できるようにする方法について説明します。また、これらすべての作業についてコード行は必要ありません。

図7-1 ビュー・オブジェクトとエンティティ・オブジェクトの連携による更新可能なデータ・モデルの有効化

オブジェクトによる更新可能なデータ・モデルの作成方法の図

注意:

この章の例では、第6章「エンティティ・オブジェクトを使用したビジネス・ドメイン・レイヤーの作成」と同じServiceRequestServiceHistoryProductUserおよびExpertiseAreaエンティティ・オブジェクトの基本的なSRDemoアプリケーションのビジネス・ドメイン・レイヤーを使用します。この例の動作バージョンを体験するには、サンプルのダウンロード・ページ(http://otn.oracle.com/documentation/jdev/b25947_01)からDevGuideExamplesワークスペースをダウンロードし、EntityBasedViewObjectsプロジェクトを参照してください。

7.2 エンティティ・ベースのビュー・オブジェクトの作成

エンティティ・ベースでビュー・オブジェクトを作成する場合、SQL文を自分で入力する必要がないため、読取り専用ビュー・オブジェクトより簡単に作成できます。また、エンティティ・ベースのビュー・オブジェクトの方が、読取り専用ビュー・オブジェクトより非常に多くの実行時機能が用意されています。

7.2.1 エンティティ・ベースのビュー・オブジェクトの作成方法

エンティティ・ベースのビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。このウィザードは、「新規ギャラリ」「Business Tier」「ADF Business Components」カテゴリから起動できます。ここでは、スタッフ・メンバーの更新可能リストを取得するために、devguide.model.queriesパッケージにStaffListビュー・オブジェクトを作成するとします。

図7-2に示すように、ステップ1の「名前」ページで、ビュー・オブジェクトの名前とパッケージを指定します。データの管理方法は、デフォルト設定の「エンティティ・オブジェクトを介した更新可能アクセス」のままにします。

図7-2 更新可能なビュー・オブジェクトの名前およびパッケージの指定

ビュー・オブジェクトの作成ウィザードのステップ1の図

ステップ2の「エンティティ・オブジェクト」ページで、ビュー・オブジェクトでデータを使用するエンティティ・オブジェクトを選択します。図7-3に、Userエンティティ・オブジェクトを選択して「選択済」リストに移動した結果を示します。このリストのエントリは、エンティティ・オブジェクトの慣用名と呼ばれます。これは、このエンティティには、ビュー・オブジェクトが使用するエンティティ・オブジェクトが記録されるためです。また、このエンティティは、エンティティ参照とも呼ばれます。これは、ビュー・オブジェクトがこのエンティティの属性を参照するためです。

図7-3 データをビュー・オブジェクトに組み込むエンティティ・オブジェクトの選択

ビュー・オブジェクトの作成ウィザードのステップ2の図

ステップ3の「属性」ページで、エンティティ・オブジェクトの慣用名から組み込む属性を「使用可能」リストから選択し、「選択済」リストに移動します。図7-4では、UserIdEmailFirstNameおよびLastName属性が選択されています。

図7-4 データをビュー・オブジェクトに組み込むエンティティ属性の選択

ビュー・オブジェクトの作成ウィザードのステップ3の図

ステップ4の「属性の設定」ページでは、「属性の選択」ドロップダウンを使用してビュー・オブジェクト属性を切り替え、名前や初期設定を変更できます。この例では、デフォルトのままでかまいません。

ステップ5の「SQL文」ページでは、図7-5のように、選択したエンティティ属性に基づいてSELECT文が自動的に生成されます。必要に応じて、データのフィルタリングや順序付けを行うためにWHEREおよびORDER BY句を問合せに追加できます。このStaffListビュー・オブジェクトでは、USERS表でUSER_ROLE列の値としてtechnicianまたはmanagerを持つ行のみを表示するために、「WHERE」フィールドに適切なWHERE句条件を含めることができます。データを姓および名前別に順序付けるには、「ORDER BY」フィールドに適切なORDER BY句を含めます。「WHERE」および「ORDER BY」フィールドの値には、WHEREまたはORDER BYキーワードは表示されません。これらのキーワードは、実行時に問合せを実行する際に追加されます。

図7-5 生成されたSQL文へのカスタムのWHEREおよびORDER BY句の追加

ビュー・オブジェクトの作成ウィザードのステップ5の図

ここで「終了」をクリックし、ビュー・オブジェクトを作成します。

7.2.1.1 エンティティ・オブジェクトのすべての属性を持つビュー・オブジェクトの作成

基礎となるエンティティ・オブジェクトのすべての属性をクライアントが操作できるようにするには、7.2.1項「エンティティ・ベースのビュー・オブジェクトの作成方法」で説明するように、ビュー・オブジェクトの作成ウィザードを使用します。この場合、エンティティ・オブジェクトを選択した後、「属性」ページですべての属性を選択します。ただし、これは頻繁に行う操作であるため、同じ作業をアプリケーション・ナビゲータでより簡単に実行する方法があります。

新しいエンティティ・ベースのビュー・オブジェクトの作成方法:

  1. アプリケーション・ナビゲータで目的のエンティティ・オブジェクトを選択します。

  2. ポップアップ・メニューから「新規デフォルト・ビュー・オブジェクト」を選択します。

  3. 図7-6に示すように、「デフォルト・ビュー・オブジェクトを作成」ダイアログで新しいビュー・オブジェクトのパッケージおよびコンポーネント名を指定します。

図7-6 エンティティ・オブジェクトのデフォルト・ビュー・オブジェクトの簡単な作成方法

「デフォルト・ビュー・オブジェクトを作成」ダイアログの図

作成される新しいエンティティ・ベースのビュー・オブジェクトは、ビュー・オブジェクトの作成ウィザードで作成できるものと同一のものです。デフォルトでは、このビュー・オブジェクトには、アプリケーション・ナビゲータで選択したエンティティ・オブジェクトを参照する単一のエンティティ・オブジェクトの慣用名があり、そのすべての属性が含まれます。最初は、このビュー・オブジェクトにはWHERE句もORDER BY句もないため、次の操作にビュー・オブジェクト・エディタを使用します。

  • 不要な属性の削除

  • WHERE句を使用した選択内容の限定

  • ORDER BY句による結果の順序付け

  • ビュー・オブジェクトのプロパティのカスタマイズ

7.2.2 エンティティ・ベースのビュー・オブジェクトの作成時に行われる処理

エンティティ・ベースのビュー・オブジェクトを作成する際、JDeveloperでビュー・オブジェクトの宣言的設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリに保存されます。図7-2のように、ビュー・オブジェクトにはdevguide.model.queriesパッケージ内でStaffListという名前が付けられたため、作成されたXMLファイルは、プロジェクトのソース・パスの下に./devguide/model/queries/StaffList.xmlとして作成されます。このXMLファイルには、SQL問合せに関する情報、エンティティ・オブジェクトの慣用名、および各属性のプロパティが含まれます。この内容を確認する場合、アプリケーション・ナビゲータでビュー・オブジェクトを選択し、構造ウィンドウで対応するソース・フォルダ内を参照し、ビュー・オブジェクトのXMLファイルを確認します。StaffList.xmlノードをダブルクリックすると、このXMLがエディタで開かれ、その内容を確認できます。


注意:

IDEレベルのビジネス・コンポーネントJava生成設定で指定されている場合、オプションのカスタム・ビュー・オブジェクト・クラスStaffListImpl.javaまたはカスタム・ビュー行クラスStaffListRowImpl.java(あるいはその両方)も生成される場合があります。

7.2.3 既存のエンティティ・ベースのビュー・オブジェクト定義の編集

エンティティ・ベースのビュー・オブジェクトを作成した後、ビュー・オブジェクト・エディタを使用してその設定を編集できます。アプリケーション・ナビゲータのポップアップ・メニューで「編集」メニュー・オプションを選択するか、ビュー・オブジェクトをダブルクリックし、ビュー・オブジェクト・エディタを起動します。エディタの様々なパネルを開き、問合せのWHERE句およびORDER BY句の調整、属性名の変更、名前付きバインド変数の追加、UIコントロール・ヒントの追加、Java生成オプションの制御、およびその他の設定の構成を行うことができます。

7.2.4 ビュー・オブジェクトについて

各ビュー・オブジェクト属性は、対応するエンティティ・オブジェクト属性のプロパティを継承します。

7.2.4.1 基礎となるエンティティ・オブジェクト属性のプロパティを継承するビュー・オブジェクト属性

エンティティ・ベースのビュー・オブジェクトに関して興味深い特徴の1つは、基礎となるエンティティ・オブジェクト属性に関連する各属性がその属性のプロパティを継承することです。図7-7に、UserId属性が選択された状態のビュー・オブジェクト・エディタを示します。ここでは、Javaの「属性」「型」および「問合せ列」「型」などのプロパティは無効になっており、これらの値は、このビュー・オブジェクトが関連付けられているUserエンティティ・オブジェクトの関連UserId属性から継承されています。属性のデータ型などの一部のプロパティは継承され、ビュー・オブジェクト・レベルでは変更できません。

QueryableおよびUpdatableなどの他のプロパティは継承されますが、オーバーライド後の設定の方が継承された設定より制限が大きい場合、これらのプロパティをオーバーライドできます。たとえば、Userエンティティ・オブジェクトのUserId属性の「更新可能」設定は、「常に」です。図7-7のように、ビュー・オブジェクト・エディタでは、対応するビュー・オブジェクト属性を、制限がより大きい「新規の間」または「なし」などに設定できます。しかし、Userエンティティ・オブジェクトのUserId属性の「更新可能」設定が「なし」である場合、StaffListの関連ビュー・オブジェクト属性を、制限がより小さい「常に」などに設定できません。

図7-7 基礎となるエンティティ・オブジェクト属性のプロパティを継承するビュー・オブジェクト属性

ビュー・オブジェクト・エディタの図

7.3 結合ビュー・オブジェクトへの参照エンティティの組込み

ビジネス・アプリケーションでは、プライマリ・ビジネス・ドメイン・オブジェクトとともにセカンダリ参照情報から情報を補足し、外部キー属性が示す内容をエンド・ユーザーが理解しやすいようにすることは非常に一般的です。ServiceRequestエンティティ・オブジェクトを例として考えてみます。このオブジェクトには、次のようなNumber型の外部キー属性が含まれます。

経験上、このような未処理の数値のみをエンド・ユーザーに示しても、あまり役立ちません。アプリケーションの使いやすさを向上させるには、関連するUserおよびProductエンティティ・オブジェクトからの参照情報の表示が理想的です。一般的な解決方法の1つとして、プライマリ情報と参照情報の組合せを取得する結合問合せを実行する方法があります。また、参照表に対する特別な問合せに基づいて、問合せ対象の行ごとにダミー・フィールドに参照情報を移入できます。ただし、エンド・ユーザーがデータの編集時に外部キー値を変更できる場合、この方法には別の課題が生じます。

たとえば、ある技術者から別の技術者にサービス・リクエストを再度割り当てる場合、エンド・ユーザーは、参照情報の同期が維持されているとみなします。この場合、エンティティ・ベースのビュー・オブジェクトは、常に最新の状態の参照情報を簡単に組み込む機能をサポートしています。この機能を活用するには、ビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名として機能するエンティティ・オブジェクトと、参照情報を提供するエンティティ・オブジェクト間にアソシエーションが確立されていることが必要です。

この項では、前に作成したデフォルトのServiceRequestsビュー・オブジェクトを変更し、UserおよびProductエンティティ・オブジェクトからの参照情報を組み込む方法について説明します。

7.3.1 ビュー・オブジェクトへの参照エンティティの組込み方法

ビュー・オブジェクトの参照エンティティを組み込むには、単一のエンティティ・オブジェクトの慣用名をすでに持つエンティティ・ベースのビュー・オブジェクトに対してビュー・オブジェクト・エディタを開き、「エンティティ・オブジェクト」ページを開きます。このページの「選択済」リストの最初のエンティティ・オブジェクトの慣用名は、ビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名と呼ばれます。ただし、このリストは単一のエンティティ・オブジェクトの慣用名には限定されません。ビュー・オブジェクトで別のエンティティ・オブジェクトを使用するには、これらを「使用可能」リストから選択し、「選択済」リストに移動します。

7.3.1.1 ビュー・オブジェクトへの別の参照エンティティ・オブジェクトの慣用名の追加

図7-8に、既存のServiceRequestsビュー・オブジェクトに3つの参照エンティティ・オブジェクトの慣用名を追加した結果を示します。このうち、1つはProductエンティティ用で、2つはUserエンティティ用の個別の慣用名です。「選択済」リストでエンティティ・オブジェクトの慣用名をクリックすると、「参照」チェック・ボックスの状態により、ビュー・オブジェクトに追加された2番目以降のエンティティ・オブジェクトの慣用名が参照情報としてデフォルトでマークされることが示されます。同様に、「更新可能」チェック・ボックスが選択されていない状態であることからわかるように、セカンダリ・エンティティ・オブジェクトの慣用名は更新不可能としてデフォルト設定されます。


注意:

セカンダリ・エンティティ・オブジェクトの慣用名を追加すると、これらの「更新可能」フラグはfalse、「参照」フラグはtrueにデフォルト設定されます。これは最も一般的な慣用名のパターンです。27.9項「複数の更新可能エンティティによるビュー・オブジェクトの作成」では、あまり一般的ではないが役に立つ方法として、更新可能エンティティ・オブジェクトの慣用名を複数持つ結合ビュー・オブジェクトを作成する方法について説明します。

「アソシエーション」ドロップダウン・リストには、選択したエンティティ・オブジェクトの慣用名をプライマリ・エンティティ・オブジェクトの慣用名に関連付けるアソシエーションの名前が表示されます。デフォルト名が明確でない場合は、「別名」フィールドを使用して、より意味のある名前をエンティティ・オブジェクトの慣用名として付けることができます。たとえば、Userエンティティ・オブジェクトの2つのエンティティ・オブジェクトの慣用名を「選択済」リストに移動した後、これらのオブジェクトの慣用名の当初の別名はUser1User2でした。図からわかるように、これらの名前をTechnicianCustomerに変更すると、ビュー・オブジェクトが参照している情報が非常に明確になります。この図からわかるもう1つ重要な点は、同じエンティティに対して複数のエンティティ・オブジェクトの慣用名を追加する場合、「アソシエーション」ドロップダウン・リストを使用して、このオブジェクトの慣用名とプライマリ・エンティティ・オブジェクトの慣用名との関連を示すアソシエーションを選択する必要があることです。Technicianエンティティ・オブジェクトの慣用名の場合はServiceRequestsAssignedToUserアソシエーションを選択し、Customerエンティティ・オブジェクトの慣用名の場合はServiceRequestsCreatedByUserアソシエーションを選択します。

図7-8 複数の参照エンティティ・オブジェクトの慣用名に対する正しいアソシエーションの指定

この図は、参照エンティティ・オブジェクトの慣用名のビュー・オブジェクトへの追加を示しています。

7.3.1.2 参照エンティティ・オブジェクトの慣用名からの追加属性の選択

セカンダリ・エンティティ・オブジェクトの慣用名を追加した後、ビュー・オブジェクト・エディタの「属性」ページに切り替え、ビュー・オブジェクトに組み込むこれらの新しいオブジェクトの慣用名から特定の追加属性を選択します。図7-9に、次の追加属性を「選択済」リストに移動した結果を示します。

  • Productエンティティ・オブジェクトの慣用名のName属性

  • Technicianエンティティ・オブジェクトの慣用名のEmail属性

  • Customerエンティティ・オブジェクトの慣用名のEmail属性

これらを組み込む意図がなかったとしても、各エンティティ・オブジェクトの慣用名の主キー属性が「選択済」リストの一部であるかどうかが自動的に検証されます。これらがリストに含まれていない場合は自動的に追加されます。

図7-9 ビュー・オブジェクトに組み込む追加の参照エンティティ属性の選択

「選択済」リストへの追加属性の移動の図

「SQL文」ページを選択すると、SELECT文に新しい列が含まれるとともに、「WHERE」フィールドが更新され、次のような適切な結合句が含まれていることがわかります。

((ServiceRequest.PROD_ID = Product.PROD_ID) AND
 (ServiceRequest.ASSIGNED_TO = Technician.USER_ID)) AND
 (ServiceRequest.CREATED_BY = Customer.USER_ID)

7.3.1.3 参照エンティティ・オブジェクトの慣用名の属性の名前変更

ビュー・オブジェクト・エディタの左側のツリーで「属性」ノードを開くと、リストの最後に属性が追加されていることがわかります。デフォルトの属性名はわかりにくいため、1つずつ順に選択し、名前を次のように変更できます。

  • NameProductName

  • ProdId1PKProdIdProductエンティティ・オブジェクトの慣用名の主キー)

  • EmailTechnicianEmail

  • UserIdPKTechnicianUserIdTechnicianエンティティ・オブジェクトの慣用名の主キー)

  • Email1CustomerEmail

  • UserId1PKCustomerUserIdCustomerエンティティ・オブジェクトの慣用名の主キー)

7.3.1.4 参照エンティティ・オブジェクトの慣用名からの不要なキー属性の削除

プライマリ・エンティティ・オブジェクトの慣用名の主キー属性に対応するビュー・オブジェクト属性は、ビュー行を識別するための主キーとして機能します。セカンダリ・エンティティ・オブジェクトの慣用名を追加すると、これらの主キー属性に対応するビュー・オブジェクト属性もビュー行キーの一部としてマークされます。ビュー・オブジェクトが単一の更新可能なプライマリ・エンティティ・オブジェクトの慣用名と多くの参照エンティティ・オブジェクトの慣用名で構成されている場合、ビュー行を一意に識別するにはプライマリ・エンティティ・オブジェクトの慣用名の主キー属性で十分です。この場合、これらの追加キー属性は不要であるため、これらの「キー属性」設定をfalseに切り替える必要があります。前に作成したビュー・オブジェクトの場合、PKProdIdPKTechnicianUserIdおよびPKCustomerUserIdの各属性についてこの設定をfalseに切り替え、「キー属性」の選択を解除します。

7.3.1.5 参照エンティティ・オブジェクトの慣用名からの主キー属性の非表示

通常、ビュー・オブジェクトに自動的に追加された主キー属性は表示する必要がないため、図7-10のように、UIの「コントロール・ヒント」ページで「ヒントの表示」プロパティを「非表示」に設定できます。

図7-10 参照エンティティ・オブジェクトの慣用名から主キー属性を非表示にするための属性のコントロール・ヒントの設定

ServiceRequests用のビュー・オブジェクト・エディタの図

「OK」をクリックし、ビュー・オブジェクトの変更を保存します。

7.3.2 ビュー・オブジェクトのエンティティを参照するときに行われる処理

参照別のセカンダリ・エンティティ・オブジェクトの慣用名をビュー・オブジェクトに組み込むと、ビュー・オブジェクトのXMLコンポーネント定義が更新され、追加のエンティティ・オブジェクトの慣用名に関する情報が組み込まれます。たとえば、3つの参照エンティティ・オブジェクトの慣用名を組み込むために拡張した前述のビュー・オブジェクトのServiceRequests.xmlファイルを参照すると、この情報がこのファイルの複数の<EntityUsage>要素に記録されていることがわかります。たとえば、プライマリ・エンティティ・オブジェクトの慣用名のエントリは次のようになります。

<EntityUsage
   Name="ServiceRequest"
   Entity="devguide.model.entities.ServiceRequest"/>

セカンダリの参照エンティティ・オブジェクトの慣用名のエントリはこれとは若干異なり、このエンティティ・オブジェクトの慣用名をプライマリ・エンティティ・オブジェクトの慣用名に関連付けるアソシエーションに関する情報が含まれます。

<EntityUsage
   Name="Product"
   Entity="devguide.model.entities.Product"
   Association=
    "devguide.model.entities.associations.ServiceRequestsForProduct"
   AssociationEnd=
    "devguide.model.entities.associations.ServiceRequestsForProduct.Product"
   SourceUsage="devguide.model.queries.ServiceRequests.ServiceRequest"
   ReadOnly="true"
   Reference="true"/>

XML内の各属性エントリは、参照先のエンティティ・オブジェクトの慣用名を示します。ProblemDescription属性のこのエントリは、この属性がServiceRequestエンティティ・オブジェクトの慣用名に関連付けられていることを示します。

   <ViewAttribute
      Name="ProblemDescription"
      IsNotNull="true"
      EntityAttrName="ProblemDescription"
      EntityUsage="ServiceRequest"
      AliasName="PROBLEM_DESCRIPTION" >
   </ViewAttribute>

一方、CustomerEmail属性は、Customerエンティティ・オブジェクトの慣用名に関連付けられています。

   <ViewAttribute
      Name="CustomerEmail"
      IsUpdatable="false"
      IsNotNull="true"
      EntityAttrName="Email"
      EntityUsage="Customer"
      AliasName="EMAIL1" >
   </ViewAttribute>

ビュー・オブジェクト・エディタでは、このアソシエーション情報が設計時に使用され、ビュー・オブジェクトの結合WHERE句が自動的に作成されます。この情報が実行時に使用されることにより、エンド・ユーザーによって外部キー属性値が変更されたときに参照情報を最新の状態に保つことが可能になります。

7.3.3 結合ビュー・オブジェクトについて

ビュー・オブジェクトが複数のエンティティ・オブジェクトを参照している場合、これらのエンティティ・オブジェクトはビジネス・コンポーネント・ダイアグラム上で個別のエンティティ・オブジェクトの慣用名として表示されます。必要であれば、デフォルトの内部結合句を外部結合として変更もできます。

7.3.3.1 ビジネス・コンポーネント・ダイアグラムでのビュー・オブジェクトの表示

6.4項「ビジネス・レイヤーのエンティティ・ダイアグラムの作成」では、ビジネス・コンポーネント・ダイアグラムを作成してビジネス・ドメイン・レイヤーを視覚化する方法について説明しました。JDeveloperのUMLダイアグラムでは、エンティティ・オブジェクトのサポートのみならず、ビュー・オブジェクトをダイアグラムにドロップして、その構造とエンティティ・オブジェクトの慣用名を視覚化できます。devguide.model.designパッケージにSRService Data Modelという名前の新しいビジネス・コンポーネント・ダイアグラムを作成し、アプリケーション・ナビゲータからServiceRequestsビュー・オブジェクトをダイアグラムにドラッグすると、図7-11のようになります。ノードを拡張して表示すると、ダイアグラムには、ビュー・オブジェクトのエンティティ・オブジェクトの慣用名を含む部分が表示されます。

図7-11 ビジネス・コンポーネント・ダイアグラム内のビュー・オブジェクトおよびそのエンティティ・オブジェクトの慣用名

オブジェクト使用方法のビジネス・コンポーネント・ダイアグラムの図

7.3.3.2 必要に応じたデフォルトの結合句の外部結合への変更

プライマリ・エンティティ・オブジェクトの慣用名の表とそれに関連付けられているセカンダリ・エンティティ・オブジェクトの慣用名の表の間に結合用のWHERE句が作成される場合、デフォルトでは常に内部結合が作成されます。ここでは、ServiceRequestsビュー・オブジェクトのWHERE句について詳しく検討します。

((ServiceRequest.PROD_ID = Product.PROD_ID) AND
 (ServiceRequest.ASSIGNED_TO = Technician.USER_ID)) AND
 (ServiceRequest.CREATED_BY = Customer.USER_ID)

サービス・リクエストがまだ技術者に割り当てられていない場合、AssignedTo属性はnullになります。前述のとおり生成されたデフォルトの内部結合条件では、これらの未割当てのサービス・リクエストは取得されません。ServiceRequestsビュー・オブジェクトを介して未割当てのサービス・リクエストを表示可能および更新可能にする必要がある場合、ビュー・オブジェクト・エディタの「SQL文」ページに再度アクセスし、ASSIGNED_TO列のnullになる可能性のある値についてのUSER表に対する外部結合に問合せを変更する必要があります。次に示す更新されたWHERE句では、結合でデータがなくても許可される関連表に対する追加の(+)演算子が等号の右側にあります。

((ServiceRequest.PROD_ID = Product.PROD_ID) AND
 (ServiceRequest.ASSIGNED_TO = Technician.USER_ID (+) )) AND
 (ServiceRequest.CREATED_BY = Customer.USER_ID)

7.4 アソシエーション・ベースのビュー・リンクの作成

読取り専用ビュー・オブジェクトの場合と同様に、エンティティ・ベースのビュー・オブジェクトを他のビュー・オブジェクトにリンクし、任意の複雑さでマスター/ディテール階層を形成できます。作成手順で唯一異なるのは、マスター・ビュー・オブジェクトとディテール・ビュー・オブジェクトの両方がエンティティ・ベースのビュー・オブジェクトで、これらの各エンティティ・オブジェクトの慣用名がアソシエーションによって関連付けられている場合です。この場合、アソシエーションにより、これらのオブジェクトを関連付ける一連のソース属性と関連先属性のペアが取得されるため、ベースとなるアソシエーションを指定するのみでビュー・リンクを作成できます。

7.4.1 アソシエーション・ベースのビュー・リンクの作成方法

アソシエーション・ベースのビュー・リンクを作成するには、ビュー・リンクの作成ウィザードを使用します。

アソシエーション・ベースのビュー・リンクの作成方法:

  1. 「新規ギャラリ」「Business Tier」「ADF Business Components」カテゴリから、ビュー・リンクの作成ウィザードを選択します。

  2. ステップ1の「名前」ページで、パッケージおよびコンポーネント名を指定します。ここでは、devguide.model.queries.viewlinksパッケージにビュー・リンクを作成し、これをRequestsAssignedToTechnicianと呼ぶとします。

  3. ステップ2の「ビュー・オブジェクト」ページの「ソース属性の選択」ツリーで、devguide.model.queriesパッケージ内のソースとなるStaffListビュー・オブジェクトを開きます。「関連先属性の選択」ツリーで、ServiceRequestsビュー・オブジェクトを開きます。ビュー・オブジェクト属性の他にも、エンティティ・ベースのビュー・オブジェクトの場合、リストには関連するアソシエーションも表示されます。図7-12のように、「ソース属性の選択」ツリーと「関連先属性の選択」ツリーの両方で同じServiceRequestsAssignedToUserアソシエ追加追加」をクリックしてアソシエーションを下の表に追加します。「次へ」および「終了」をクリックし、新しいビュー・リンクの作成を完了します。

    図7-12 ソース・ビュー・オブジェクトのエンティティ・オブジェクトの慣用名を関連先に関連付けるアソシエーションの選択

    ビュー・オブジェクトウィザードのステップ2の図
  4. 次に、サービス・リクエストの履歴エントリに関する詳細情報を表示するビュー・オブジェクトとServiceRequestsビュー・オブジェクトの間に別のアソシエーション・ベースのビュー・リンクを作成します。すでにマスターのServiceRequestsビュー・オブジェクトはありますが、最初にディテール用のビュー・オブジェクトを作成してから、これらをリンクさせる必要があります。前述の簡単な作成方法を使用して、アプリケーション・ナビゲータでdevguide.model.entitiesパッケージ内のServiceHistoryエンティティ・オブジェクトを選択し、ポップアップ・メニューから新規デフォルト・ビュー・オブジェクト」を選択し、このエンティティに基づいてdevguide.model.queries内にServiceHistoriesという名前のビュー・オブジェクトを作成します。


    注意:

    「デフォルト・ビュー・オブジェクトを作成」では、コンボボックスを使用して、既存のパッケージのリストからdevguide.model.queriesを選択できます。

  5. 最後に、前述のステップを繰り返し、ServiceRequestsビュー・オブジェクトと新しいServiceHistoriesビュー・オブジェクト間に、これらのプライマリ・エンティティ・オブジェクトの慣用名をそれぞれ関連付けるアソシエーションに基づいてアソシエーション・ベースのビュー・リンクを作成します。devguide.model.queries.viewlinksパッケージでこのビュー・リンクにHistoryLinesForRequestという名前を付けます。パッケージ名を入力する必要のない、もう1つの簡単な方法として、図7-13に示すとおり、アプリケーション・ナビゲータでviewlinksパッケージ・ノードのポップアップ・メニューの「新規ビュー・リンク」を使用できます。

    図7-13 既存のパッケージでビュー・リンクを作成する簡単な方法

    この図は、アプリケーション・ナビゲータのポップアップ・メニューを示しています。

7.4.2 アソシエーション・ベースのビュー・リンクの作成時に行われる処理

アソシエーション・ベースのビュー・リンクを作成する際、JDeveloperでその宣言的設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリ内に保存されます。前述の例では、ビュー・リンクにdevguide.model.queries.viewlinksパッケージでRequestsAssignedToTechnicianおよびHistoryLinesForRequestという名前が付けられたため、これらのXMLファイルは、プロジェクトのソース・パスの下の./devguide/model/queries/viewlinksディレクトリで/RequestsAssignedToTechnician.xmlおよび/HistoryLinesForRequest.xmlとして作成されます。このXMLファイルには、指定したソース・ビュー・オブジェクトとターゲット・ビュー・オブジェクトを関連付けるアソシエーションに関する宣言情報が含まれています。また、ビュー・リンク・コンポーネント定義自体が保存されるのみならず、ビュー・リンク関連内のソース・ビュー・オブジェクトのXML定義も更新され、ビュー・リンクのアクセッサ属性に関する情報が追加されます。

7.5 エンティティ・ベースのビュー・オブジェクトの対話的テスト

エンティティ・ベースのビュー・オブジェクトの対話的テストは、読取り専用ビュー・オブジェクトと同じ方法で行います。目的のビュー・オブジェクトのインスタンスをアプリケーション・モジュールのデータ・モデルに追加してから、Business Component Browserを使用してこのアプリケーション・モジュールをテストします。

7.5.1 更新可能データ・モデル用のBusiness Component Browser機能の概要

Business Component Browserは、アプリケーション・モジュールを簡単にテストおよびデバッグできる非常に便利なツールです。図7-14に、Business Component Browserのすべてのツールバー・ボタンで実行する操作の概要を示します。

図7-14 更新可能データ・モデル用のBusiness Component Browser機能

更新可能データ・モデル用のオブジェクト機能の図

7.5.2 データ・モデルに対するビュー・オブジェクト・インスタンスの追加

5.10.4.3項「データ・モデルでアクティブなマスター/ディテール調整を有効化する方法」で説明した内容と同じ手順に従い、次のビュー・オブジェクト・インスタンスをSRServiceアプリケーション・モジュールのデータ・モデルに追加し、図7-15に示すマスター/ディテール・ビュー・オブジェクトの階層を作成します。

  • 最初に「データ・モデル」ツリーで既存のServiceRequestsビュー・オブジェクト・インスタンスを選択してから、「選択可能なビュー・オブジェクト」リストでServiceRequestsの子として表示されているServiceHistoriesビュー・オブジェクトをServiceHistoriesという名前のディテール・インスタンスとして追加します。

  • 最初に「データ・モデル」ツリーで既存のStaffListビュー・オブジェクト・インスタンスを選択してから、「選択可能なビュー・オブジェクト」リストでStaffListの子として表示されているServiceRequestsビュー・オブジェクトをAssignedServiceRequestsという名前のディテール・インスタンスとして追加します。

  • 最初に「データ・モデル」ツリーで新しいAssignedServiceRequestsビュー・オブジェクト・インスタンスを選択してから、「選択可能なビュー・オブジェクト」リストでServiceRequestsの子として表示されているServiceHistoriesビュー・オブジェクトをAssignedServiceHistoriesという名前のディテール・インスタンスとして追加します。

図7-15 エンティティ・ベースのビュー・オブジェクトの編集可能な結果を示すBusiness Component Browser

Business Component Browserの「データ・モデル」ページの図

7.5.3 エンティティ・ベースのビュー・オブジェクトの対話的テスト方法

図7-15に示すように、SRServiceアプリケーション・モジュールのデータ・モデルを設定した場合、そのテスト方法は次のとおりです。

エンティティ・ベースのビュー・オブジェクトのテスト方法:

  1. アプリケーション・ナビゲータでアプリケーション・モジュールを選択し、ポップアップ・メニューから「テスト」を選択します。

  2. Business Component Browserの「接続」ダイアログで「接続」をクリックし、テスト用としてデフォルトのSRServiceLocal構成を使用します。


注意:

デフォルトでは、アプリケーション・モジュールには、AppModuleNameLocalという名前のデフォルトのローカル構成のみがあります。アプリケーション・モジュール用として別の追加構成を作成してある場合、かわりにこれらの1つを使用してアプリケーション・モジュールをテストするには、「接続」をクリックする前に、「接続」ダイアログのビジネス・コンポーネント構成ドロップダウン・リストから目的の構成を選択します。

7.5.4 エンティティ・ベースのビュー・オブジェクトを対話的にテストするときに行われる処理

Business Component Browserを起動する場合、JDeveloperでは別のプロセスでテスター・ツールが起動され、Business Component Browserが表示されます。図7-16に示すとおり、左側に表示されるツリーには、データ・モデルのビュー・オブジェクト・インスタンスの階層が表示されます。また、このツリーには、マスターで現在の行が変更されたときにマスター/ディテールをアクティブに調整するビュー・リンク・インスタンスを示す、マスター・ビュー・オブジェクト・インスタンスとディテール・ビュー・オブジェクト・インスタンスの間の追加ノードが含まれます。

図7-16 ビジネス・コンポーネント・テスター内のSRServiceデータ・モデル

ビジネス・コンポーネント・テスター内のデータ・モデルの図

ツリーでHistoryLinesForRequest2ビュー・リンク・インスタンスをダブルクリックすると、これまでのテスト・セッションで実行されていなかった場合はマスター・オブジェクトが実行され、図7-17に示すように、マスター/ディテール・テスター・パネルが表示されます。ビュー・オブジェクト・ノード上に表示される追加ポップアップ・メニュー項目では、テスター・パネルの削除、およびその他タスクの実行のため、必要に応じて問合せを再実行できます。5.5項「Business Component Browserを使用したビュー・オブジェクトのテスト」でBusiness Component Browserを使用したときも、類似するマスター/ディテール・パネルを使用しました。問合せ結果は表示およびスクロールできます。ここでの重要な違いの1つは、エンティティ・ベースのビュー・オブジェクトの使用による直接的な結果です。読取り専用データを示す無効なUIコントロールが表示されるかわりに、編集可能なフィールドが表示され、作成、挿入、更新、検証、コミットおよびロールバックを自由に実行できます。

図7-17 テスターによって自由に編集されるエンティティ・ベースのビュー・オブジェクト・インスタンス

この図は、テスターでのオブジェクトの編集方法を示しています。

マスター/ディテール階層の様々なレベルを参照する、同時に複数のテスター・パネルを開く、また「ウィンドウに結果を表示」ツールバー・ボタンでフレームからタブを独立した別ウィンドウに表示させるなど、複数のビュー・オブジェクトのデータを同時に表示する操作を試してみてください。

7.5.5 エンド・ユーザーによるアプリケーション・モジュールのデータ・モデルとの対話のシミュレーション

Business Component Browserを使用すると、独自のカスタム・ユーザー・インタフェースを作成する前に、エンド・ユーザーによるアプリケーション・モジュールのデータ・モデルとの対話をシミュレートできます。UIページを作成した後でも、Business Component Browserは問題が発生した場合の診断に役立ちます。Business Component Browserで問題を再現して、問題がアプリケーションのビューまたはコントローラ・レイヤーにあるのか、あるいはビジネス・サービス・レイヤーのアプリケーション・モジュール自体にあるのかを確認できます。

図7-17に示すマスター/ディテール・テスター・ページを使用して、アプリケーションの様々な機能領域をテストできます。

7.5.5.1 マスター/ディテール調整のテスト

ツールバーのナビゲーション・ボタンを使用して、現在のサービス・リクエストのサービス履歴行が正しく調整されているかどうかを確認できます。

7.5.5.2 UIコントロール・ヒントのテスト

テスト・パネルに表示されるプロンプトにより、属性ごとにわかりやすい「ラベル・テキスト」コントロール・ヒントが定義されているかどうかを確認できます。たとえば、ServiceRequestsビュー・オブジェクト・インスタンスのRequestDate属性のプロンプトは、「リクエスト日」です。RequestDateフィールドの編集フィールドの上にマウスを置くと、定義されていればツールチップのコントロール・ヒント・テキストが表示されます。表示されるツールチップの内容は「The date on which the service request was created」です。これは、6.5.1項「属性のコントロール・ヒントの追加方法」でエンティティ・オブジェクトに設定したヒントです。このことは、ServiceRequestsビュー・オブジェクトには特に新しいコントロール・ヒントを指定していないため、エンティティ・ベースのビュー・オブジェクト属性には基礎となるエンティティ・オブジェクト属性からコントロール・ヒントが継承されたことを示しています。

7.5.5.3 エンティティ・オブジェクトの慣用名を参照するビュー・オブジェクトのテスト

データをスクロールするか、「ビュー基準の指定」ツールバー・ボタンを使用して、まだ割り当てられていないサービス・リクエストが正しく表示されるかどうかを検証できます。外部結合を使用するよう問合せのWHERE句を正しく変更すると、これらの行は想定どおり表示されます。

AssignedTo属性を別の技術者のユーザーIDに変更して、Business Component BrowserでStaffListビュー・オブジェクト・インスタンスをダブルクリックして有効なスタッフのユーザーIDを参照すると、対応する参照情報が自動的に更新され、新しい技術者が反映されているかどうかを検証できます。

ServiceRequestsのサンプル行のProductNameフィールドおよび2つの「電子メール・アドレス」フィールドの値を調べて、対応するわかりやすい製品名と、リクエストを作成した顧客およびリクエストが割り当てられる技術者双方の電子メール・アドレスを確認できます。また、顧客の電子メール・アドレスと技術者の電子メール・アドレスの両方に「電子メール・アドレス」Userエンティティ・オブジェクトから「ラベル・テキスト」ヒントが継承されているという問題にも気付きます。これでは、エンド・ユーザーには電子メール・アドレスがどちらのものであるかがわかりません。

このように、技術者の電子メール・アドレスと顧客の電子メール・アドレスの両方に同じ「電子メール・アドレス」ラベルが継承されて表示されるという問題に対処するには、ServiceRequestsビュー・オブジェクトを編集し、これらの両方についてビュー・オブジェクト・レベルで「ラベル・テキスト」コントロール・ヒントを定義します。この場合、「ラベル・テキスト」ヒントを、TechnicianEmail属性は「Technician Email Address」CustomerEmail属性は「Customer Email Address」に設定します。ビュー・オブジェクト・レベルで定義したこれらのコントロール・ヒントにより、基礎となるエンティティ・オブジェクトから一般的に継承されるコントロール・ヒントがオーバーライドされたかどうかは、Business Component Browserを使用して検証します。

7.5.5.4 ビジネス・ドメイン・レイヤーの検証のテスト

Closedサービス・リクエストのStatus属性値をReopenedに変更してみてください。フィールド値以外のものを指定しようとすると、次の例外が発生します。

(oracle.jbo.AttrSetValException) The status must be Open, Pending, or Closed

6.7項「宣言的な検証規則の使用」で定義した他の簡単な宣言的検証規則に基づいて、ProblemDescription値をスペースで囲まれた単語urgentを含めて更新し、エラーの表示を試行できます。

(oracle.jbo.AttrSetValException)
Problem Description cannot contain the word urgent

最後に、ProdIdの値として1234を入力してレンジ検証規則に違反し、次のメッセージの表示を試行できます。

(oracle.jbo.AttrSetValException) Valid product codes are between 100 and 999

ツールバーで「ロールバック」ボタンをクリックし、データを以前の状態に戻してください。

7.5.5.5 代替言語のメッセージ・バンドルおよびコントロール・ヒントのテスト

Business Component Browserの起動時、「接続」ダイアログの「プロパティ」タブを開いて、デフォルトのロケール設定をオーバーライドして次のように変更できます。

  • jbo.default.country = IT

  • jbo.default.language = it

このようにプロパティを設定して、ServiceRequestエンティティ・オブジェクトのコントロール・ヒントのイタリア語の翻訳が正しく配置されているかどうかを確認できます。特に、StatusRequested OnおよびProblemラベルがそれぞれStatoAperto IlおよびProblemaラベルになっています。また、RequestDateのフォーマットが03/12/2006 16:55などの値から12/03/2006 16:55に変更されています。

7.5.5.6 行の作成およびデフォルト値の生成のテスト

ServiceRequestsビュー・オブジェクトに対してツールバーの「行の作成」ボタンをクリックし、新しい空白行を作成します。宣言的なデフォルト値があるフィールドの場合、その値が空白行に表示されます。DBSequence値を持つSvrId属性の場合、新しい行に一時的な負の数で読取り専用として表示されます。すべての必須フィールドに入力した後(ProdIdフィールドには100Requested Byフィールドには300を入力)、「コミット」ボタンをクリックしてトランザクションをコミットします。コミットが成功した後、トリガーによって割り当てられた実際の主キーがSvrIdフィールドに表示されます。

7.5.5.7 新しいディテール行の外部キーが正しいかどうかのテスト

既存のサービス・リクエストに新しいサービス履歴行を追加しようとすると、ビュー・リンクによって新しいServiceHistories行のSvrIdの外部キー属性値が現在のマスター・サービス・リクエスト行に自動的に設定されることがわかります。

7.6 エンティティ・ベースのビュー・オブジェクトへの計算済および一時属性の追加

ビュー・オブジェクトには、基礎となるエンティティ・オブジェクトにマップされた属性以外にも、エンティティ・オブジェクトの属性値にマップされない計算属性を組み込むこともできます。計算属性としては、次の2種類があります。

この項では、この両方の種類の追加方法について説明します。最初にLastCommaFirstという名前のSQL計算属性、次にFirstDotLastという名前の一時計算属性をStaffListビュー・オブジェクトに追加する方法について説明します。最後に、ビュー・オブジェクトには、エンティティ・オブジェクト・レベルではそれ自体が一時属性である、エンティティにマップされた属性を組み込むことができることを示し、サポートされているすべての組合せが明確であることを確認します。

7.6.1 SQL計算済属性の追加方法

エンティティ・ベースのビュー・オブジェクトへのSQL計算属性の追加方法:

  1. ビュー・オブジェクト・エディタで「属性」ページを開き、「新規」をクリックします。

  2. LastCommaFirstなどの属性名を入力します。

  3. Javaの「属性」「型」Stringなどの適切な値に設定します。

  4. 「列またはSQLにマップ済」チェック・ボックスを選択します。

  5. 「式」フィールドにLAST_NAME||', '||FIRST_NAMEなどのSQL式を入力します。

  6. 属性名と一致するようにSQL列の別名を変更するかどうかを検討します。

  7. データベースの「問合せ列」「型」を確認し、必要に応じて長さ(または精度/スケール)を調整します。

  8. 「OK」をクリックし、属性を作成します。

図7-18 新しいSQL計算属性の追加

「新規ビュー・オブジェクト属性」ダイアログの図

7.6.2 SQL計算済属性を追加するときに行われる処理

SQL計算属性を追加し、ビュー・オブジェクト・エディタを終了すると、ビュー・オブジェクトのXMLコンポーネント定義が更新され、新しい属性が反映されます。XMLでは、LastNameのようなエンティティにマップされた属性は次のようになりますが、ほとんどのプロパティはこの属性がマップされた、基礎となるエンティティ属性から継承されます。

<ViewAttribute
   Name="LastName"
   IsNotNull="true"
   EntityAttrName="LastName"
   EntityUsage="User1"
   AliasName="LAST_NAME" >
</ViewAttribute>

一方、SQL計算属性のViewAttributeタグは、次のようになります。想定どおり、このタグにはEntityUsageまたはEntityAttrNameプロパティはなく、次のようにデータ型情報とともにSQL式が含まれます。

<ViewAttribute
   Name="LastCommaFirst"
   IsUpdatable="false"
   IsPersistent="false"
   Precision="62"
   Type="java.lang.String"
   ColumnType="VARCHAR2"
   AliasName="FULL_NAME"
   Expression="LAST_NAME||&#39;, &#39;||FIRST_NAME"
   SQLType="VARCHAR" >
</ViewAttribute>

注意:

&39;は、アポストロフィを示すXML文字参照で、数値ASCIIコードの39(10進)によって参照します。XMLで同じように構成する必要があるその他のリテラル・テキスト文字には、小なり、大なりおよびアンパサンド文字があります。

7.6.3 一時属性の追加方法

エンティティ・ベースのビュー・オブジェクトへの一時属性の追加方法:

  1. ビュー・オブジェクト・エディタで「属性」ページを開き、「新規」をクリックします。

  2. FirstDotLastなどの属性名を入力します。

  3. Java「属性」「型」Stringに設定します。

  4. 「列またはSQLにマップ済」チェック・ボックスは未チェックのままにします。

  5. 「OK」をクリックし、属性を作成します。

図7-19 新しい一時属性の追加

「新規ビュー・オブジェクト属性」ダイアログの図

7.6.3.1 ビュー・オブジェクトへのエンティティにマップされた一時属性の追加

エンティティ・ベースのビュー・オブジェクトに一時エンティティ・オブジェクト属性を追加するには、ビュー・オブジェクト・エディタの「エンティティ・オブジェクト」ページでエンティティにエンティティ・オブジェクトの慣用名があることを最初に確認します。次に、「属性」ページに移動し、目的の属性を「使用可能」リストから「選択済」リストに移動します。これらの手順を使用して、FullName計算属性をUserエンティティ・オブジェクトからStaffListビュー・オブジェクトに追加できます。

これら3つの属性をStaffListビュー・オブジェクト・リストに追加した後、Business Component Browserを使用してSRServiceデータ・モデルをテストする場合、図7-20のように、その結果を確認できます。

図7-20 3種類の計算属性があるStaffListビュー・オブジェクト

テスター内のStaffListビュー・オブジェクトの図

7.6.4 一時属性の追加時に発生する処理

一時属性を追加し、ビュー・オブジェクト・エディタを終了すると、ビュー・オブジェクトのXMLコンポーネント定義が更新され、新しい属性が反映されます。XMLでは、一時属性のViewAttributeタグは、SQL計算属性の場合と類似していますが、Expressionプロパティがありません。

7.6.5 ビュー行クラスへの計算実行用Javaコードの追加

一時属性は、データ値のプレースホルダです。一時属性の「更新可能」プロパティを「新規の間」または「常に」に変更すると、エンド・ユーザーは属性値を入力できるようになります。一時属性に計算値を表示する場合は通常、「更新可能」プロパティを「なし」に設定し、値を計算するカスタムJavaコードを記述します。

ビュー・オブジェクトに一時属性を追加した後、この属性を計算済一時属性にするには、次のようにする必要があります。

  • ビュー・オブジェクト・エディタの「Java」ページでカスタム・ビュー行クラスを有効にし、アクセッサ・メソッドの生成を選択します。

  • 一時属性のアクセッサ・メソッドの内部で、計算済値を戻すJavaコードを記述します。

たとえば、StaffListRowImpl.javaビュー行クラスの生成を有効化した後、計算値を戻すJavaコードは、次のようにgetLastCommaFirst()メソッド内にあります。

// In StaffListRowImpl.java
public String getFirstDotLast() {
  // Commented out this original line since we're not storing the value
  // return (String) getAttributeInternal(FIRSTDOTLAST);
  return getFirstName().substring(0,1)+". "+getLastName();
}

注意:

26.8項「属性の自動再計算の実装」では、計算属性が依存している属性値の1つが変更されたときに、計算属性をエンティティ行レベルで再計算するためのコーディング技術について説明します。これと非常に類似した方法をビュー行レベルでも使用すると、計算済ビュー・オブジェクト属性を自動的に再計算できます。

7.6.6 一時属性について

ビュー・オブジェクトには、実行時に問合せのSELECTリストにSQL計算属性のSQL式が含まれます。この式はデータベースが評価し、結果を問合せ内のその列の値として戻します。この値は、問合せを実行するたびに再評価されます。

7.7 実行時にビュー・オブジェクトとエンティティ・オブジェクトが連携する仕組み

ビュー・オブジェクトとエンティティ・オブジェクトにより、すべてのエンタープライズ・アプリケーション開発者が実行する必要のある次の2つの重要なジョブが単独で簡略化されます。

エンティティ・ベースのビュー・オブジェクトは、エンド・ユーザーが表示または変更する必要のあるデータを任意に選択して問い合せることができます。エンド・ユーザーによる変更が可能なデータは、再使用可能なビジネス・ドメイン・レイヤーによって検証および保存されます。開発者として指定する次のような主要な構成要素は、それらを指定するユーザーのみが把握しています。

これらの内容により、アプリケーションが一意のものになります。実装の残りの詳細部分は、エンティティ・ベースのビュー・オブジェクトに組み込まれた機能によって処理されます。これまで、Business Component Browserでエンティティ・ベースのビュー・オブジェクトを試用し、その利点を目にしてきましたが、次に必要なのは、その正確な仕組みを理解することです。この項では、エンティティ・ベースのビュー・オブジェクトを介してデータを取得および変更するシナリオについて段階的に説明し、その背景で行われている処理のうち、注目に値するものに焦点を当てます。ただし、詳しい内容に入る前に、行キーの背景や、トランザクションでエンティティ・キャッシュが果す役割について多少理解する必要があります。その後、エンティティ・ベースのビュー・オブジェクトについて詳しく理解する準備が整います。

7.7.1 ビュー行またはエンティティ行ごとに関連付けられたキー

図7-21に示すとおり、ビュー行を操作する場合、oracle.jboパッケージ内のRowインタフェースを使用します。このインタフェースには、getKey()と呼ばれるメソッドが含まれます。このメソッドを使用して、任意の行を識別するKeyオブジェクトにアクセスできます。ここで、Rowインタフェースはoracle.jbo.serverパッケージ内のEntityインタフェースによって拡張されます。このような関係は、エンティティ行という用語が最適であることの具体的な理由となります。エンティティ行がビジネス・ロジックのカプセル化やデータベース・アクセスの処理のための追加機能をサポートしている場合でも、エンティティ行はそのままRowとして扱うことができます。

前述のとおり、ビュー行とエンティティ行はどちらも単一属性または複数属性のキーをサポートしているため、特定のRowに関連付けられたKeyオブジェクトにより、キーを構成するすべての属性がカプセル化されます。Keyオブジェクトがある場合は、任意の行セットに対してfindByKey()メソッドを使用して、Keyオブジェクトに基づいて行を検索できます。

図7-21 識別キーの取得をサポートしているビュー行またはエンティティ行

この図は、行が識別キーを取得する方法を示しています。

注意:

エンティティ・ベースのビュー・オブジェクトを定義する場合、デフォルトでは、すべてのエンティティ・オブジェクトの慣用名の主キー属性は、「キー属性」プロパティの設定がtrueとしてマークされます。参照エンティティ・オブジェクトの慣用名のキー属性では、「キー属性」プロパティを後で無効にすることをお薦めします。更新可能なエンティティ・オブジェクトの慣用名の主キーに関連するビュー・オブジェクト属性は、複合ビュー行キーの一部である必要があるため、「キー属性」プロパティは無効にできません。

7.7.2 エンティティ・キャッシュがトランザクションで果す役割

アプリケーション・モジュールは、論理的な作業ユニットのトランザクション・コンテナです。アプリケーション・モジュールは実行時に、自分で指定した名前付き構成の情報を使用してデータベース接続を確立し、トランザクション管理を別のTransactionオブジェクトに委譲します。論理的な作業ユニットには、様々なタイプの複数のエンティティ行の検索や変更が含まれる場合があるため、Transactionオブジェクトには、現在のユーザーのトランザクションに関連するエンティティ行を保持するための作業領域として、エンティティ・キャッシュが用意されています。各エンティティ・キャッシュには単一のエンティティ・タイプの行が含まれるため、Userエンティティ・オブジェクトとServiceHistoryエンティティ・オブジェクトの両方が関連するトランザクションでは、これらのエンティティ行の作業用コピーが2つの個別キャッシュに保持されます。

エンティティ・オブジェクトの関連エンティティ定義を使用して、アプリケーション・モジュールで既存のエンティティ行を検索し、変更するためのコードを記述できます。図7-22に示すとおり、ServiceRequestエンティティ・オブジェクトのエンティティ定義に対してfindByPrimaryKey()をコールすると、そのキーを持つ行を取得できます。この行がまだエンティティ・キャッシュ内にない場合、エンティティ・オブジェクトによってこの行をデータベースから取得するための問合せが実行されます。この問合せにより、基礎となる表からエンティティ・オブジェクトのすべての持続属性が選択され、エンティティ・オブジェクトの主キー属性に対応する列に適したWHERE句を使用して行が検索されます。同一トランザクション中に同じエンティティ行をキーによって引き続き検索する場合、この行をキャッシュ内で検索することにより、データベースへのトリップを回避します。特定のエンティティ・キャッシュでは、主キーによってエンティティ行が索引付けされます。これにより、キャッシュ内でのエンティティ行の検索処理が速くなります。

アソシエーション・アクセッサ・メソッドを使用して関連エンティティ行にアクセスする場合、これらの行はエンティティ・キャッシュからも取得されます。また、キャッシュ内にない場合はデータベースから取得されます。最後に、エンティティ・キャッシュは、新しいエンティティ行が保存されるまで保持される場所でもあります。つまり、createInstance2()メソッドを使用してエンティティ定義に対して新しいエンティティ行を作成する場合、このエンティティ行はエンティティ・キャッシュに追加されます。

図7-22 ServiceRequestトランザクション中にServiceRequestエンティティ・キャッシュに格納されるエンティティ行

この図は、行をエンティティ・キャッシュに格納する方法を示しています。

エンティティ行を作成、変更または削除すると、このエンティティ行はトランザクションの保留中の変更リストに自動的に登録されます。Transactionオブジェクトに対してcommit()をコールすると、保留中の変更リストが処理され、まだ無効の新規または変更済エンティティ行があるかどうかが検証されます。保留リスト内のエンティティ行がすべて有効である場合、Transactionにより、データベースのSAVEPOINTが発行され、データベースへのエンティティ行の保存が調整されます。すべて正常に完了すると、最終的にデータベースのCOMMIT文が発行されます。問題が発生した場合、TransactionによってROLLBACK TO SAVEPOINTが実行され、ユーザーはエラーを修正するか、または再試行できるようになります。

アプリケーション・モジュールで使用されるTransactionオブジェクトは、単一のエンド・ユーザー・トランザクション用のエンティティ行の作業セットです。設計上、このオブジェクトは共有のグローバル・キャッシュではありません。データベース・エンジン自体は、複数の同時ユーザーを対象とした非常に効率的な共有グローバル・キャッシュです。ADF Business Componentsでは、30年間も行われてきたデータベースの共有グローバル・キャッシュ機能の微調整を繰り返すのではなく、その結果が意識的に採用されています。refresh()メソッドをコールすると、データベースから単一のエンティティ・オブジェクトのデータをいつでもリフレッシュできます。Transactionオブジェクトに対してsetClearCacheOnCommit()またはsetClearCacheOnRollback()を使用すると、コミットまたはロールバック時にエンティティ・キャッシュを消去するかどうかを制御できます。デフォルトはそれぞれfalsetrueです。また、TransactionオブジェクトにはclearEntityCache()メソッドも用意されており、このメソッドを使用して、特定のエンティティ・タイプ(またはすべてのタイプ)のエンティティ行をプログラムで消去できます。エンティティ・キャッシュを消去すると、そのタイプのエンティティ行が主キーによって次に検出されるとき、データベースから最新の状態で取得されるか、次の各項で説明するように、エンティティ・ベースのビュー・オブジェクトによって取得されるようになります。

7.7.3 明確に区別されたデータソースとデータ・シンクの役割を統合するメタデータ

主キーによるエンティティ行の検索、およびアソシエーション・アクセッサを介した関連エンティティのナビゲーションの次は、エンティティ・ベースのビュー・オブジェクトを使用してこのような処理を行う方法について説明します。エンティティ・ベースのビュー・オブジェクトでは、ビュー・オブジェクトとエンティティ・オブジェクトが果す役割が明確に区別されています。

  • ビュー・オブジェクトは、データソースです。SQLを使用してデータを取得します。

  • エンティティ・オブジェクトは、データ・シンクです。データの変更を検証および保存します。

ビュー・オブジェクトとエンティティ・オブジェクトの果す役割は明確に区別されているため、再使用可能なエンティティ・オブジェクトを変更せずに、大量の異なるビュー・オブジェクトを作成できます。つまり、ユーザー・インタフェースで求められる、アプリケーションごとに異なる方法でデータを計画、フィルタ、結合およびソートできます。実際、一部の大規模な開発組織では、エンティティ・オブジェクトのコア・ビジネス・ドメイン・レイヤーを担当するチームは、エンド・ユーザー要件に対応するための特定のアプリケーション・モジュールおよびビュー・オブジェクトを作成したチームとは完全に区別されている場合があります。この非常に柔軟な共生関係は、エンティティ・ベースのビュー・オブジェクトにより、SELECTリストの列を1つ以上の基礎となるエンティティ・オブジェクトに関連付ける方法をカプセル化するメタデータによって実現されます。

ここで、未処理および保留中のサービス・リクエストを簡単に確認できるページをエンド・ユーザーから要求されるという新しい要件が発生したとします。エンド・ユーザーは、サービス・リクエストID、ステータス、問題の説明、リクエストの解決を担当する技術者、リクエストが未処理であった日数のみの表示を求めています。また、ステータスと割り当てられた技術者を更新可能にする必要があります。図7-23に、この要件に対応したOpenProblemsAndAssigneesという名前の新しいエンティティ・ベースのビュー・オブジェクトを示します。

図内の点線は、問合せのSELECTリストの列を、ビュー・オブジェクトで使用されるエンティティ・オブジェクトの属性にマップする、ビュー・オブジェクトのXMLコンポーネント定義で取得されるメタデータを示します。

ビュー・オブジェクトとその問合せについては、次の点に注意してください。

  • 前述の例にある、割当て済技術者に関連するアソシエーションに基づいて、プライマリ・エンティティ・オブジェクトの慣用名(ServiceRequest)のデータとセカンダリの参照エンティティ・オブジェクトの慣用名(User)のデータが結合されます。

  • 外部結合のServiceRequest.ASSIGNED_TO = Technician.USER_ID (+)が使用されます。

  • SQL式のCEIL(SYSDATE - TRUNC(REQUEST_DATE))に基づいてSQL計算属性DaysOpenが含まれます。

図7-23 SQL問合せとエンティティ属性マッピング・メタデータをカプセル化するビュー・オブジェクト

ビュー・オブジェクトが問合せとメタデータをカプセル化する方法の図

7.7.4 ビュー・オブジェクトによる問合せ実行時に発生する処理

OpenProblemsAndAssigneesのインスタンスを同じ名前でSRServiceのデータ・モデルに追加した後、問合せの実行時に行われる処理を確認できます。読取り専用ビュー・オブジェクトの場合と同様、エンティティ・ベースのビュー・オブジェクトが標準のJDBC(Java Database Connectivity)APIを使用してデータベースにSQL問合せを直接送信し、データベースが結果セットを生成します。ただし、読取り専用ビュー・オブジェクトの場合とは異なり、エンティティ・ベースのビュー・オブジェクトは、データベースの結果セットの各行を取得するときに、関連付けられているエンティティ・オブジェクトの慣用名に基づいて行属性を分割します。この分割の際、各ビュー・オブジェクトのエンティティ・オブジェクトの慣用名に適切なタイプのエンティティ・オブジェクト行が作成され、問合せによって取得された関連属性がこれらのエンティティ行に移入され、このようなエンティティ行がそれぞれ個別のエンティティ・キャッシュに格納されます。これにより、データの複製コピーを格納するかわりに、ビュー行が構成元のエンティティ行部分を指し示すだけですみます。図7-24に示すとおり、結果セットで強調表示された行は、主キー306Userエンティティ行と、主キー112ServiceRequestエンティティ行に分割されています。SQL計算属性DaysOpenはエンティティ・オブジェクトに関連付けられていないため、その値はビュー行に直接格納されます。

前にfindByPrimaryKey()を使用してキャッシュに取り込んだServiceRequestエンティティ行には、ServiceRequestエンティティ・オブジェクトのすべての属性が含まれていました。一方、OpenProblemsAndAssigneesの問合せ結果の行を分割して作成されたServiceRequestエンティティ行には、問合せ結果に表れる属性の値のみが含まれます。このエンティティ行には、属性の完全セットは含まれません。このようにエンティティ行が部分的に移入されることにより、実行時のパフォーマンスが大幅に最適化されます。

一般的なエンタープライズ・アプリケーションで変更される行と比較して、取得される行の比率が非常に高くなるため、表示する必要がある属性のみをメモリーに読み込むことにより、常にすべての属性をメモリーに読み込む場合よりもメモリーを大幅に節約できます。

最後に、サービス・リクエストについて問い合せた結果の行114には、技術者が割り当てられていません。このため、ビュー行では、Userエンティティ・オブジェクトについてのエンティティ行部分がnullになります。

図7-24 エンティティ・キャッシュのエンティティ行に分割されるビュー行

この図は、ビュー行を分割する方法を示しています。

このような方法で問合せ結果のデータを基礎となるエンティティ行の構成要素部分に分割すると、現在のトランザクションが変更された場合、ユーザーに関してUserId = 306を使用して問い合せた結果のデータを含むすべての行で整合している結果が表示されるという、重要な利点が得られます。つまり、あるビュー・オブジェクトでユーザー306Email属性を変更できる場合、ユーザー306Email属性を表示する任意のエンティティ・ベースのビュー・オブジェクトのすべての行が即座に更新され、変更が反映されます。ユーザー306に関連付けられたデータがUserエンティティ・キャッシュで主キー306を持つエンティティ行に格納されるのは1回のみであるため、ユーザーのEmail属性を問い合せたビュー行はどれも、この単一のエンティティ行を指し示すことになります。

このような実装の詳細は、ビュー・オブジェクトの行セット内の行を操作するクライアントに対しては完全に非表示にされます。Business Component Browserの場合と同様、クライアントはビュー行を操作して属性を取得し、設定する際、これらの属性がその背後でどのようにエンティティ行に関連付けられているかには気付きません。

7.7.5 ビュー行属性の変更時の処理

たとえば、前述のOpenProblemsAndAssignees結果セットには、サービス・リクエスト112に関連付けられた行が含まれます。クライアントがサービス・リクエスト112のステータスの値をClosedに更新しようとすると、最終的にはビュー行でsetStatus("Closed")メソッドがコールされます。図7-25に、基礎となるエンティティ行に応じてこのビュー行属性の変更を自動的に調整する手順を示します。

  1. クライアントがStatus属性の値をClosedに設定しようとします。

  2. StatusServiceRequestエンティティ・オブジェクトの慣用名にマップされた属性であるため、ビュー行は、ServiceRequestエンティティ・キャッシュ内で主キー112を持つ、基礎となるエンティティ行に設定されている属性を委譲します。

  3. ServiceRequestエンティティ・オブジェクトのStatus属性に対する属性レベルの検証規則が評価され、検証が失敗した場合は操作に失敗します。

    Status属性の検証規則によってRequestDate属性がプログラム的に参照されるとします(たとえば、ServiceRequestは開始日と同日に処理済にはできないというビジネス・ルールを適用する必要がある場合など)。RequestDateは、問合せによって取得されたServiceRequest属性ではないため、ServiceRequestエンティティ・キャッシュ内の部分的に移入されたエンティティ行にはありません。

  4. ビジネス・ルールでエンティティ・オブジェクトのすべての属性を常に参照可能にするため、エンティティ・オブジェクトはこの状況を検出し、主キーによって変更されるエンティティ行についてServiceRequestエンティティ・オブジェクト属性のセット全体を失敗にします(主キーはビュー・オブジェクトに適用される各エンティティ・オブジェクトの慣用名に表示されます)。

  5. 属性レベルの検証がすべて成功した後、エンティティ・オブジェクトでは、最初の属性の変更を許可する前にSERVICE_REQUESTS表でこの行にロックをかけます。

  6. この行をロックできた場合、行内のStatus属性の設定が成功し、エンティティ行でこの値が変更されます。


注意:

行のロック方法は、jbo.locking.mode構成プロパティによって制御されます。このデフォルト値はpessimisticであり、この動作はここで説明する手順に対応しています。pessimisticロック・モードの場合、エンティティ・キャッシュで行に対する変更を許可する前に行のロックを可能にする必要があります。Webアプリケーションの場合は通常、このプロパティはoptimisticに設定されており、トランザクションのコミット時まで行はロックされません。

図7-25 エンティティに委譲されるビュー行属性の更新

この図は、ビュー行がエンティティに委譲する方法を示しています。

7.7.6 外部キー属性の変更時に発生する処理

ユーザーがサービス・リクエスト112に割り当てられている技術者も更新すると、また別の処理が行われます。リクエストは現在、ユーザーID 306を持つvpatabalに割り当てられています。ここで、エンド・ユーザーがAssignedTo属性を300に設定し、リクエストをskingに再度割り当てるとします。図7-26に示すように、背後では次の処理が行われます。

  1. クライアントがAssignedTo属性の値を300に設定しようとします。

  2. AssignedToServiceRequestエンティティ・オブジェクトの慣用名にマップされた属性であるため、ビュー行は、ServiceRequestエンティティ・キャッシュ内で主キー112を持つ、基礎となるエンティティ行に設定されている属性を委譲します。

  3. ServiceRequestエンティティ・オブジェクトのAssignedTo属性に対する属性レベルの検証規則が評価され、検証が失敗した場合は操作に失敗します。

  4. この行はすでにロックされているため、行内のAssignedTo属性の設定が成功し、エンティティ行でこの値が変更されます。

  5. ServiceRequestエンティティ・オブジェクトの慣用名のAssignedTo属性はUserエンティティ・オブジェクトに対するTechnicianという名前の参照エンティティ・オブジェクトの慣用名に関連付けられているため、外部キー値をこのように変更すると、ビュー行で、ユーザー306の現在のエンティティ行部分が、新しいUserId = 300に対応するエンティティ行に置き換えられます。これにより、サービス・リクエスト112のビュー行がskingのエンティティ行を指し示すよう効率的に変更できるため、ビュー行のEmailの値が更新され、この新しく割り当てられた技術者の参照情報が正しく反映されます。

図7-26 外部キーの更新後に新しいエンティティを指し示すビュー行

この図は、更新後に新しいエンティティを指し示す外部キーを示しています。

7.7.7 データ問合せの再実行時の処理

ビュー・オブジェクトの問合せを再実行する場合、デフォルトでは、最新の結果セットを読み込むための準備中、現在の行セット内のビュー行は無視されます。ただし、このビュー・オブジェクト操作はエンティティ・キャッシュには直接影響しません。この場合、ビュー・オブジェクトがデータベースに対してSQLを送信することによってプロセスが再開され、データベースの結果セット行が取得され、エンティティ行部分に分割されます。


注意:

通常、問合せを再実行する場合、この操作は最新のデータベース情報を確認するためのものです。かわりにビュー・オブジェクトの問合せ対象をキャッシュ内の既存のエンティティ行のみに制限する場合や、すでにビュー・オブジェクトの行セット内にある既存の行に制限して、データベースのラウンドトリップを回避する場合、その方法は、27.5項「行セットのメモリー内でのソート処理とフィルタ処理の実行」を参照してください。

7.7.7.1 問合せの再実行時にリフレッシュされるエンティティ・キャッシュ内の変更なしの属性

問合せの再実行時におけるこのエンティティ行分割プロセスの一環として、エンティティ行の属性が変更されていない場合、エンティティ・キャッシュ内の値が更新され、新しい問合せ結果の値が反映されます。

7.7.7.2 問合せの再実行時にそのまま残されるエンティティ・キャッシュ内の変更済属性

現在のトランザクションでエンティティ行属性の値が変更されている場合、問合せ再実行時のエンティティ行の分割プロセスでは、値はリフレッシュされません。現在のトランザクションでコミットされない変更はそのまま残されるため、エンド・ユーザーの論理作業ユニットは保持されます。任意のエンティティ属性値の場合と同様、これらの保留中の変更は、変更済エンティティ行を参照する任意のエンティティ・ベースのビュー・オブジェクト行に継続して表示されます。

図7-27に、このシナリオを示します。現在のトランザクションで保留中の変更について、ユーザーがServiceRequestsビュー・オブジェクト・インスタンスを使用する別のページにドリルダウンし、サービス・リクエスト112に関する詳細を取得するとします。このビュー・オブジェクトには4つのエンティティ・オブジェクトの慣用名、つまり、プライマリServiceRequestの慣用名と、ProductUser(技術者)およびUser(顧客)の3つの参照の慣用名があります。問合せ結果がエンティティ行に分割されると、前のOpenProblemsAndAssigneesビュー行で変更されたものと同じServiceRequestエンティティ行を指し示すことになります。つまり、エンド・ユーザーには保留中の変更が正しく表示され、サービス・リクエストはSteven Kingに割り当てられています。

図7-27 エンティティ・キャッシュにマージされる、オーバーラップする個別ビュー・オブジェクトのエンティティ属性セット

個別ビュー・オブジェクトをキャッシュにマージする方法の図

7.7.7.3 問合せの再実行時にマージされる、オーバーラップする属性のサブセット

図7-27はまた、ServiceRequestsビュー・オブジェクトの問合せにより、OpenProblemsAndAssigneesの場合とは異なる、ユーザーに関する参照情報のサブセットを取得する状況も示しています。OpenProblemsAndAssigneesビュー・オブジェクトはユーザーのEmailを問い合せていましたが、ServiceRequestsはユーザーのFirstNameおよびLastNameを問い合せています。この図は、このシナリオで実行時に行われる処理を示しています。取得した行の分割時に、すでにキャッシュ内にある、部分的に移入されたエンティティ行とは異なる属性セットがエンティティ行部分に含まれる場合、属性はマージされます。その結果、キャッシュ内で部分的に移入されたエンティティ行に、オーバーラップするユーザー属性のサブセットが統合されます。一方、キャッシュにはなかったJohn Chen(ユーザー308)の場合、新しいエンティティ行にはFirstNameおよびLastName属性のみが含まれ、Emailは含まれません。

7.7.8 トランザクションのコミット時の処理

変更に問題がなく、トランザクションをコミットするとします。図7-28に示すとおり、次の2つの基本手順があります。

  1. Transactionオブジェクトにより、保留中の変更リスト内の無効なエンティティ行が検証されます。

  2. 保留中の変更リスト内のエンティティ行がデータベースに保存されます。

この図は、1つの変更済エンティティ・オブジェクトの検証処理が他のエンティティ・オブジェクトに対する変更にプログラム的に影響する前に行う、手順1のループを示します。保留中の変更リスト上で無効なエンティティのリストが処理された後、リストにまだエンティティが残されている場合、無効なエンティティのリストのパススルーが行われます。リストのパススルーは最大10回行われます。この時点でまだ無効なエンティティ行がある場合は通常、ビジネス・ロジックにエラーがあり、このビジネス・ロジックを調査する必要があるため、例外がスローされます。

図7-28 トランザクションのコミット時に行われる無効なエンティティの検証とエンティティの保存

無効なエンティティを検証する方法の図

7.7.9 マルチユーザー・シナリオの対話的テスト

ビュー・オブジェクトとエンティティ・オブジェクトの連携について理解する必要がある最後の側面は、マルチユーザー環境で操作するときに発生する2つの例外です。これらの例外はテスト用に簡単にシミュレートできます。これを行うには、SRServiceアプリケーション・モジュールに対してBusiness Component Browserを2回起動するだけですみます(2つ目を起動するとき、最初のインスタンスは終了しません)。次の2つのテストを実行し、これらのマルチユーザー環境で例外がどのように発生するかを確認してください。

  • 1つのBusiness Component Browserのテスターで、既存のサービス・リクエストのステータスを変更し、このフィールドから次に進みます。次に、もう1つのBusiness Component Browserウィンドウで、同じサービス・リクエストを同じ方法で変更してみます。この場合、2人目のユーザーにはoracle.jbo.AlreadyLockedExceptionが発生します。

    テストを繰り返しますが、今度は、Business Component Browserの「接続」ダイアログの「プロパティ」ページでjbo.locking.modeの値をoptimisticにオーバーライドしてみます。この場合、エラーは即座には発生せず、2人目のユーザーのコミット時に発生します。

  • 1つのBusiness Component Browserのテスターで、既存のサービス・リクエストのステータスを変更し、このフィールドから次に進みます。次に、もう1つのBusiness Component Browserウィンドウで、同じステータス・リクエストを変更せずに取得してみます。最初のウィンドウに戻り、変更をコミットします。2人目のユーザーが同じサービス・リクエストを変更しようとすると、oracle.jbo.RowInconsistentExceptionが発生します。これは、2人目のユーザーが行をエンティティ・キャッシュに取得した後に、行が別のユーザーによって変更されてコミットされたためです。

7.8 エンティティ・ベースのビュー・オブジェクトのプログラム的操作

アプリケーション・モジュールのデータ・モデルにアクセスするクライアントの視点からは、読取り専用ビュー・オブジェクトを操作するAPIと、エンティティ・ベースのビュー・オブジェクトを操作するAPIは同一のものです。機能的な主な相違点は、エンティティ・ベースのビュー・オブジェクトでは、ビュー・オブジェクトのデータを完全に更新できる点です。エンティティ・ベースのビュー・オブジェクトを含むアプリケーション・モジュールは、作業ユニットを定義し、トランザクションを管理します。この項では、SRServiceアプリケーション・モジュールを操作する簡単な4つのテスト・クライアント・プログラムを使用して次について説明します。

7.8.1 マスター/ディテール/ディテール階層の反復の例

例7-1では、次の基本手順を実行しています。

  1. StaffListビュー・オブジェクト・インスタンスを検索します。

  2. 問合せを実行します。

  3. 結果のStaffList行を反復します。

  4. 計算されたFullName属性の値を取得し、スタッフ・メンバーの氏名を出力します。

  5. ビュー・リンクのアクセッサ属性を使用してServiceRequestsの関連行セットを取得します。

  6. ServiceRequests行を反復します。

  7. サービス・リクエストの属性値を出力します。

  8. ステータスがClosedではない場合、ビュー・リンクのアクセッサ属性を使用してServiceHistoriesの関連行セットを取得します。

  9. ServiceHistories行を反復します。

  10. サービス・リクエストの履歴属性を出力します。


注意:

この例では、もう1つ別のネスト・レベルを使用せずに、5.10.4.2項「ビュー・リンク・アクセッサを使用したディテール・コレクションのアクセス方法」で説明した、マスター/ディテール読取り専用ビュー・オブジェクトを反復するTestClient2プログラムと同じAPIを使用しています。

例7-1 マスター/ディテール/ディテール階層の反復

package devguide.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
public class TestClient {
  public static void main(String[] args) {
    String        amDef = "devguide.model.SRService";
    String        config = "SRServiceLocal";
    ApplicationModule am =
      Configuration.createRootApplicationModule(amDef,config);
    // 1. Find the StaffList view object instance.
    ViewObject staffList = am.findViewObject("StaffList");
    // 2. Execute the query
    staffList.executeQuery();
    // 3. Iterate over the resulting rows
    while (staffList.hasNext()) {
      Row staffMember = staffList.next();
      // 4. Print the staff member's full name
      System.out.println("Staff Member: "+staffMember.getAttribute("FullName"));
      // 5. Get related rowset of ServiceRequests using view link accessor
      RowSet reqs = (RowSet)staffMember.getAttribute("ServiceRequests");
      // 6. Iterate over the ServiceRequests rows
      while (reqs.hasNext()) {
        Row svcreq = reqs.next();
        // 7. Print out some service request attribute values
        System.out.println(" ["+svcreq.getAttribute("Status")+"] "+
                               svcreq.getAttribute("SvrId")+": "+
                               svcreq.getAttribute("ProblemDescription"));
        if(!svcreq.getAttribute("Status").equals("Closed")) {
          // 8. Get related rowset of ServiceHistories
          RowSet hists = (RowSet)svcreq.getAttribute("ServiceHistories");
          // 9. Iterate over the ServiceHistories rows
          while (hists.hasNext()) {
            Row hist = hists.next();
            // 10. Print out some service request history attributes
            System.out.println("  "+hist.getAttribute("LineNo")+": "+
                               hist.getAttribute("Notes"));
          }
        }
      }
    }
    Configuration.releaseRootApplicationModule(am,true);
  }
}

このプログラムを実行すると、次の結果が生成されます。

Staff Member: David Austin
 [Open] 104: Spin cycle not draining
  1: Researching issue
Staff Member: Bruce Ernst
 [Closed] 101: Agitator does not work
 [Pending] 102: Washing Machine does not turn on
  1: Called customer to make sure washer was plugged in...
  2: We should modify the setup instructions to include...
 [Open] 108: Freezer full of frost
  1: Researching issue
Staff Member: Alexander Hunold
 [Closed] 100: I have noticed that every time I do a...
 [Closed] 105: Air in dryer not hot
   :

7.8.2 行の検索および外部キー値の更新の例

例7-2では、次の基本手順を実行しています。

  1. ServiceRequestsビュー・オブジェクト・インスタンスを検索します。

  2. サービス・リクエスト番号101の行を検索するためのKeyオブジェクトを作成します。

  3. findByKey()を使用して行を検索します。

  4. サービス・リクエストの属性値を出力します。

  5. 無効な値であるReopenedStatus属性への割当てを試行します。

    ビュー・オブジェクト行はエンティティ・オブジェクトと連携するため、Status属性に対する検証規則によって例外がスローされ、この不正な変更が阻止されます。

  6. Statusを有効な値のOpenに設定します。

  7. Status属性の値を出力し、正常に更新されたことを示します。

  8. 割り当てられている技術者の電子メールの現在の値を出力します。

  9. AssignedTo属性を設定し、サービス・リクエストを技術者番号303(Alexander Hunold)に再度割り当てます。

  10. 新しい技術者が反映された参照情報(TechnicianEmail)の値を表示します。

  11. ロールバックを発行してトランザクションを取り消します。

例7-2 外部キー値の検索および更新

package devguide.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.JboException;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
public class TestFindAndUpdate {
  public static void main(String[] args) {
    String        amDef = "devguide.model.SRService";
    String        config = "SRServiceLocal";
    ApplicationModule am =
      Configuration.createRootApplicationModule(amDef,config);
    // 1. Find the ServiceRequests view object instance
    ViewObject vo = am.findViewObject("ServiceRequests");
    // 2. Construct a new Key to find ServiceRequest# 101
    Key svcReqKey = new Key(new Object[]{101});
    // 3. Find the row matching this key
    Row[] reqsFound = vo.findByKey(svcReqKey,1);
    if (reqsFound != null && reqsFound.length > 0) {
      Row svcReq = reqsFound[0];
      // 4. Print some service request information
      String curStatus = (String)svcReq.getAttribute("Status");
      System.out.println("Current status is: "+curStatus);
      try {
        // 5. Try setting the status to an illegal value
        svcReq.setAttribute("Status","Reopened");
      }
      catch (JboException ex) {
        System.out.println("ERROR: "+ex.getMessage());
      }
      // 6. Set the status to a legal value
      svcReq.setAttribute("Status","Open");
      // 7. Show the value of the status was updated successfully
      System.out.println("Current status is: "+svcReq.getAttribute("Status"));
      // 8. Show the current value of the assigned technician
      System.out.println("Assigned: "+svcReq.getAttribute("TechnicianEmail"));
      // 9. Reassign the service request to technician # 303
      svcReq.setAttribute("AssignedTo",303); // Alexander Hunold (technician)
      // 10. Show the value of the reference info reflects new technician
      System.out.println("Assigned: "+svcReq.getAttribute("TechnicianEmail"));
      // 11. Rollback the transaction
      am.getTransaction().rollback();
      System.out.println("Transaction cancelled");
    }
    Configuration.releaseRootApplicationModule(am,true);
  }
}

この例を実行すると、次の結果が生成されます。

Current status is: Closed
ERROR: The status must be Open, Pending, or Closed
Current status is: Open
Assigned: bernst
Assigned: ahunold
Transaction cancelled

7.8.3 新規サービス・リクエストの作成の例

例7-3では、次の基本手順を実行しています。

  1. ServiceRequestsビュー・オブジェクト・インスタンスを検索します。

  2. 新しい行を作成し、行セットに挿入します。

  3. 関連するエンティティ・オブジェクトのStatus属性がデフォルト設定されることを示します。

  4. 新しい行の必須属性の値を設定します。

  5. トランザクションをコミットします。

  6. トリガーによって割り当てられたサービス・リクエストIDを取得して表示します。

例7-3 新規サービス・リクエストの作成

package devguide.client;
import java.sql.Timestamp;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.DBSequence;
import oracle.jbo.domain.Date;
public class TestCreatingServiceRequest {
  public static void main(String[] args) throws Throwable {
    String amDef = "devguide.model.SRService";
    String config = "SRServiceLocal";
    ApplicationModule am =
      Configuration.createRootApplicationModule(amDef, config);
    // 1. Find the ServiceRequests view object instance.
    ViewObject svcReqs = am.findViewObject("ServiceRequests");
    // 2. Create a new row and insert it into the row set
    Row newSvcReq = svcReqs.createRow();
    svcReqs.insertRow(newSvcReq);
    // 3. Show effect of entity object defaulting for Status attribute
    System.out.println("Status defaults to: "+newSvcReq.getAttribute("Status"));
    // 4. Set values for some of the required attributes
    newSvcReq.setAttribute("CreatedBy",308); // Nancy Greenberg (user)
    Date now = new Date(new Timestamp(System.currentTimeMillis()));
    newSvcReq.setAttribute("RequestDate",now);
    newSvcReq.setAttribute("ProdId",119); // Ice Maker
    newSvcReq.setAttribute("ProblemDescription","Cubes melt immediately");
    // 5. Commit the transaction
    am.getTransaction().commit();
    // 6. Retrieve and display the trigger-assigned service request id
    DBSequence id = (DBSequence)newSvcReq.getAttribute("SvrId");
    System.out.println("Thanks, reference number is "+id.getSequenceNumber());
    Configuration.releaseRootApplicationModule(am, true);
  }
}

この例を実行すると、次の結果が生成されます。

Status defaults to: Open
Thanks, reference number is 200

7.8.4 行を識別する行キーの取得の例

例7-4では、次の基本手順を実行しています。

  1. ServiceRequestsビュー・オブジェクトを検索します。

  2. サービス・リクエスト番号101を検索するためのキーを作成します。

  3. このキーを使用してServiceRequests行を検索します。

  4. ServiceRequests行のキーを表示します。

  5. ビュー・リンクのアクセッサ属性を使用してServiceHistoriesの行セットにアクセスします。

  6. ServiceHistories行を反復します。

  7. ServiceHistoriesの各行のキーを表示します。

例7-4 行を識別する行キーの取得

package devguide.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
public class TestFindAndShowKeys {
  public static void main(String[] args) {
    String amDef = "devguide.model.SRService";
    String config = "SRServiceLocal";
    ApplicationModule am =
      Configuration.createRootApplicationModule(amDef, config);
    // 1. Find the ServiceRequests view object
    ViewObject vo = am.findViewObject("ServiceRequests");
    // 2. Construct a key to find service request number 101
    Key svcReqKey = new Key(new Object[] { 101 });
    // 3. Find the ServiceRequests row with this key
    Row[] reqsFound = vo.findByKey(svcReqKey, 1);
    if (reqsFound != null && reqsFound.length > 0) {
      Row svcReq = reqsFound[0];
      // 4. Display the key of the ServiceRequests row
      showKeyFor(svcReq);
      // 5. Access row set of ServiceHistories using view link accessor
      RowSet histories = (RowSet)svcReq.getAttribute("ServiceHistories");
      // 6. Iterate over the ServiceHistories row
      while (histories.hasNext()) {
        Row historyRow = histories.next();
        // 7. Display the key of the current ServiceHistories row
        showKeyFor(historyRow);
      }
    }
    Configuration.releaseRootApplicationModule(am, true);
  }
  private static void showKeyFor(Row r) {
    // get the key for the row passed in
    Key k = r.getKey();
    // format the key as "(val1,val2)"
    String keyAttrs=formatKeyAttributeNamesAndValues(k);
    // get the serialized string format of the key, too
    String keyStringFmt = r.getKey().toStringFormat(false);
    System.out.println("Key "+keyAttrs+" has string format "+keyStringFmt);
  }
  // Build up "(val1,val2)" string for key attributes
  private static String formatKeyAttributeNamesAndValues(Key k) {
    StringBuffer sb = new StringBuffer("(");
    int attrsInKey = k.getAttributeCount();
    for (int i = 0; i < attrsInKey;i++) {
      if (i > 0 ) sb.append(",");
      sb.append(k.getAttributeValues()[i]);
    }
    sb.append(")");
    return sb.toString();
  }
}

この例を実行すると、次の結果が生成されます。シリアライズされたキーの文字列フォーマットは16進数で、キー内のすべての属性を示す単一文字列に情報が含まれています。

Key (101) has string format 000100000003313031
Key (101,1) has string format 000200000003C2020200000002C102
Key (101,2) has string format 000200000003C2020200000002C103

7.9 エンティティ・ベースのビュー・オブジェクトと読取り専用ビュー・オブジェクトの相違点の概要

これまでの説明で、ビュー・オブジェクトには基礎となるエンティティ・オブジェクトに関連付けられるものと関連付けられないものがあることを示しました。この項では、これら2つの基本的なビュー・オブジェクトの実行時の動作の相違点についての概要を示します。

7.9.1 エンティティ・ベースのビュー・オブジェクト固有の実行時機能

ビュー・オブジェクトに基礎となるエンティティ・オブジェクトの慣用名が1つ以上ある場合、新しい行の作成や、問合せ結果の行の変更または削除が可能です。エンティティ・ベースのビュー・オブジェクトは基礎となるエンティティ・オブジェクトと連携して、ビジネス・ルールを適用し、変更を永続的に保存します。また、エンティティ・ベースのビュー・オブジェクトには次のような特徴があることを説明しました。

  • 同じトランザクションの他のビュー・オブジェクトを介して行われた、関連するエンティティ・オブジェクト属性に対する保留中の変更を即時に反映します。

  • 新しく作成された行の属性値を、基礎となるエンティティ・オブジェクト属性の値で初期化します。

  • 外部キー属性値の変更時に更新された参照情報を反映します。

7.9.2 エンティティ・オブジェクトの慣用名のない読取り専用ビュー・オブジェクト

エンティティ・オブジェクトの慣用名のないビュー・オブジェクトは読取り専用であり、エンティティから導出されたデフォルト値は使用されず、保留中の変更、および更新された参照情報は反映されません。アプリケーションに必要な機能の種類を判断し、それに応じてビュー・オブジェクトを設計する必要があります。通常、SQLベースの検証を行うためのビュー・オブジェクトや、ドロップダウン・リストに有効な選択肢のリストを表示するためのビュー・オブジェクトは読取り専用です。ビュー・オブジェクト行とエンティティ・オブジェクト行の間の調整には、実行時に若干のオーバーヘッドが生じるため、エンティティにマップされたビュー・オブジェクトに用意された機能を必要としない場合、関連するエンティティ・オブジェクトのない読取り専用ビュー・オブジェクトを使用すると、パフォーマンスが多少向上します。

7.9.3 読取り専用ビュー・オブジェクトに対するビュー・オブジェクト・キー管理の有効化について

エンティティ・ベースのビュー・オブジェクトは、キーによる行の検索タスクを、基礎となるエンティティ行部分に委譲します。findByKey()メソッドを使用してキーによってビュー行を検索すると、ビュー行では逆にエンティティ定義のfindByPrimaryKey()により、ビュー行キーに属性を提供する各エンティティ行が検索されます。

読取り専用ビュー・オブジェクトの場合、ジョブを委譲する基礎となるエンティティ行がないため、このようなスキームは実現できません。読取り専用ビュー・オブジェクトを使用すると、エンティティ・ベースのビュー・オブジェクトに用意されている追加機能を必要とせず、問合せ結果を簡単に反復できます。このため、読取り専用ビュー・オブジェクトでは、ビュー・オブジェクトの行セット・レベルでキーを使用して行を管理することによる若干のオーバーヘッドが発生することは前提としていません。

読取り専用ビュー・オブジェクトでfindByKey()メソッドを正常に使用するには、次の2つの手順を実行する必要があります。

  1. ビュー・オブジェクトの少なくとも1つの属性に「キー属性」プロパティが設定されていることを確認します。

  2. ビュー・オブジェクトのカスタムJavaクラスを有効にし、次のようにcreate()メソッドをオーバーライドし、super.create()のコール後にsetManageRowsByKey(true)をコールします。

    // In custom Java class for read-only view object
    public void create() {
      super.create();
      setManageRowsByKey(true);
    }
    

注意:

読取り専用ビュー・オブジェクトへのデータ・バインディングにADFモデル・レイヤーを使用するアプリケーションの場合、ADF Faces表(またはエンド・ユーザーがページ内をクリックして現在の行を設定する他のコントロール)を正常に操作するには、この追加手順が必要です。また、このことは、読取り専用ビュー・オブジェクトでsetCurrentRowWithKeysetCurrentRowWithKeyValueなどの組込み型のバインディング・レイヤー・アクションを正常に使用する場合にも同様です。これらをすべて要約すると、背後にあるfindByKey()をコールするということになります。

25.3.2項「ランタイム・メタデータを使用した汎用機能の実装」では、findByKey()を想定どおりに機能させる必要があるすべての読取り専用ビュー・オブジェクトに対して、この作業を省略できるようにするための一般的な技術について説明します。