この章では、SRDemoアプリケーションのSRServiceを例として使用し、アプリケーション・モジュールのアクティブ・データ・モデルおよびビジネス・サービス・インタフェース・メソッドが、設計時にデータ・バインディングのドラッグ・アンド・ドロップ用にどのように表示されるか、および実行時にアプリケーション・モジュールのデータ・コントロールを使用してADF Modelデータ・バインディング・レイヤーからどのようにアクセスできるかについて説明します。
この章の内容は次のとおりです。
Oracle ADF Modelレイヤーは、宣言的なデータ・バインディング機能です。ADF Modelレイヤーでは、ユーザー・インタフェース・テクノロジとビジネス・サービス実装の分離を可能にするJSR-227仕様の2つの概念(データ・コントロールと宣言的バインディング)が具体化されています。データ・コントロールおよび宣言的バインディングにより、統一化された設計時および実行時のアプローチを使用して、ユーザー・インタフェースをコード記述なしでバックエンド・ビジネス・サービスにバインドできます。
データ・コントロールでは、サービスの操作およびデータ・コレクションを記述する標準のメタデータ・インタフェースを使用して、ビジネス・サービスの実装テクノロジを抽象化します。このインタフェースには、プロパティ、メソッドおよび関連する型についての情報が含まれます。設計時に、JDeveloperのようなビジュアル・ツールで標準的なサービス・メタデータを操作し、データ・コントロール操作やデータ・コレクションへのUIコンポーネントのバインドを簡略化します。実行時に、汎用のOracle ADF Modelレイヤーによって、適切なXMLファイルからデータ・コントロールおよびバインディングを記述した情報が読み取られ、ユーザー・インタフェースとビジネス・サービスを結合する双方向の関係が実装されます。
宣言的なバインディングによって、データ・コントロール内のデータ・コレクションからのデータ・アクセスの詳細と、その操作の実行が抽象化されます。すべてのエンタープライズ・アプリケーションに必要とされるデータ・バインディングの主要な機能を自動化する宣言的なバインディング・オブジェクトには、次の3つの基本的な種類があります。
イテレータ・バインディング: データ・コレクションの現在の行を追跡するイテレータをバインド
値バインディング: データ・コレクションの属性へのUIコンポーネントの関連付け
アクション・バインディング: カスタム操作または組込み操作をデータ・コントロールまたはそのデータ・コレクションで実行
イテレータ・バインディングを使用すると、データ・コレクションのスクロール処理やページ移動、または概要情報から詳細情報へのドリルダウンを可能にするユーザー・インタフェースを簡単に作成できます。データを表示するUIコンポーネントでは、値のバインディングを使用します。値バインディングは、単純なテキスト・フィールドで使用する最も基本的なものから、リスト、表、ツリーでのUIコントロールのニーズをサポートするような、より高度なリスト、表、ツリーのバインディングまで、様々な種類があります。アクション・バインディングは、メソッドを起動するハイパーリンクやボタンなどのUIコンポーネントによって使用されます。アクション・バインディングにより、ユーザーがコンポーネントをクリックすると、コードなしでビジネス・サービスが起動します。アクション・バインディングには、組込み操作を起動する通常のアクション・バインディングと、カスタム操作を起動するメソッド・アクション・バインディングの2種類があります。
アプリケーション・モジュールのデータ・コントロールは、Oracle ADFに付属する複数のデータ・コントロール実装のうちの1つです。その役目は、リクエスト開始時に使用可能なアプリケーション・モジュール・インスタンスを自動的に取得するアプリケーション・モジュール・プールのシン・アダプタとなることです。現在のリクエスト中に、アプリケーション・モジュールのデータ・コントロールにより、現在のユーザー・セッションのかわりに、アプリケーション・モジュール・インスタンスへの参照が保持されます。リクエスト終了時に、アプリケーション・モジュール・インスタンスは、アプリケーション・モジュールのデータ・コントロールによって解放され、プールに戻されます。ここで重要なことは、データ・コレクション、組込み操作およびサービス・メソッドに対してバインディング・オブジェクトが求めるインタフェースを、アプリケーション・モジュール・コンポーネントが直接実装することです。このため、バインディングでは、そのアクティブ・データ・モデル内のアプリケーション・モジュールおよびビュー・オブジェクトを直接操作できます。この最適化された対話では、特に、次のバインディングが可能になります。
任意のビュー・オブジェクト・インスタンスのデフォルト行セットのデフォルト行セット・イテレータに直接バインドするイテレータ・バインディング
次のいずれかに直接バインドするアクション・バインディング
アプリケーション・モジュールのクライアント・インタフェース上のカスタム・メソッド
アプリケーション・モジュールの組込み操作およびビュー・オブジェクト
図10-2は、アプリケーション・モジュールのデータ・コントロールが果すプール管理ロールを示し、バインディングとアプリケーション・モジュール・インスタンスの間の直接リンクを説明しています。
設計時に、データ・コントロール・パレットを使用して、JSF、JSP/StrutsおよびSwingアプリケーションのデータ・バインディングのドラッグ・アンド・ドロップを実行します。ワークスペースの各アプリケーション・モジュールは、データ・コントロール・パレットに自動的に表示されます。この項では、SRDemoアプリケーションのSRServiceアプリケーション・モジュールを例として使用し、特に、独自のアプリケーション・モジュールの操作時のデータ・コントロール・パレットでの正確な表示について説明します。
図10-3は、SRDemoアプリケーションのビジネス・サービス・レイヤーを実装するSRServiceアプリケーション・モジュールを示しています。このデータ・モデルには、複数のマスター/ディテール階層など、多数のビュー・オブジェクト・インスタンスが含まれています。デモのビュー・レイヤーはJSFページで構成され、このページのUIコンポーネントは、SRServiceのアクティブ・データ・モデル内のビュー・オブジェクト・インスタンスのデータと、そのクライアント・インタフェース上の組込み操作およびサービス・メソッドにバインドされています。10.6項「SRDemoページでのSRServiceの使用方法の概要」では、各ページでこのアプリケーション・モジュールのどの部分が使用されるかが詳しく説明されます。
デフォルトでは、アプリケーション・モジュールはデータ・コントロール・パレットにAppModuleNameDataControlという名前のデータ・コントロールとして表示されます。たとえば、SRServiceの元の名前はSRServiceDataControlでした。デフォルトのデータ・コントロール名を短くする、またはより適した名前に変更するには、次の手順を実行します。
アプリケーション・モジュール名を変更する方法:
アプリケーション・モジュールをアプリケーション・モジュール・エディタで開きます。
「カスタム・プロパティ」ページを開きます。
「名前」コンボ・ボックスで、ドロップダウン・リストからDATA_CONTROL_NAMEプロパティを選択します。
任意のデータ・コントロール名を「値」フィールドに入力し、「OK」をクリックしてウィザードを閉じます。
図10-4は、SRServiceのデータ・コントロール名を、デフォルトのSRServiceDataControlから、アプリケーション・モジュールの名前に応じた短いSRService名に変更したカスタム・プロパティ設定を示しています。変更は、データ・コントロール・パレットに即時に反映されます。
アプリケーション・モジュールからアプリケーション・ページまたはパネルへのデータのバインディングを開始すると、アプリケーション・モジュールのデータ・コントロール名が、ユーザー・インタフェース・プロジェクトのDataBindings.cpxファイルと、データ・バインディングの各ページ定義XMLファイルに表示されます。また、アプリケーション・モジュールのサービス・インタフェースをプログラムで操作する必要がある場合は、コードでデータ・コントロール名を参照できます。このため、アプリケーション・モジュールの名前を変更する予定がある場合は、ビュー・レイヤーを作成する前にこの変更を行うことをお薦めします。
|
注意: JDeveloperリリース3では、アプリケーション・モジュールのデータ・コントロール名を1つ以上のページで参照した後に変更することにした場合、それが参照されているページ定義ファイルを開き、元の名前を新しい名前に手動で更新する必要があります。JDeveloperの今後のリリースでは、このリファクタ・サポートを拡張し、データ・コントロール名の変更が容易になる予定です。 |
図10-5は、SRServiceのアクティブ・データ・モデル内のビュー・オブジェクト・インスタンスがデータ・コントロール・パレットでどのように表示されるかを示しています。各ビュー・オブジェクト・インスタンスは、ビュー・オブジェクト・インスタンス名と対応する名前が付いたデータ・コレクションとして表示されます。データ・コレクションの階層構造に注目してください。この図では、わかりやすくするために、各ビュー・オブジェクトに表示されるツリー内の詳細の一部は省略されています。ビュー・オブジェクトのこれらの追加の詳細は、10.3.6項「データ・コントロール・パレットでのビュー・オブジェクトの表示」で説明します。データ・コントロール・パレットは、マスター・データ・コレクションの下にネストされたディテール・データ・コレクションを表示することで、アプリケーション・モジュールのデータ・モデル内のマスター/ディテール階層を示します。
データ・コントロール・パレットは、アプリケーション・モジュールのクライアント・インタフェース上の各カスタム・メソッドも、メソッド名と対応する名前が付いたデータ・コントロール・カスタム操作として表示します。メソッドが引数を受け入れると、その引数は、操作ノード内にネストされた操作パラメータとして「Parameters」フォルダに表示されます。
ビュー・オブジェクト・インスタンスをデータ・モデルに初めて追加するとき、インスタンス名を自分で入力していない場合には、ビュー・オブジェクト名に数値接尾辞が付加された名前が生成されます。たとえば、ServiceRequestsビュー・オブジェクトのインスタンスをデータ・モデルに追加すると、デフォルトのビュー・オブジェクト・インスタンス名はServiceRequests1になります。ビュー・オブジェクト・インスタンス名は、アプリケーション・モジュール・エディタの「データ・モデル」ページで簡単に変更できます。
データ・コントロール・パレット内のデータ・コレクションからアプリケーション・ページまたはパネルへデータのバインディングを開始すると、データ・コントロール名の他に、データ・コレクション名も、ADF Modelデータ・バインディング・レイヤーで使用されるページ定義XMLファイル内で参照されます。アプリケーション・モジュールのデータ・モデル内のビュー・オブジェクト・インスタンスの名前がこれらのデータ・コレクションの名前として使用されるため、データがバインドされたページの作成にその名前を使用する前に、ビュー・オブジェクト・インスタンスの名前が説明的であることを確認してください。
|
注意: JDeveloperリリース3では、ビュー・オブジェクト・インスタンス名を1つ以上のページで参照した後に変更することにした場合、それが参照されているページ定義ファイルを開き、元の名前を新しい名前に手動で更新する必要があります。JDeveloperの今後のリリースでは、このリファクタ・サポートを拡張し、ビュー・オブジェクト・インスタンス名の変更が容易になる予定です。 |
アプリケーション・モジュールのデータ・コントロールでは、図10-6に示すように、CommitおよびRollbackという名前の2つのデータ・コントロールの組込み操作を公開します。これらの操作は、実行時にデータ・バインディング・レイヤーによって起動されると、現在のアプリケーション・モジュール・インスタンスに関連付けられているTransactionオブジェクトのcommit()メソッドおよびrollback()メソッドに委譲します。データ・コントロール・ツリー内の「Operations」フォルダでは、ビューを簡略化するために、データ・コレクションおよびカスタム操作がすべて省略されています。
|
注意: 多数のビュー・オブジェクト・インスタンスおよびカスタム・メソッドがあるアプリケーション・モジュールでは、データ・コントロールの直接の子ノードである「Operations」フォルダを見つけるには、データ・コントロール・パレット表示のスクロールが必要な場合があります。このフォルダに、その組込み操作が含まれています。 |
図10-7は、アプリケーション・モジュールのデータ・モデル内の各ビュー・オブジェクト・インスタンスがデータ・コントロール・パレットでどのように表示されるかを示しています。ビュー・オブジェクト属性は、対応するデータ・コレクションの直接の子ノードとして表示されます。この図のperformSearch()メソッドのように、いずれかのカスタム・メソッドをビュー・オブジェクトのクライアント・インタフェースに表示することを選択している場合、そのメソッドは、同じレベルのビュー・オブジェクト属性のすぐ下にカスタム操作として表示されます。メソッドが引数を受け入れると、その引数は、ネストされた「Parameters」フォルダに操作パラメータとして表示されます。
図10-7に示すように、データ・コレクションの下の「Operations」フォルダには、そのコレクションで使用可能な組込み操作がすべて表示されます。操作で1つ以上のパラメータを受け入れると、そのパラメータは、ネストされた「Parameters」フォルダに表示されます。実行時に、これらのデータ・コレクション操作の1つがデータ・バインディング・レイヤーによって名前別に起動されると、アプリケーション・モジュールのデータ・コントロールは、組込み機能を処理するViewObjectインタフェース上の適切なメソッドにそのコールを委譲します。組込み操作は、現在の行に影響を与える操作、データ・コレクションをリフレッシュする操作、および他の全操作の3つのカテゴリに分類されます。
Create: 現在の行になる新規行を作成。
Delete: 現在の行を削除。
First: 現在の行を行セット内の最初の行に設定。
Last: 現在の行を行セット内の最後の行に設定。
Previous: 現在の行を行セット内の前の行に設定。
Next: 行を行セット内の次の行に設定。
Previous Set: 1ページ前の行に移動。
Next Set: 1ページ後の行に移動。
setCurrentRowWithKey: パラメータとして渡された行キーのシリアライズされた文字列表現を使用して行の検索を試行。検出されると、それが現在の行になります。
setCurrentRowWithKeyValue: パラメータとして渡された主キーの属性値を使用して行の検索を試行。検出されると、それが現在の行になります。
他のアプリケーション・モジュールのネストされたインスタンスを組み込んで複合アプリケーション・モジュールを作成すると、データ・コントロール・パレットではこのコンポーネント・アセンブリがツリー階層で表示されます。たとえば、SRDemoアプリケーションのoracle.srdemo.model.SRServiceアプリケーション・モジュールの他に、同じパッケージに次のアプリケーション・モジュールも作成したとします。
ProductServiceという名前のアプリケーション・モジュールを作成し、そのデータ・コントロール名をProductServiceに変更
CompositeServiceという名前のアプリケーション・モジュールを作成し、そのデータ・コントロール名をCompositeServiceに変更
次に、OtherViewObjectおよびAnotherViewObjectという名前の2つのビュー・オブジェクト・インスタンスをCompositeServiceのデータ・モデルに追加し、アプリケーション・モジュール・エディタの「アプリケーション・モジュール」ページで、SRServiceアプリケーション・モジュールのインスタンスおよびProductMaintenanceアプリケーション・モジュールのインスタンスを追加してこれらのインスタンスをCompositeServiceの一部として再使用するとします。図10-8は、CompositeServiceがデータ・コントロール・パレットでどのように表示されるかを示しています。SRServiceおよびProductServiceのネストされたインスタンスは、CompositeServiceデータ・コントロール内にネストされたパレット・ツリーに表示されます。ネストされたアプリケーション・モジュール・インスタンスによってクライアントに公開されるデータ・モデル全体および一連のクライアント・メソッドは、それらを再使用するCompositeServiceの一部として自動的に使用可能になります。
SRServiceおよびProductServiceのネストされたインスタンスをCompositeService内で再使用していても、SRServiceおよびProductServiceアプリケーション・モジュールもパレット・ツリー内の最上位データ・コントロール・ノードとして表示されることに注意してください。JDeveloperでは、SRServiceまたはProductServiceがCompositeServiceから独立した個別のデータ・コントロールとして使用される場合があると想定し、この3つすべてを表示します。正しいデータ・コントロールからデータ・バインディングのドラッグ・アンド・ドロップを実行するように注意してください。CompositeServiceデータ・コントロールに集約された部分である、ネストされたSRServiceインスタンスのデータ・モデルのビュー・オブジェクト・インスタンスをページで使用する場合は、必ず、パレットでCompositeServiceデータ・コントロール・ノードの一部として表示されるデータ・コレクションを選択してください。
目的の用途に応じたドラッグ・アンド・ドロップ操作をすることが重要です。パレット内の最上位のSRServiceデータ・コントロール・ノードからデータ・コレクションをドロップすると、実行時に、SRServiceコンポーネントのプールから取得されたSRServiceアプリケーション・モジュールのインスタンスがページで使用されます。CompositeServiceの一部であるSRServiceのネストされたインスタンスからデータ・コレクションをドロップすると、実行時に、CompositeServiceコンポーネントのプールから取得されたCompositeServiceアプリケーション・モジュールのインスタンスがページで使用されます。各タイプのアプリケーション・モジュールのデータ・コントロールでは異なるトランザクションおよびデータベース接続が使用されるため、ネストされたアプリケーション・モジュールと最上位のデータ・コントロールの両方から不適切にデータ・コレクションを組み合せると、実行時に予期せぬ動作が発生します。事前に十分に注意してください。
「Create」ボタンを追加するには、データ・コレクションのCreate操作をデータ・コントロール・パレットからJSPページまたはSwingパネルにドラッグ・アンド・ドロップします。
|
注意: 組込みCreate操作は、WebベースのアプリケーションとSwingベースのデスクトップ・アプリケーションでは動作が異なります。Webアプリケーションでは、状況に応じて、CreateよりCreateInsertのほうが便利な場合があります。 |
データ・コレクションのCreate操作をデータ・コントロール・パレットからJSPページにドラッグすると、JSFを使用するかどうかに関係なく、組込みCreate操作に宣言的にバインドされているページ上に「Create」ボタンがドロップされます。Create操作により、データ・コレクションの新規行が作成されますが、その行はデータ・コレクションの行セットには挿入されません。作成時に新規行を行セットに追加しないことで、新規行にデータを実際に入力する前にユーザーがこのページから移動した場合に、不要な空白行が他のページに表示されることを回避できます。Create操作の起動後、イテレータ・バインディングでは、この新規行が現在の行であるかのようにこの行を一時的にポイントします。ユーザーが新規行の属性のデータを正常に送信した時点で、新規行が行セットに挿入されます。これは、ほとんどのWebアプリケーションのユースケースに最適な、宣言的な行作成アプローチです。
データ・コレクションのCreate操作をデータ・コントロール・パレットからSwingパネルにドラッグすると、CreateInsertという組込み操作に宣言的にバインドされているパネル上に「Create」ボタンがドロップされます。CreateInsert操作により、データ・コレクションの新規行が作成され、その新規行が、コレクション内の現在の行の直前に挿入されます。CreateInsertは、Swingアプリケーションに最適なアプローチです。
Webアプリケーションでは、CreateのかわりにCreateInsertを使用することが必要な場合があります。CreateInsertは、次の要素の作成時に使用します。
編集可能な表の制御
単一の現在編集可能な行が含まれる表
マスター/ディテール・ページ(新規作成したマスター行で既存のディテール行が表示されないようにする場合)
CreateInsertは、新規行を行セットに挿入する必要がある場合に使用します。データ・コントロール・パレットには1つのCreate操作のみが表示されるため、WebアプリケーションでCreateInsert操作を使用するには、いくつかの追加手順が必要です。
Create操作をCreateInsertに変更する方法:
Create操作をデータ・コントロール・パレットからページにドロップします。
ビジュアル・エディタでボタンを選択し、ポップアップ・メニューから「バインディングを編集」を選択します。
「アクション・バインディング・エディタ」で、「操作の選択」ドロップダウン・リストを使用して、アクション・バインディングでCreateではなくCreateInsertを使用するように変更します。
CreateまたはCreateInsert操作を使用して新規行を宣言的に作成すると、次のコード行が暗黙的に実行されます。
// create a new row for the view object Row newRow = yourViewObject.createRow(); // mark the row as being "initialized", but not yet new newRow.setNewRowState(Row.STATUS_INITIALIZED);
また、CreateInsert操作を使用している場合は、行を行セットに挿入する追加のコード行も実行されます。
// insert the new row into the view object's default rowset yourViewObject.insertRow(newRow);
行をエンティティ・ベースのビュー・オブジェクトに作成すると、現在のアプリケーション・モジュールに関連付けられているTransactionオブジェクトによって、その処理が即時に記録されます。ビュー行の後に作成された新規エンティティ行は、Transactionの保留中の変更リストにすでに含まれています。新しく作成した行の状態が初期化済とマークされている場合、その行はTransactionの保留中の変更リストから削除され、エンド・ユーザーがまだデータ値を入力していない空白行とみなされます。新規行は、基礎となるエンティティ・オブジェクトで定義済のデフォルト値で初期化されてエンド・ユーザーに表示されるため、初期化済という用語はこの状態を表す適切な用語です。初期化済の行のどの属性にもユーザーがデータを入力していない場合は、その行が存在していないかのように処理されます。トランザクションのコミット時には、その行がTransactionの保留中の変更リストに含まれていないため、INSERT文はその行に対して試行されません。
初期化済の行に1つ以上の属性が設定されると同時に、初期化済の状態から新規状態(Row.STATUS_NEW)に自動的に遷移します。この時点で、基礎となるエンティティ行はTransactionの保留中の変更リストに登録され、トランザクションの次回コミット時に新規行が永続的に保存されます。
|
注意: 多数の初期化済の行を作成してもその行への移入を行わないアプリケーションを使用しながらエンド・ユーザーが手順を実行した場合、低速メモリー・リークが発生することがあります。しかし、心配は無用です。新規状態に遷移しない初期化済の行で使用されるメモリーは、Java仮想マシンのガベージ・コレクタによって最終的に解放されます。 |
ページの作成時は、Record 5 of 25などの、なんらかのレコード・ステータス・インジケータを表示すると便利な場合がよくあります。複数の行を1ページに表示するときは、Record 5-10 of 25などのバリアントを表示すると便利な場合もあります。このようなレコード・インジケータは、単純なテキスト・コンポーネントを使用して作成でき、各コンポーネントにより、EL式を使用してイテレータ・バインディングまたは表バインディングから適切な値が表示されます。イテレータ・バインディングのrangeSizeプロパティにより、ユーザー・インタフェースで表示可能なページごとの行数が定義されます。ページ定義にSomeViewIterという名前のイテレータ・バインディングまたはSomeViewという名前の表バインディングが含まれている場合は、次のEL式を参照できます。
ページごとの行数
#{bindings.SomeViewIter.rangeSize}
#{bindings.SomeView.rangeSize}
合計行数
#{bindings.SomeViewIter.estimatedRowCount}
#{bindings.SomeView.estimatedRowCount}
現在のページの最初の行
#{bindings.SomeViewIter.rangeStart + 1}
#{bindings.SomeView.rangeStart + 1}
現在のページの最後の行
#{bindings.SomeViewIter.rangeStart + bindings.SomeViewIter.rangeSize}
#{bindings.SomeView.rangeStart + bindings.SomeView.rangeSize}
現在の行の番号
#{bindings.SomeViewIter.rangeStart + bindings.SomeViewIter.currentRowIndexInRange + 1}
#{bindings.SomeView.currentRowIndex + 1}
ビュー・オブジェクトに名前付きバインド変数がある場合、追加のExecuteWithParams操作が、対応するデータ・コレクションの「Operations」フォルダ内に表示されます。図10-9に示すように、この組込み操作では名前付きバインド変数ごとに対応するパラメータを1つ受け入れます。たとえば、図のStaffListByEmailNameRoleビュー・オブジェクトには、EmailAddress、Role、TheFirstNameおよびTheLastNameという4つの名前付きバインド変数があるため、ExecuteWithParamsアクションに対してこれらと同じ名前のパラメータが表示されます。実行時に、ExecuteWithParams組込み操作がデータ・バインディング・レイヤーによって名前別に起動されると、バインディング・レイヤーによって渡された個別のパラメータ値に名前付きバインド変数の値がそれぞれ設定され、ビュー・オブジェクトの問合せが実行されてその結果行セットがリフレッシュされます。
別のアプローチでは、同じく図10-9に示すように、ユーザーに設定を許可する引数を受け入れるビュー・オブジェクトのカスタムfinderメソッドを作成した後、ビュー・オブジェクト・エディタでビュー・オブジェクトのクライアント・インタフェースにこのメソッドを追加します。例10-1は、このようなカスタム・メソッドのコードを示しています。これは、SRDemoアプリケーションのStaffListByEmailNameRoleビュー・オブジェクトから引用したものです。アプリケーション・モジュールのアクティブ・データ・モデルにより、メソッドではデータをクライアントに戻す必要がないことに注意してください。このメソッドにはvoid戻り型があり、生成されたバインド変数のアクセッサ・メソッドsetEmailAddress()、setRole()、setTheFirstName()およびsetTheLastName()を使用して、メソッドに渡されたパラメータ値にバインド変数の値を設定します。次に、executeQuery()をコールして、ビュー・オブジェクトの結果行セットをリフレッシュする問合せを実行します。
例10-1 ビュー・オブジェクトのカスタム・メソッドによる名前付きバインド変数の設定および問合せの実行
// From SRDemo Sample's StaffListByEmailNameRoleImpl.java
public void findStaffByEmailNameRole(String email,
String firstName,
String lastName,
String role)
{
setEmailAddress(email);
setRole(role);
setTheFirstName(firstName);
setTheLastName(lastName);
executeQuery();
}
この両方のアプローチでは、最終結果は同じになります。いずれも、データ・コントロール・パレットから検索フォームを作成する際に、同様に簡単に使用できます。この2つのアプローチの主な違いは次のとおりです。
組込み機能であるため、コードの記述が不要です。
操作をデータ・コントロール・パレットからドロップして、ADF検索フォームを作成でき、ユーザーが名前付きバインド変数の値に基づいてビュー・オブジェクトを検索できるようになります。
名前付きのバインド変数に対応する検索フィールドでは、ビュー・オブジェクト・コンポーネントの一部として定義可能なラベル・テキストに対するバインド変数のUIコントロール・ヒントを継承します。
カスタム・コードを多少記述する必要がありますが、最上位の操作の名前がデータ・コントロール・パレットに表示されることによって、検索操作の目的が明確になります。
カスタム操作をデータ・コントロール・パレットからドロップして、ADF検索フォームを作成でき、ユーザーがパラメータ値に基づいてビュー・オブジェクトを検索できるようになります。
メソッド引数に対応する検索フィールドでは、ビュー・オブジェクトの名前付きバインド変数自体に定義されるラベル・テキストのUIコントロール・ヒントを継承しません。このため、図10-10に示すように、メソッド引数に対応するページ定義変数のポップアップ・メニューの「プロパティ」を選択して、ラベル・テキストのUIコントロール・ヒントを定義する必要があります。
つまり、適切なアプローチを自分で判断できるように、両方のアプローチを覚えておくことをお薦めします。10.6.5項「SRStaffSearchページ」で説明されているように、StaffListByEmailNameRoleビュー・オブジェクトには前述のfindStaffByEmailNameRole()カスタム・メソッドが説明目的で含まれていますが、デモのJSFページでは宣言的なExecuteWithParams組込みアクションを使用しています。
ADF Modelレイヤーでは、QBEをサポートするデータ・コレクションの操作時に、イテレータ・バインディングによって検索モードという機能がサポートされます。5.8項「Query-By-Exampleビュー基準を使用した結果のフィルタリング」で説明したとおり、ビュー基準行のビュー基準行セットが適用されている場合に、ビュー・オブジェクトでQBEがサポートされます。ビュー基準行は、ビュー・オブジェクトの行と同じ構造をしていますが、各属性のデータ型はStringであり、> 304またはIN (314,326)のような基準を検索基準として入力できます。
検索モード機能をサポートするために、イテレータ・バインディングには、ブール型findModeプロパティがあり、デフォルトでfalseに設定されています。図10-11に示すように、findModeがfalseの場合、イテレータ・バインディングは、データ・コレクション内のデータの行を含む行セットをポイントします。対照的に、findModeをtrueに設定すると、イテレータ・バインディングは、ビュー基準行の行セットをポイントするように切り替えられます。
バインディング・レイヤーでは、組込みデータ・コントロール操作を起動するアクション・バインディングは、ページ定義メタデータ内のイテレータ・バインディングに関連付けられます。このため、バインディング・レイヤーでCreateやDeleteなどの操作を実行時に正しいデータ・コレクションなどに適用することが可能になります。findModeがfalseに設定されているイテレータ・バインディングに対してCreateやDeleteなどの操作が起動されると、これらの操作は、データを含む行セットに影響を与えます。対照的に、findModeがtrueに設定されているイテレータ・バインディングに対して操作が起動されると、これらの操作はビュー基準行を含む行セットに影響を与え、QBE基準の行を効率的に作成または削除します。
組込みFind操作を使用すると、イテレータ・バインディングのfindModeフラグを切り替えることができます。イテレータ・バインディングのfindModeがfalseの場合、そのイテレータ・バインディングに対してFind操作を起動すると、フラグがtrueに設定されます。このイテレータに関連付けられている属性バインディングにバインドされているUIコンポーネントは、適宜、ビュー基準行セット内の現在のビュー基準行を表示するように切り替えられます。関連イテレータが検索モードの間にユーザーがUIコンポーネントの値を変更すると、その値はビュー基準行セット内のビュー基準行に適用されます。検索モードのイテレータにExecuteまたはExecuteWithParams組込み操作を起動すると、それぞれの操作によって検索モードがfalseに切り替えられた後、検索モード基準が適用され、データ・コレクションがリフレッシュされます。
イテレータ・バインディングのfindModeがtrueの場合、Find操作を起動すると、検索モードがfalseに設定され、ビュー基準行セット内のビュー基準行が削除されます。この結果、QBEモードが効率的に取り消されます。
ADF Controllerレイヤーでは、JSFページ・ライフサイクルとADF Modelデータ・バインディング・レイヤーが統合されます。この統合のカスタマイズは、アプリケーション全体でグローバルに行うか、ページごとに行うことができます。この項では、主に、SRDemoアプリケーションでこの2つの方法がどのように使用されているかについて、いくつかのヒントを示します。
ADFページ・ライフサイクルをグローバルにカスタマイズするには、次の処理を実行します。
oracle.adf.controller.faces.lifecycle.FacesPageLifecycleを拡張するクラスを作成します。
ADFPhaseListenerの拡張とcreatePageLifecycle()メソッドのオーバーライドを行ってカスタム・ページ・ライフサイクル・クラスのインスタンスを戻すクラスを作成します。
faces-config.xmlファイルを、デフォルトのADFPhaseListenerではなく、ADFPhaseListenerのサブクラスを使用するように変更します。図10-12に示すように、これは、JDeveloperのfaces-config.xmlエディタの「概要」タブの「Life Cycle」カテゴリで実行できます。
|
注意: 既存のADFPhaseListenerをADFPhaseListenerのカスタム・サブクラスと必ず置換してください。置換しないと、JSF/ADFライフサイクル調整がすべて2回実行されます。 |
SRDemoアプリケーションには、SRDemoPageLifecycleクラスが含まれています。このクラスにより、ページ・ライフサイクルのreportErrors()メソッドがグローバルにオーバーライドされ、ADF Modelレイヤーで捕捉およびキャッシュされた例外がJSFに報告されるデフォルトの方法が変更されます。この変更された実装では、ユーザーに報告される例外が、ユーザーが直接処理できる例外のみに削減され、エンド・ユーザーにはあまり意味のない追加のラップ例外が抑制されます。
単一ページのライフサイクルのカスタマイズは、pageDefinitionのControllerClass属性を設定して次のいずれかのクラスを識別することで実行できます。
oracle.adf.controller.v2.PageControllerクラスを拡張するクラス
oracle.adf.controller.v2.PagePhaseListenerインタフェースを実装するクラス
ページ定義のControllerClass属性の値は次のいずれかになります。
完全修飾クラス名
前述の要件を満たすクラスに解決されるEL式
ControllerClassの値にEL式を使用すると、faces-config.xmlファイルでマネージドBeanとして構成したカスタム・ページ・コントローラ・クラス(またはページ・フェーズ・リスナー実装)の名前を指定できます。これにはJSFページのバッキングBeanが含まれます(PageControllerが拡張されている場合またはPagePhaseListenerが実装されている場合)。
図10-13は、ページ定義のルート・ノードを構造ウィンドウで選択して設定する方法を示しています。
|
注意: ControllerClass属性の値にEL式を使用すると、#{YourExpression}が有効なクラスではないことを示す警告が構造ウィンドウに表示される場合があります。この警告は無視しても問題ありません。 |
SRDemoアプリケーションには、oracle.srdemo.view.utilパッケージのOnPageLoadBackingBeanBaseクラスが含まれています。このクラスにより、例10-2に示すようなコードを使用して、前述のPagePhaseListenerインタフェースが実装されます。通常のADFモデル準備フェーズの前にonPageLoad()メソッドを起動し、通常のADFレンダリング準備フェーズの後にonPagePreRender()メソッドを起動するために、このクラスでインタフェースのbeforePhase()メソッドおよびafterPhase()メソッドを実装します。
例10-2 PagePhaseListenerによるonPageLoad()メソッドおよびonPagePreRender()メソッドの起動
// In class oracle.srdemo.view.util.OnPageLoadBackingBeanBase
/**
* Before the ADF page lifecycle's prepareModel phase, invoke a
* custom onPageLoad() method. Subclasses override the onPageLoad()
* to do something interesting during this event.
* @param event
*/
public void beforePhase(PagePhaseEvent event) {
FacesPageLifecycleContext ctx =
(FacesPageLifecycleContext)event.getLifecycleContext();
if (event.getPhaseId() == Lifecycle.PREPARE_MODEL_ID) {
bc = ctx.getBindingContainer();
onPageLoad();
bc = null;
}
}
/**
* After the ADF page lifecycle's prepareRender phase, invoke a
* custom onPagePreRender() method. Subclasses override the onPagePreRender()
* to do something interesting during this event.
* @param event
*/
public void afterPhase(PagePhaseEvent event) {
FacesPageLifecycleContext ctx =
(FacesPageLifecycleContext)event.getLifecycleContext();
if (event.getPhaseId() == Lifecycle.PREPARE_RENDER_ID) {
bc = ctx.getBindingContainer();
onPagePreRender();
bc = null;
}
}
public void onPageLoad() {
// Subclasses can override this.
}
public void onPagePreRender() {
// Subclasses can override this.
}
マネージドBeanはこのインタフェースの実装をベース・クラスから継承するため、OnPageLoadBackingBeanBaseクラスを拡張するマネージドBeanであれば、ADFページ・フェーズ・リスナーとして使用できます。次に、そのバッキングBeanがonPageLoad()とonPagePreRender()の一方または両方のメソッドをオーバーライドする場合、そのメソッドは、ページ・リクエスト・ライフサイクル内の適切な時期にADFページ・ライフサイクルによって起動されます。このようなバッキングBeanを有効にするための最終手順では、バッキングBeanをそのページのページ・コントローラとして使用するようにADFページ定義に指示します。前述のとおり、この指示は、該当するページ定義のControllerClass属性を、バッキングBeanに対して評価するEL式に設定して行います。
SRDemoアプリケーションのSRMainページでは、この項に記載された方法を使用して、oracle.srdemo.view.backingパッケージのSRMainバッキングBeanのonPageLoad()メソッドにプログラム・コードを記述する方法を示しています。そのバッキングBeanの名前はfaces-config.xmlではbacking_SRMainであるため、SRMainページのページ定義のControllerClassプロパティはEL式#{backing_SRMain}に設定されます。
図10-14は、JSFページ・ライフサイクルと、ADFページ・ライフサイクルが追加する拡張ページ処理フェーズとの関係を示しています。Refreshプロパティをページ定義内のイテレータ・バインディングおよびinvokeAction実行可能ファイルで使用すると、ADFページ・ライフサイクル中にそれぞれが評価されるフェーズ(prepareModelフェーズもしくはprepareRenderフェーズ、またはその両方)を制御できます。リフレッシュという用語はわかりにくいため、この項では、実行可能ファイルのタイプごとにその意味を説明し、必要な動作を実現するためにRefreshプロパティをどのように設定すればよいかについて説明します。
アプリケーション・モジュール内のビュー・オブジェクト・インスタンスのイテレータ・バインディングを操作するとき、実際にはデフォルト設定のRefresh="ifNeeded"をそのまま使用できます。これをRefreshConditionプロパティ内のブール値のEL式で補完すると、イテレータのリフレッシュを必要に応じて条件付きで回避できます。ここで、イテレータ・バインディングのリフレッシュの意味について説明します。
イテレータ・バインディングとは、その名前が示すとおり、イテレータをポイントするバインディングです。スケーラビリティ上の理由から、バインディング・コンテナ内のイテレータ・バインディングは、実行時に、行セット・イテレータへの参照を各リクエストの終了時に解放します。次のリクエスト中に、イテレータ・バインディングは、データ・コレクションの現在の行を追跡している有効な行セット・イテレータをポイントするように再度リフレッシュされます。ADFページ・ライフサイクル中のADFイテレータのリフレッシュとは、正確には、行セット・イテレータにアクセスし、バインディングをバインド先の行セット・イテレータに再結合する操作を意味します。
ライフサイクル中にリフレッシュされない場合、イテレータ・バインディングは、そのリクエストのどの行セット・イテレータもポイントしません。この結果、値バインディングは、表示するデータがないイテレータ・バインディングに関連付けられます。これは、最初はデータを表示しない検索ページのようなページなどの場合、望ましい結果となることがあります。これを実現するために、次のRefreshConditionを使用できます。
#{adfFacesContext.postback == true}
adfFacesContext.postbackブール型プロパティは、ページが最初にレンダリングされたとき、または別のページからの移動によりレンダリングされたときは、falseと評価されます。エンド・ユーザーがページ上のいずれかのUIコントロールと対話済の場合はtrueと評価され、そのページへのポストバックが発生してイベントが処理されます。この式を特定のイテレータ・バインディングのRefreshConditionとして使用すると、ユーザーがページ上のコントロールと対話した場合にのみ、イテレータ・バインディングがリフレッシュされます。
イテレータ・バインディングのRefreshプロパティの有効な値は次のとおりです。イテレータをリフレッシュするフェーズに応じて選択してください。
prepareModelとprepareRenderの両方のフェーズの場合は、Refresh="ifNeeded"(デフォルト)を使用
prepareModelフェーズのみの場合は、Refresh="prepareModel"を使用
prepareRenderフェーズのみの場合は、Refresh="renderModel"を使用
独自のコードでイテレータ・バインディングにgetRowSetIterator()をコールした場合にのみイテレータ・バインディングをリフレッシュする場合は、Refresh="never"を設定します。その他のRefreshの値は、イテレータ・バインディングには無関係であるか、将来使用するために予約されています。
アプリケーション・モジュールのビュー・オブジェクト・インスタンスに関連するイテレータ・バインディングを操作するときは、イテレータ・バインディングをリフレッシュしても、その問合せの再実行が毎回強制されるわけではないことを理解することが重要です。特定のユーザーの作業ユニット中にビュー・オブジェクト・インスタンスの行セット・イテレータへ最初にアクセスしたときは、ビュー・オブジェクトの問合せがまだ実行されていない場合、問合せが暗黙的に実行されます。同じ論理作業ユニットの一部であるページ・リクエストでそのビュー・オブジェクト・インスタンスに関連するイテレータ・バインディングをこの後でリフレッシュすると、行セット・イテレータへの再アクセスのみが行われ、問合せの再実行は強制されません。問合せを再実行してそのデータをリフレッシュするには、ExecuteまたはExecuteWithParamsという組込み操作を使用するか、イテレータ・バインディングでexecuteQuery()メソッドをプログラムによってコールします。
SRDemoアプリケーションのいくつかのページ定義では、宣言的なinvokeActionバインディングを使用して、この拡張ADFページ処理ライフサイクル中に、組込み操作またはカスタム操作をトリガーしています。各invokeActionには、バインディングに名前を付けるidプロパティの他に、主なプロパティが3つあります。
invokeActionが起動した場合に行われる処理を制御するBindsプロパティ
この値は、同じバインディング・コンテナにあるアクション・バインディングまたはメソッド・アクション・バインディングの名前になります。
invokeActionによってアクション・バインディングが起動するフェーズを制御するRefreshプロパティ
これを起動するには、ADFページ・ライフサイクルのフェーズに応じて次のいずれかを使用します。
prepareModelフェーズの場合は、Refresh=prepareModelを使用
prepareRenderフェーズの場合は、Refresh=renderModelを使用
prepareModelフェーズおよびprepareRenderフェーズの場合は、Refresh=ifNeededを使用
起動されるかどうかを制御できるRefreshConditionプロパティ
この値を指定する場合は、ブール値のEL式になります。ページ・ライフサイクル中にinvokeActionが考慮されたときに式がtrueと評価されると、関連付けられているアクション・バインディングが起動します。falseと評価された場合、アクション・バインディングは起動しません。
|
注意: ここに記載されていないRefreshプロパティの他の値は、将来使用するために予約されています。 |
図10-14からわかるとおり、ADFのprepareModelフェーズとprepareRenderフェーズの主な違いは、一方がJSFのinvokeApplicationフェーズの前にあり、他方が後にあることです。JSFのinvokeApplicationフェーズではアクション・リスナーが起動されるため、これらのアクション・リスナーが自身の処理を実行した後にinvokeActionをトリガーする必要がある場合は、Refresh="renderModel"を設定します。
パラメータを受け入れるメソッド・アクション・バインディングにinvokeActionがバインドされる場合は、prepareModelIfNeededおよびrenderModelIfNeededの2つの追加の値をRefreshプロパティに指定できます。これらの値は、*IfNeeded接尾辞がない同等の設定と同じ意味を持ちますが、評価されたパラメータ値の現在のセットを、前回のメソッド・アクション・バインディングの起動に使用されたセットと比較するために最適化を実行する点が異なります。現在の起動に対するパラメータ値が前回使用した値と完全に同じ場合、invokeActionは、バインドされているメソッド・アクション・バインディングを起動しません。
|
注意: Refreshプロパティのデフォルト値はifNeededです。これは、起動を再調整するRefreshCondition式を指定しない場合、各リクエスト中に、関連付けられているアクション・バインディングが2回起動されることを意味します。適切なRefreshCondition式を追加するか(両方のフェーズ中に評価する場合)、JSFのinvokeApplicationフェーズの前と後のどちらでinvokeActionを起動するかに応じて、invokeActionバインディングのデフォルトのRefresh設定をprepareModelまたはrenderModelのいずれかに変更することをお薦めします。 |
getKey()メソッドをどのビュー行でコールしても、行を識別する1つ以上のキー属性をカプセル化するKeyオブジェクトを取得できます。前述の例で説明したとおり、このようなKeyオブジェクトは、findByKey()による行セット内のビュー行の検索にも使用できます。実行時に、setCurrentRowWithKeyまたはsetCurrentRowWithKeyValueという組込み操作のいずれかがデータ・バインディング・レイヤーによって名前別に起動されると、検索された行が現在の行として設定される前に、findByKey()メソッドを使用して、パラメータとして渡された値に基づいて行が検索されます。
図10-15に示すように、setCurrentRowWithKey操作およびsetCurrentRowWithKeyValue操作はいずれもrowKeyという名前のパラメータを予想しますが、実行時にそのrowKeyパラメータに予想される値がそれぞれで異なるため、混乱が生じることがあります。
setCurrentRowWithKeyでは、rowKeyパラメータ値がビュー行キーのシリアライズされた文字列表現であると予想されます。これは、次のような16進エンコード文字列です。
000200000002C20200000002C102000000010000010A5AB7DAD9
キーのシリアライズされた文字列表現により、ブラウザのURL文字列またはフォーム・パラメータで単一値として渡すことができる方法で、ビュー行のキーを構成する可能性のあるすべてのキー属性がエンコードされます。実行時に、有効なシリアライズされた文字列キーではないパラメータ値を不注意に渡すと、oracle.jbo.InvalidParamExceptionまたはjava.io.EOFExceptionなどの例外を結果として受信する場合があります。Webページでは、ADFコントロール・バインディングのrowKeyStrプロパティ(#{bindings.SomeAttrName.rowKeyStr}など)またはADF Faces表の行変数(#{row.rowKeyStr}など)を参照すると、行のシリアライズされた文字列キーの値にアクセスできます。
setCurrentRowWithKeyValueでは、rowKeyパラメータ値がビュー行のキーを表すリテラル値であると予想します。たとえば、サービス・リクエスト番号201を検索する場合、この値は単に201になります。
|
注意: アプリケーション・モジュール・クラスにカスタム・コードを記述し、クライアントから渡されたシリアライズされた文字列キーに基づいて行を検索する必要がある場合は、oracle.jbo.clientパッケージにあるJboUtilクラスのgetRowFromKey()メソッドを次のように使用できます。
static public Row getRowFromKey(RowSetIterator rsi, String sKey) 行を検索するビュー・オブジェクト・インスタンスを第1パラメータとして、キーのシリアライズされた文字列書式を第2パラメータとして渡します。 |
アプリケーション・モジュールに付属のバンドル例外モードという機能を使用すると、Webアプリケーションでは、発生した最初のエラーのみを提示するのではなく、失敗した検証例外の最大セットをエンド・ユーザーに簡単に提示できます。ADF Business Componentsアプリケーション・モジュール・プールでは、Webアプリケーションに対してバンドル例外モードをデフォルトで有効にしています。
このデフォルト設定は、通常変更する必要はありません。ただし、検証例外がスローされる方法に影響を与えることからデフォルトで有効になっていることを理解することが重要です。Java言語およびJavaランタイムは単一例外オブジェクトのスローのみをサポートするため、バンドルされた検証例外は、例外のセットを、それを含む新しい親例外の詳細としてラップすることで実装されます。たとえば、単一エンティティ・オブジェクトの複数の属性で属性レベルの検証ができない場合、これら複数のValidationExceptionオブジェクトは、RowValExceptionにラップされます。このラップ例外には、検証できなかった行の行キーが含まれています。トランザクションのコミット時に、コミット中に実行された検証を複数の行で渡せなかった場合、すべてのRowValExceptionオブジェクトは、包含するTxnValExceptionオブジェクトにラップされます。
カスタム・エラー処理コードを記述するときは、SRDemoアプリケーションのSRDemoPageLifecycleクラスのオーバーライドされたreportErrors()メソッドで示されるように、JboExceptionベース例外クラスのgetDetails()メソッドを使用すると、内部にバンドルされている例外を再帰的に処理できます。
|
注意: ここに記載されている例外クラスはすべて、oracle.jboパッケージのクラスです。 |
この項では、SRDemoアプリケーションの各ページでSRServiceアプリケーション・モジュールのビュー・オブジェクト・インスタンスおよびサービス・メソッドをどのように使用しているかを簡単に説明します。
図10-16は、SRListページのデータ・バインディングを示しています。
ServiceRequestsByStatusビュー・オブジェクト・インスタンスのServiceRequestsByStatusIterator
なし
ServiceRequestsByStatusIteratorイテレータ・バインディングに関連付けられているsetCurrentRowWithKey、ExecuteWithParams
なし
なし
図10-17は、SRMainページのデータ・バインディングを示しています。
ServiceRequestsビュー・オブジェクト・インスタンスのServiceRequestsIterator
ServiceHistoriesビュー・オブジェクト・インスタンスのServiceHistoriesIterator
なし
ServiceRequestsIteratorイテレータ・バインディングに関連付けられているsetCurrentRowWithKey、Delete
ServiceHistoriesIteratorイテレータ・バインディングに関連付けられているCreate、DeleteNewHistory(Delete組込みの場合)
SRServiceデータ・コントロールに関連付けられているCommit
deleteServiceHistoryNotesがSRServiceクライアント・インタフェース上のdeleteServiceHistoryNotes()メソッドを起動
なし
|
注意: SRMainページのSRMainバッキングBean(oracle.srdemo.view.backingパッケージ内)では、10.5.4.3項「カスタムADFページ・ライフサイクルによるonPageLoadバッキングBeanメソッドの起動」で説明されている方法を使用して、SREditページが宣言的に実行している処理と完全に同じ処理をプログラムで実行します。 |
ServiceRequestsは、エンティティ・ベースのビュー・オブジェクトServiceRequestsのインスタンスです。メインのServiceRequestエンティティ・オブジェクトの慣用名および3つの追加の参照エンティティ・オブジェクトの慣用名、すなわちCreatedByUser(Userエンティティ・オブジェクト)、AssignedToUser(Userエンティティ・オブジェクト)およびProductからデータを結合します。ServiceRequestsビュー・オブジェクトは、ServiceHistoriesビュー・オブジェクトにリンクされているマスター/ディテールです。
ServiceHistoriesは、エンティティ・ベースのビュー・オブジェクトServiceHistoriesのインスタンスです。メインのServiceHistoryエンティティ・オブジェクトの慣用名および1つの追加の参照エンティティ・オブジェクトの慣用名、すなわちSystemUser(Userエンティティ・オブジェクト)からデータを結合します。これは、カスタムJavaクラスを持たない、XML専用のビュー・オブジェクトです。
例10-3に示すように、deleteServiceHistoryNotes()メソッドにより、引数として渡されたキー・セット内のKeyオブジェクトに対応するサービス履歴メモ行が削除されます。
例10-3 SRServiceImpl.javaのDeleteServiceHistoryNotesメソッド
public void deleteServiceHistoryNotes(Set keySet) {
if (keySet != null) {
ViewObject histories = getServiceHistories();
for (Key k: (Set<Key>)keySet) {
Row[] rowToDelete = histories.findByKey(k, 1);
if (rowToDelete == null || rowToDelete.length == 0) {
throw new JboException("Failed to find row with serialized key" +
k.toStringFormat(false));
}
rowToDelete[0].remove();
getDBTransaction().commit();
}
}
}
図10-18は、SREditページのデータ・バインディングを示しています。
ServiceRequestsビュー・オブジェクト・インスタンスのServiceRequestsIterator
ServiceRequestStatusListビュー・オブジェクト・インスタンスのServiceRequestStatusListIterator
なし
ServiceRequestsIteratorイテレータ・バインディングに関連付けられているsetCurrentRowWithKey
SRServiceデータ・コントロールに関連付けられているCommit
cancelEditsToCurrentServiceRequestがSRServiceクライアント・インタフェース上のcancelEditsToCurrentServiceRequest()を起動
ページへの初回移動時(つまり、ポストバックを処理していない)で、かつ、processScope.rowKeyStr属性が設定されていない(RefreshCondition="${adfFacesContext.postback == false and not empty processScope.rowKeyStr}")場合、setRowToEditFromRequestParameterが組込みsetCurrentRowWithKey操作をモデル準備フェーズ中(Refresh="prepareModel")に起動
ServiceRequestsは、エンティティ・ベースのビュー・オブジェクトServiceRequestsのインスタンスです。メインのServiceRequestエンティティ・オブジェクトの慣用名および3つの追加の参照エンティティ・オブジェクトの慣用名、すなわちCreatedByUser(Userエンティティ・オブジェクト)、AssignedToUser(Userエンティティ・オブジェクト)およびProductからデータを結合します。
ServiceRequestStatusListは、読取り専用ビュー・オブジェクトServiceRequestStatusListのインスタンスです。そのデータは、ServiceRequestStatusListImpl.javaクラスにある静的リストから提供されます。このクラスは、静的データに基づくビュー・オブジェクトの実装に対する基本サポートを提供する、FrameworkExtensionsプロジェクトのSRStaticDataViewObjectImplを拡張します。
例10-4に示すように、cancelEditsToCurrentServiceRequest()メソッドでは、refresh()メソッドを使用して、現在のトランザクションで現在のサービス・リクエスト行に行われた編集内容を取り消します。
図10-19は、SRSearchページのデータ・バインディングを示しています。
SearchServiceRequestsビュー・オブジェクト・インスタンスのSearchServiceRequestsIterator(AlwaysFind invokeActionによって検索モードに強制的に設定)
SearchServiceRequestsビュー・オブジェクト・インスタンスのSearchServiceRequestsResultsIterator
ServiceRequestStatusListビュー・オブジェクト・インスタンスのServiceRequestStatusListIterator
なし
SearchServiceRequestsIteratorイテレータ・バインディングに関連付けられているExecute、Delete、Create
SearchServiceRequestsResultsIteratorイテレータ・バインディングに関連付けられているFind、First、Next、Previous、Last、setCurrentRowWithKey
なし
SearchServiceRequestsIteratorが検索モードでない(${bindings.SearchServiceRequestsIterator.findMode == false})場合、AlwaysFindが組込みFind操作(SearchServiceRequestsIteratorイテレータ・バインディングの場合)をモデル準備フェーズまたはモデル・レンダリング・フェーズ中(Refresh="ifNeeded")に起動
SearchServiceRequestsIteratorが検索モードでない(${bindings.SearchServiceRequestsIterator.findMode == false})場合、insertBlankViewCriteriaRowIfThereAreNoneが組込みCreate操作をモデル準備フェーズまたはモデル・レンダリング・フェーズ中(Refresh="ifNeeded")に起動
SearchServiceRequestsは、エンティティ・ベースのビュー・オブジェクトServiceRequestsのインスタンスです。メインのServiceRequestエンティティ・オブジェクトの慣用名および3つの追加の参照エンティティ・オブジェクトの慣用名、すなわちCreatedByUser(Userエンティティ・オブジェクト)、AssignedToUser(Userエンティティ・オブジェクト)およびProductからデータを結合します。
ServiceRequestStatusListは、読取り専用ビュー・オブジェクトServiceRequestStatusListのインスタンスです。そのデータは、ServiceRequestStatusListImpl.javaクラスにある静的リストから提供されます。このクラスは、静的データに基づくビュー・オブジェクトの実装に対する基本サポートを提供する、FrameworkExtensionsプロジェクトのSRStaticDataViewObjectImplを拡張します。
なし
図10-20は、SRStaffSearchページのデータ・バインディングを示しています。
StaffListByEmailNameRoleビュー・オブジェクト・インスタンスのStaffListByEmailNameRoleIterator
StaffListByEmailNameRole_Role、StaffListByEmailNameRole_EmailAddress、StaffListByEmailNameRole_TheFirstName、StaffListByEmailNameRole_TheLastName
StaffListByEmailNameRoleIteratorイテレータ・バインディングに関連付けられているExecuteWithParams
なし
なし
図10-21は、SRManageページのデータ・バインディングを示しています。
StaffWithOpenRequestsビュー・オブジェクト・インスタンスのStaffWithOpenRequestsIterator
ExpertiseAreasビュー・オブジェクト・インスタンスのExpertiseAreasIterator
OpenOrPendingServiceRequestsビュー・オブジェクト・インスタンスのOpenOrPendingServiceRequestsIterator
ServiceHistoriesForRequestビュー・オブジェクト・インスタンスのServiceHistoriesForRequestIterator
なし
StaffWithOpenRequestsIteratorイテレータ・バインディングに関連付けられているsetCurrentStaffRowWithKey(setCurrentRowWithKey組込みの場合)
setCurrentProblemAndAssigneeRowsがSRServiceクライアント・インタフェース上のsetCurrentProblemAndAssigneeRows()を起動
なし
StaffWithOpenRequestsは、読取り専用ビュー・オブジェクトStaffWithOpenRequestsのインスタンスです。情報をUSERSおよびSERVICE_REQUESTS表から問い合せます。これは、Javaクラスが関連付けられていない、XML専用のビュー・オブジェクトです。このビュー・オブジェクトは、ExpertiseAreasおよびOpenOrPendingServiceRequestsビュー・オブジェクトにリンクされているマスター/ディテールです。
ExpertiseAreasは、エンティティ・ベースのビュー・オブジェクトExpertiseAreasのインスタンスです。プライマリExpertiseAreaエンティティ・オブジェクトの慣用名および1つの参照Productエンティティ・オブジェクトの慣用名から情報を結合します。これは、Javaクラスが関連付けられていない、XML専用のビュー・オブジェクトです。
OpenOrPendingServiceRequestsは、読取り専用ビュー・オブジェクトOpenOrPendingServiceRequestsのインスタンスです。情報をSERVICE_REQUESTS表から問い合せます。これは、Javaクラスが関連付けられていない、XML専用のビュー・オブジェクトです。このビュー・オブジェクトは、ServiceHistoriesビュー・オブジェクトにリンクされているマスター/ディテールです。
ServiceHistoriesForRequestは、エンティティ・ベースのビュー・オブジェクトServiceHistoriesのインスタンスです。メインのServiceHistoryエンティティ・オブジェクトの慣用名および1つの追加の参照エンティティ・オブジェクトの慣用名、すなわちSystemUser(Userエンティティ・オブジェクト)からデータを結合します。これは、カスタムJavaクラスを持たない、XML専用のビュー・オブジェクトです。
例10-5に示すように、setCurrentProblemAndAssigneeRows()メソッドでは、ヘルパー・メソッドを使用して、渡されたシリアライズされた文字列キーに基づき、StaffWithOpenRequestsビュー・オブジェクト・インスタンスおよびOpenOrPendingServiceRequestsビュー・オブジェクト・インスタンスに現在の行を設定します。
図10-22は、SRSkillsページのデータ・バインディングを示しています。
StaffListビュー・オブジェクト・インスタンスのStaffListIterator
StaffExpertiseAreasビュー・オブジェクト・インスタンスのStaffExpertiseAreasIterator
ProductListビュー・オブジェクト・インスタンスのProductListIterator
なし
なし
updateSkillsForCurrentStaffがSRServiceクライアント・インタフェース上のupdateSkillsForCurrentStaff()メソッドを起動
なし
StaffListは、エンティティ・ベースのビュー・オブジェクトStaffListのインスタンスです。プライマリ・エンティティ・オブジェクトの慣用名SystemUser(Userエンティティ・オブジェクト)から情報を問い合せます。このビュー・オブジェクトは、ExpertiseAreasビュー・オブジェクトにリンクされているマスター/ディテールです。
StaffExpertiseAreasは、エンティティ・ベースのビュー・オブジェクトExpertiseAreasのインスタンスです。プライマリExpertiseAreaエンティティ・オブジェクトの慣用名および1つの参照Productエンティティ・オブジェクトの慣用名から情報を結合します。これは、Javaクラスが関連付けられていない、XML専用のビュー・オブジェクトです。
ProductListは、エンティティ・ベースのビュー・オブジェクトProductListのインスタンスです。プライマリ・エンティティ・オブジェクトの慣用名Products(Productエンティティ・オブジェクト)からデータを問い合せます。これは、Javaクラスが関連付けられていない、XML専用のビュー・オブジェクトです。
例10-6に示すように、updateSkillsForCurrentStaff()メソッドでは、次の手順を実行します。
製品IDのリストをクローニングします。
ExpertiseAreasの反復処理をプログラムで行うセカンダリRowSetIteratorを作成します。
製品IDのリストにない製品の現在のユーザーの行を削除します。
反復処理の実行時に2次行セット・イテレータをクローズします。
残っているキー用に新規行を追加します。
トランザクションをコミットします。
例10-6 SRServiceImpl.javaのUpdateSkillsForCurrentStaffメソッド
public void updateSkillsForCurrentStaff(List productIds) {
if (productIds != null && productIds.size() > 0) {
// 1. Cone the list of product ids
List<Number> copyOfProductIds = (List<Number>)Utils.cloneList(productIds);
ViewObject skills = getStaffExpertiseAreas();
// 2. Create a secondary rowset iterator for iteration
RowSetIterator rsi = skills.createRowSetIterator(null);
// 3. Remove rows for current user for products not in list of products
while (rsi.hasNext()) {
Row r = rsi.next();
Number productId = (Number)r.getAttribute("ProdId");
// if the existing row is in the list, we're ok, so remove from list.
if (copyOfProductIds.contains(productId)) {
copyOfProductIds.remove(productId);
}
// if the existing row is in not list, remove it.
else {
r.remove();
}
}
// 4. Close the secondary row set iterator when we're done
rsi.closeRowSetIterator();
// 5. Add new rows for the keys that are left
for (Number productIdToAdd: copyOfProductIds) {
Row newRow = skills.createRow();
skills.insertRow(newRow);
newRow.setAttribute("ProdId", productIdToAdd);
}
// 6. Commit the transaction
getDBTransaction().commit();
}
}
|
注意: ADF Modelレイヤーのイテレータ・バインディングでは、関連付けられているビュー・オブジェクト・インスタンスのデフォルト行セットのデフォルト行セット・イテレータへのバインドをデフォルトで行うため、ビュー・オブジェクトの行セットの反復処理をプログラムで実行する2次行セット・イテレータをアプリケーション・ビジネス・ロジックに作成することをお薦めします。この方法では、ユーザー・インタフェースでユーザーに表示される現在の行が影響を受けません。2次イテレータには開発者が名前を割り当てることができますが、nullを渡すと、名前が自動的に割り当てられます。通常は、反復処理の終了と同時にこのイテレータをクローズするため、名前を手動で割り当てる必要は特にありません。 |
図10-23は、SRCreateページのデータ・バインディングを示しています。
Globalsビュー・オブジェクト・インスタンスのGlobalsIterator
ProductListビュー・オブジェクト・インスタンスのProductListIterator
なし
なし
cancelNewServiceRequestがSRServiceクライアント・インタフェース上のcancelNewServiceRequestメソッドを起動
ページでポストバック・イベントを処理していない場合で、かつ、requestScope.processChoice属性が設定されていない(RefreshCondition="${adfFacesContext.postback == false and empty requestScope.processChoice}")場合、clearServiceRequestFieldsIfNotInTrainがcancelNewServiceRequestメソッド・アクション・バインディングをモデル準備フェーズ中(Refresh="prepareModel")に起動
ProductListは、エンティティ・ベースのビュー・オブジェクトProductListのインスタンスです。プライマリ・エンティティ・オブジェクトの慣用名Products(Productエンティティ・オブジェクト)からデータを問い合せます。これは、Javaクラスが関連付けられていない、XML専用のビュー・オブジェクトです。
Globalsは、一時ビュー・オブジェクトGlobalsのインスタンスです。ページ全体のProductId、ProductNameおよびProductDescriptionに関する情報を一時的に格納する一時属性を含んでいます。
|
注意: 一時ビュー・オブジェクトは、SQL問合せを持たない、一時属性のみのオブジェクトです。このオブジェクトには、アプリケーション・モジュールのビジネス・ロジックによってプログラムで移入される行、またはADF Modelレイヤーの組込み操作のアクション・バインディングを使用して宣言的に移入される行を含めることができます。Oracle Formsを使い慣れているユーザーは、これをFormsの非データベース・ブロックに類似したものと考えることができます。 |
例10-7に示すように、cancelNewServiceRequest()メソッドにより、Globalsビュー・オブジェクト・インスタンスに単一の空白行が用意されます。
図10-24は、SRConfirmCreateページのデータ・バインディングを示しています。
Globalsビュー・オブジェクト・インスタンスのGlobalsIterator
LoggedInUserビュー・オブジェクト・インスタンスのLoggedInUserIterator
なし
なし
cancelNewServiceRequestがSRServiceクライアント・インタフェース上のcancelNewServiceRequest()を起動
createServiceRequestがSRServiceクライアント・インタフェース上のcreateServiceRequest()を起動
なし
Globalsは、一時ビュー・オブジェクトGlobalsのインスタンスです。ページ全体のProductId、ProductNameおよびProductDescriptionに関する情報を一時的に格納する一時属性を含んでいます。
LoggedInUserは、エンティティ・ベースのビュー・オブジェクトLoggedInUserのインスタンスです。プライマリ・エンティティ・オブジェクトの慣用名Users(Userエンティティ・オブジェクト)からデータを問い合せます。LoggedInUserビュー・オブジェクトは、ServiceRequestsByStatusビュー・オブジェクトにリンクされているマスター/ディテールです。
例10-8に示すように、createServiceRequest()メソッドでは、次の基本手順を実行します。
ServiceRequestのエンティティ定義を取得します。
新規ServiceRequestエンティティ行(ServiceRequestImplクラス)を作成します。
Globalsの現在の行に強く型付けされたGlobalsRowImplとしてアクセスします。
新しいサービス・リクエストの問題の説明と製品IDを設定します。
トランザクションをコミットします。
データベース・トリガーによって割り当てられた新しいサービス・リクエストのSR番号を表す整数を戻り値として提供します。
例10-8 SRServiceImpl.javaのCreateServiceRequestメソッド
public Integer createServiceRequest() {
// 1. Get the entity definition for ServiceRequest
EntityDefImpl svcReqDef = ServiceRequestImpl.getDefinitionObject();
// 2. Create a new ServiceRequest entity row
ServiceRequestImpl newReq =
(ServiceRequestImpl)svcReqDef.createInstance2(getDBTransaction(),null);
// 3. Access the current row in Globals as a strongly-typed GlobalsRowImpl
GlobalsRowImpl globalsRow = (GlobalsRowImpl)getGlobals().getCurrentRow();
// 4. Set the problem description and product id for new service request
newReq.setProblemDescription(globalsRow.getProblemDescription());
newReq.setProdId(globalsRow.getProductId());
// 5. Commit the transaction
getDBTransaction().commit();
// 6. Return an integer representing the database-assigned SR Number
return newReq.getSvrId().getSequenceNumber().intValue();
}