この章では、ADFビュー・オブジェクトによってカプセル化されたSQL問合せを使用して、データベースから問い合せる方法について説明します。
この章の内容は次のとおりです。
ビュー・オブジェクトは、SQL問合せをカプセル化し、その結果の作業を簡略化する、Oracle ADFコンポーネントです。この章の終わりには、図5-1に示す次の概念をすべて理解できるようになります。
SQL問合せを提供することにより、ビュー・オブジェクトを定義します。
ビュー・オブジェクト・インスタンスは、問合せのデータベース・トランザクションを提供するアプリケーション・モジュールと関連して使用します。
ビュー・オブジェクトを1つ以上の他のビュー・オブジェクトにリンクして、マスター/ディテール階層を作成できます。
実行時には、ビュー・オブジェクトが問合せを実行し、行の行セットを生成します。
各行は、対応する行キーによって識別されます。
行セット・イテレータを使用して、行セットの行を反復します。
一連のQuery-By-Example基準行を適用すると、ビュー・オブジェクトにより生成される行セットをフィルタリングできます。
|
注意: この章の例の動作バージョンを体験するには、サンプルのダウンロード・ページ(http://otn.oracle.com/documentation/jdev/b25947_01/)からDevGuideExamplesワークスペースをダウンロードし、QueryingDataWithViewObjectsプロジェクトを参照してください。 |
ビュー・オブジェクトはデータの読取りおよびデータの更新に使用できます。この章では、ビュー・オブジェクトによる読取り専用データの使用に重点を置いて説明します。第7章「エンティティ・ベースのビュー・オブジェクトを使用した更新可能なデータ・モデルの構築」では、データの更新処理が可能なビュー・オブジェクトの作成方法について学習します。
ビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。このウィザードは、「新規ギャラリ」の「Business Tier」→「ADF Business Components」カテゴリから起動できます。これがプロジェクトで作成する最初のコンポーネントである場合、「ビジネス・コンポーネント・プロジェクトの初期化」ダイアログが表示され、データベース接続を選択できます。これらの例では、SRDEMOスキーマについてSRDemoという名前の接続を使用していることが前提です。
図5-2に示すように、パッケージ名およびビュー・オブジェクト名を入力し、このビュー・オブジェクトが読取り専用アクセスでデータを管理することを示します。この図は、devguide.examplesパッケージでのUsersという名前のビュー・オブジェクトの作成を示しています。
ウィザードのステップ2(「SQL文」ページ)で、任意の有効なSQL文を「問合せ文」ボックスに貼り付けるか、または「クエリー・ビルダー」をクリックして、対話型クエリー・ビルダーを使用します。図5-3は、EMAILによって順序付けられたUSERS表からユーザー情報の列をいくつか取得するための問合せを示しています。
|
注意: ここに示されている「SQL文」ページではなく、「エンティティ・オブジェクト」ページが表示される場合、ステップ1に戻り、「読取り専用アクセス」を選択してあることを確認してください。 |
問合せはバインド変数を参照しないため、ここではステップ3の「バインド変数」ページを省略できます。5.9項「名前付きバインド変数の使用」で、バインド変数を追加し、問合せでのバインド変数の使用方法を確認します。
SQL問合せ情報に加えて、ビュー・オブジェクトでは、それ自体の問合せのSELECTリストにあるそれぞれの式の名前とデータ型に関する情報を取得できます。5.6項「ビュー・オブジェクト問合せ結果のプログラム的操作」で示すように、これらのビュー・オブジェクト属性名を使用して、ビュー・オブジェクトの結果セットの任意の行のデータに名前別にアクセスできます。SQL列名はJavaで属性名として直接使用できますが、一般に、SQL名はすべて大文字であり、多くの場合に下線で区切られた複数の単語で構成されます。ビュー・オブジェクト・ウィザードは、これらのSQLに適した名前をJavaに適した名前に変換します。
「属性マッピング」ページのステップ4では、図5-4に示すように、ウィザードによってデフォルトで作成された、Javaに適したビュー・オブジェクト属性名がSELECTリスト名とどのように対応しているかを確認できます。SOME_COLUMN_NAMEのように下線で区切られた列名の各部分は、SomeColumnNameのような単語の最初を大文字にした属性名に変えられます。ビュー・オブジェクト属性名はSELECTリストの基礎となる問合せ列に対応しますが、ビュー・オブジェクト・レベルでの属性名とは必ずしも一致しません。ビュー・オブジェクト属性の名前は、基礎となる問合せを変更せずに、後でより適切な名前に変更できます。
|
注意: ADF Business Componentsウィザードおよびエディタの全体のデフォルトの規則では、読みやすくするために最初の文字を大文字にし、名前が複数の単語で構成される場合には名前の途中で大文字を使用する「CamelCapped」の属性名を使用します。 |
ここで「終了」をクリックし、ビュー・オブジェクトを作成します。
ビュー・オブジェクトを作成する際、JDeveloperでは最初にSELECTリストの列から次を推測する問合せが記述されます。
Javaに適したビュー属性名(USER_ID -> UserIdなど)
各属性のSQLおよびJavaデータ型
次に、JDeveloperでビュー・オブジェクトの宣言設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリ内に格納されます。前述の例では、devguide.examplesパッケージでビュー・オブジェクトにUsersという名前が付けられたため、作成されるXMLファイルはプロジェクトのソース・パスにある./devguide/examples/Users.xmlになります。このXMLファイルには、入力したSQL問合せが、名前、データ型および各属性のその他のプロパティとともに含まれます。この内容を確認する場合は、アプリケーション・ナビゲータでビュー・オブジェクトを選択し、構造ウィンドウで対応するソース・フォルダを調べると、そのビュー・オブジェクトのXMLファイルを参照できます。Users.xmlノードをダブルクリックすると、このXMLがエディタで開かれ、その内容を確認できます。
|
注意: IDEレベルのビジネス・コンポーネントJava生成設定で指定されている場合、オプションのカスタム・ビュー・オブジェクト・クラスUsersImpl.javaまたはカスタム・ビュー行クラスUsersRowImpl.java(あるいはその両方)もウィザードで生成される場合があります。 |
一般に、アプリケーションで実行するそれぞれのSQL問合せに対して、1つのビュー・オブジェクトを作成します。
ビュー・オブジェクトを作成した後、ビュー・オブジェクト・エディタを使用してその設定を編集できます。アプリケーション・ナビゲータのポップアップ・メニューで「編集」メニュー・オプションを選択するか、ビュー・オブジェクトをダブルクリックし、ダイアログを起動します。エディタの様々なパネルを開き、SQL問合せの調整、属性名の変更、名前付きバインド変数の追加、UIコントロール・ヒントの追加、Java生成オプションの制御、および後の章で説明されるその他の設定を行うことができます。
SQL問合せに次のような計算式が含まれる場合。
select USER_ID, EMAIL,
SUBSTR(FIRST_NAME,1,1)||'. '||LAST_NAME
from USERS
order by EMAIL
SQLの別名を使用して、列にJavaに適した名前を付ける際のビュー・オブジェクトの作成ウィザードを支援します。
select USER_ID, EMAIL,
SUBSTR(FIRST_NAME,1,1)||'. '||LAST_NAME AS USER_SHORT_NAME
from USERS
order by EMAIL
図5-5に示すように、ビュー・オブジェクト・エディタで特定の属性名を選択することにより、その実行時動作を制御する宣言設定の値を参照および変更できます。重要なプロパティの1つに、「問合せ列」セクションの「型」があります。このプロパティは、VARCHAR2列の長さ情報やNUMBER列の精度およびスケール情報を含む、列のSQL型を記録します。JDeveloperエディタでは、列の型が自動的に推測されますが、一部のSQL式では推測された値がVARCHAR2(255)になることがあります。このような属性に対する「型」の値を更新し、正しい長さがわかっている場合は、その長さを反映します。たとえば、図5-5でFirstName属性に対する「型」 として示されているVARCHAR2(30)は、最大文字数が30文字であることを意味しています。NUMBER列の場合、小数点以下7桁の精度と2桁のスケールを持つ属性について、NUMBER(7,2)の「型」を示します。
作成するビュー・オブジェクトはすべて、1つ以上のアプリケーション・モジュールにおいて使用できる再使用可能なコンポーネントであり、そのアプリケーション・モジュールのトランザクションにおいてカプセル化される問合せを実行します。アプリケーション・モジュールで使用される一連のビュー・オブジェクトは、それ自体のデータ・モデル、つまり、クライアントがユーザー・インタフェースを介して表示および操作できる一連のデータを定義します。簡単に開始するには、アプリケーション・モジュールを作成し、前述のアプリケーション・モジュールのデータ・モデルで作成した単一のビュー・オブジェクトを使用します。
アプリケーション・モジュールを作成するには、アプリケーション・モジュールの作成ウィザードを使用します。このウィザードは、「新規ギャラリ」の「Business Tier」→「ADF Business Components」カテゴリから起動できます。図5-6に示すように、パッケージ名およびアプリケーション・モジュール名を指定します。この図は、devguide.examplesパッケージでのアプリケーション・モジュールUserServiceの作成を示しています。
「データ・モデル」ページのステップ2では、最初は図5-7に示すようにデータ・モデルは空です。つまり、ビュー・オブジェクト・インスタンスはまだ含まれていません。「選択可能なビュー・オブジェクト」リストでは、プロジェクト内の使用可能なすべてのビュー・オブジェクトがパッケージ別に示されています。
ビュー・オブジェクトのインスタンスをデータ・モデルに追加するには、最初に「選択可能なビュー・オブジェクト」リストでインスタンスを選択します。リストの下の「名前」フィールドに、データ・モデルに追加するビュー・オブジェクトの次のインスタンスを識別する際に使用される名前が示されます。インスタンスの追加ボタン(>)を押す前に別の名前を入力すると、名前を自由に変更できます。最後に、下に表示されたインスタンス名によって識別される、選択したビュー・オブジェクトのインスタンスをデータ・モデルに追加するには、インスタンスの追加ボタン(>)をクリックします。
図5-8では、インスタンス名をUserListにすると想定した場合、インスタンス追加後に「データ・モデル」ページがどのように表示されるかを示します。「データ・モデル」リストの選択済ビュー・オブジェクト・インスタンスの下の「インスタンス名」フィールドでは、必要に応じてインスタンス名を参照および変更できます。「定義」フィールドには、実行時にこのビュー・オブジェクト・インスタンスを作成する際に使用する、ビュー・オブジェクト・コンポーネントの完全修飾名が表示されます。予測どおり、使用される定義はdevguide.examples.Usersビュー・オブジェクトです。
ビュー・オブジェクト・コンポーネントとビュー・オブジェクト・インスタンスの相違点を理解することは非常に重要です。この相違点を理解する最も簡単な方法は、最初にビジュアル的な例で検討することです。2つのボタンを含むJavaユーザー・インタフェースを構築する必要があると仮定します。JDeveloperのビジュアル・エディタでは、コンポーネント・パレットを使用して図5-9に示すJButtonコンポーネントを選択するページを作成し、クリックしてJButtonコンポーネントをパネルに追加します。2回目も同じステップを繰り返して、パネルに別のボタンをドロップできます。JButtonコンポーネントの2つのインスタンスを使用する、カスタムJPanelコンポーネントを設計しています。パネルは、JButtonクラスの2つのインスタンスを使用しているのみであり、このクラスを所有していません。
設計中のこの新規パネルのJavaコードを簡単に参照したのみでも、パネルで使用されているボタンの2つのインスタンスへの参照を保持するJButtonタイプのメンバー・フィールドが2つあることがわかります。コード内の2つのインスタンスを区別するため、メンバー・フィールドの1つにmyButtonという名前を付け、もう1つのメンバー・フィールドにはanotherButtonという名前を付けます。
例5-1 Jbuttonコンポーネントの2つのインスタンス
public class Panel1 extends JPanel implements JUPanel {
private JButton myButton = new JButton(); // First instance
private JButton anotherButton = new JButton(); // Second instance
// etc.
}
アプリケーション・モジュールが非ビジュアル的なコンポーネントの場合でも、コンポーネント、インスタンスおよび固有のメンバー名について同じ直観を適用すると、概念をより理解しやすくなります。アプリケーション・モジュールの設計時に、ビュー・オブジェクト・コンポーネントのインスタンスを使用してそのデータ・モデルを定義します。図5-10は、UserServiceアプリケーション・モジュールのJDeveloperビジネス・コンポーネント・ダイアグラムを示しています。例5-1のパネルでJButtonコンポーネントのインスタンスに、区別するためのmyButtonおよびanotherButtonというメンバー名が付けられているように、アプリケーション・モジュールにも、Usersビュー・オブジェクト・コンポーネントの2つのインスタンスに、区別するためのUserListおよびAnotherUserListというメンバー名が付けられています。実行時に、この2つのJButtonインスタンスは両方とも同じ定義に基づきます。これで、プロパティ・セットが両方とも同じであり、両方がJButtonのような動作を示す理由の説明がつきます。ただし、PositionとTextのようなプロパティの値は異なります。UserServiceアプリケーション・モジュールのUsersビュー・オブジェクトの複数のインスタンスについても同様です。実行時に、両方のインスタンスが同じUsersビュー・オブジェクト・コンポーネント定義を共有します。つまり、必ず同じ属性構造とユーザー・ビュー・オブジェクト動作があります。ただし、異なるユーザーに関するデータを取得するため、それぞれが独立して使用されることがあります。たとえば、追加フィルタリングのWHERE句やバインド変数の値のような実行時プロパティの一部には、2つの固有のインスタンスでは異なる場合があります。
例の1つがビジュアル・パネルであり、別の例がビジュアルでないデータ・モデル・コンポーネントであるという明白な事実以外の論理的な相違点は、インスタンスとメンバー名が定義される方法のみです。例5-1のビジュアル・パネルでは、独特なJButtonインスタンスを保持する2つのメンバー・フィールドがコードで宣言されたことを示します。対照的に、UserServiceアプリケーション・モジュールは、そのXMLコンポーネント定義ファイルでメンバー・ビュー・オブジェクト・インスタンスを定義します。
アプリケーション・モジュールを作成すると、JDeveloperでは、その宣言設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリに保存されます。図5-6の例では、アプリケーション・モジュールはdevguide.examplesパッケージでUserServiceという名前が付けられるため、作成されるXMLファイルは、プロジェクトのソース・パスの./devguide/examples/UserService.xmlになります。このXMLファイルには、ビュー・オブジェクト・インスタンスをアプリケーション・モジュールのデータ・モデルで再作成するために実行時に必要な情報が含まれています。この内容を確認する場合は、アプリケーション・ナビゲータでビュー・オブジェクトを選択し、構造ウィンドウで対応するソース・フォルダを調べると、そのアプリケーション・モジュールのXMLファイルを参照できます。UsersService.xmlノードをダブルクリックすると、このXMLがエディタで開かれ、その内容を確認できます。
|
注意: IDEレベルのビジネス・コンポーネントのJava生成設定で指定されている場合、オプションのカスタム・アプリケーション・モジュール・クラスUsersServiceImpl.javaもウィザードで生成される場合があります。 |
アプリケーション・モジュールを作成した後、アプリケーション・モジュール・エディタを使用してその設定を編集できます。アプリケーション・ナビゲータのポップアップ・メニューで「編集」メニュー・オプションを選択して、アプリケーション・モジュールを起動します。エディタの様々なパネルを開き、データ・モデル内のビュー・オブジェクト・インスタンスの調整、Java生成オプションの制御、および後の章で学習するその他の設定を行うことができます。
ランタイム構成プロパティの複数のセットを定義して使用することは有用であるため、各アプリケーション・モジュールでは、複数の名前付きランタイム構成をサポートしています。アプリケーション・モジュールを作成する際、JDeveloperではアプリケーション・モジュールのランタイム構成プロパティのデフォルト・セットが作成されます。YourServiceという名前のアプリケーション・モジュールの場合、構成プロパティのデフォルト・セットには、YourServiceLocalという名前が付けられます。これらの設定は、アプリケーション・モジュールのXMLコンポーネント定義が格納される場所に相対する、commonというサブディレクトリのbc4j.xcfgというXMLファイルに格納されます。たとえば、devguide.examplesパッケージの上にUserServiceアプリケーション・モジュールを作成した場合、JDeveloperではプロジェクトのソース・パスの./devguide/examples/commonディレクトリにファイルbc4j.xcfgが作成されます。
アプリケーション・モジュール構成マネージャを使用して、既存の構成を編集するか、新規構成を作成できます。構成マネージャにアクセスするには、アプリケーション・ナビゲータで該当するアプリケーション・モジュールを選択し、ポップアップ・メニューから「構成」を選択します。図5-11に示すように、「Configuration Manager」ダイアログが表示されます。UserServiceアプリケーション・モジュールに対してデフォルトのUserServiceLocal構成を参照できます。作成する追加構成、または編集する構成プロパティは、いずれも同じbc4j.xcfgに格納されます。
「Configuration Manager」で編集ボタンをクリックして、特定の構成を編集します。図5-12に示すように、このエディタにより、データベース接続情報、プーリングとスケーラビリティ設定の数、および最初の2つのタブでは扱わない残りのプロパティの最終タブを構成できます。すべてのランタイム・プロパティおよびその意味は、JDeveloperオンライン・ヘルプで説明しています。
ADF Business Componentsの数ある強力な組込み機能の1つに、属性についてのコントロール・ヒントを定義する機能があります。コントロール・ヒントは、ロケールに依存した一貫性のある方法で問合せ情報を自動表示する際にビュー・レイヤーで使用される、追加の属性設定です。ヒントの格納は、多言語アプリケーションを容易にローカライズできる方法で管理されます。
属性のコントロール・ヒントをUserListビュー・オブジェクトの属性に追加するには、ビュー・オブジェクト・エディタを開き、左側のツリーで「属性」ノードを開き、ビュー・オブジェクトの属性のリストを表示します。図5-13に示すように、UserIdなどの特定の属性名を選択し、「コントロール・ヒント」タブを選択すると、Idなどの「ラベル・テキスト」ヒントの値を入力できます。また、「フォーマットの種類」をNumberに設定すると、「フォーマット」マスクに00000と入力できます。続いてEmail、FirstNameおよびLastNameなどの他の属性のそれぞれに対して電子メール・アドレス、名および姓など、「ラベル・テキスト」ヒントを定義できます。
|
注意: Javaで定義される数値および日付のフォーマット・マスクの標準セットは、OracleデータベースのSQLおよびPL/SQL言語によって使用されるものとは異なります。詳細は、java.text.DecimalFormatおよびjava.text.SimpleDateFormatクラスのJavadocを参照してください。 |
ビュー・オブジェクトの属性のコントロール・ヒントを定義すると、JDeveloperによりこれらを格納する標準のJavaメッセージ・バンドル・ファイルが作成されます。そのファイルは、関連付けられているビュー・オブジェクト・コンポーネントに固有のもので、このコンポーネントに基づいて名前が付けられます。devguide.examplesパッケージ内のUserListビュー・オブジェクトの場合、作成されるメッセージ・バンドル・ファイルの名前はUserListRowImplMsgBundle.javaになり、devguide.examples.commonサブパッケージに作成されます。アプリケーション・ナビゲータでUserListを選択すると、各ビジネス・コンポーネントの実装ファイルのグループを示す構造ウィンドウのソース・フォルダに、この新しいファイルが追加されていることを確認できます。例5-3に、メッセージ・バンドル・ファイルにコントロール・ヒント情報がどのように表示されるかを示します。各String配列の最初のエントリはメッセージ・キーです。2番目のエントリは、このキーに対応するロケール固有のString値です。
例5-3 ビュー・オブジェクト・コンポーネントのメッセージ・バンドル・クラスに格納されるロケールに依存したコントロール・ヒント
package devguide.examples.common;
import oracle.jbo.common.JboResourceBundle;
// ---------------------------------------------------------------------
// --- File generated by Oracle ADF Business Components Design Time.
// ---------------------------------------------------------------------
public class UsersRowImplMsgBundle extends JboResourceBundle {
static final Object[][] sMessageStrings =
{
{ "UserId_LABEL", "Id" },
{ "UserId_FMT_FORMATTER", "oracle.jbo.format.DefaultNumberFormatter" },
{ "UserId_FMT_FORMAT", "00000" },
{ "Email_LABEL", "Email Address" },
{ "FirstName_LABEL", "Given Name" },
{ "LastName_LABEL", "Surname" }
};
ADF Business Componentsを使用して作成されたアプリケーションのモデル・レイヤーを国際化するには、各コンポーネントのメッセージ・バンドル・ファイルの翻訳バージョンを作成する必要があります。たとえば、UsersRowImplMsgBundleメッセージ・バンドルのイタリア語バージョンはUsersRowImplMsgBundle_itという名前のクラスになり、より限定されたスイス・イタリア語バージョンはUsersRowImplMsgBundle_it_chになります。通常、これらのクラスにより、ベース・メッセージ・バンドル・クラスが拡張されます。また、これらのクラスには、ローカライズが必要なメッセージ・キーのエントリとともに、ローカライズされたこれらの翻訳が含まれます。たとえば、イタリア語ロケールに対する数値フォーマット・マスクの翻訳が不要であったと仮定すると、UserList ビュー・オブジェクトのメッセージ・バンドルのイタリア語バージョンは、例5-4のようになります。オーバーライドされたgetContents()メソッドに注意してください。このメソッドは、スーパークラス・バンドルからオーバーライドされていない文字列にマージされた、より限定された翻訳文字列によるメッセージ配列を戻します。実行時には、現在のユーザーのロケール設定に基づいて適切なメッセージ・バンドルが自動的に使用されます。
例5-4 イタリア語にローカライズされたビュー・オブジェクト・コンポーネントのメッセージ・バンドル
package devguide.examples.common;
import oracle.jbo.common.JboResourceBundle;
public class UsersRowImplMsgBundle_it extends UsersRowImplMsgBundle {
static final Object[][] sMessageStrings =
{
{ "UserId_LABEL", "Codice Utente" },
{ "Email_LABEL", "Indirizzo Email" },
{ "FirstName_LABEL", "Nome" },
{ "LastName_LABEL", "Cognome" }
};
// merge this message bundles messages with those in superclass bundle
public Object[][] getContents() {
return super.getMergedArray(sMessageStrings, super.getContents());
}
}
JDeveloperには、アプリケーション・ユーザー・インタフェースを使用せず、またはテスト・クライアントのプログラムを記述せずに、データ・モデルのすべての面をテストできるようにする、対話型アプリケーション・モジュール・テスト・ツールが用意されています。これは一般的には、開発時にビジネス・サービスのデータ機能を実行する最も迅速な方法です。
アプリケーション・モジュールをテストするには、アプリケーション・ナビゲータでこのモジュールを選択し、ポップアップ・メニューから「テスト」を選択します。図5-14に示すように、 「Business Component Browser: 接続」ダイアログが表示されます。ダイアログの右上隅にある「ビジネス・コンポーネントの構成名」リストにより、実行するテスター・ツールに対してアプリケーション・モジュールの構成を選択できます。「接続」をクリックし、選択した構成を使用してアプリケーション・モジュールを起動します。
Business Component Browserを起動する場合、JDeveloperでは別のプロセスでテスター・ツールが起動され、Business Component Browserが表示されます。ダイアログの左側のツリーには、アプリケーション・モジュールのデータ・モデルのすべてのビュー・オブジェクト・インスタンスが表示されます。図5-15には、UserListと呼ばれるインスタンスが1つのみあります。ツリーでUserListビュー・オブジェクト・インスタンスをダブルクリックすると、これまでのテスト・セッションで実行されていなかった場合はビュー・オブジェクトが実行され、図5-15に示すように問合せ結果を検査するパネルが表示されます。ビュー・オブジェクト・ノード上に表示される追加ポップアップ・メニュー項目では、テスター・パネルの削除、およびその他タスクの実行のため、必要に応じて問合せを再実行できます。
ビュー・オブジェクトが読取り専用のため、UserListビュー・オブジェクト・インスタンスに対して、ビュー・オブジェクトのテスト・パネルのフィールドが無効になっています。7.5項「エンティティ・ベースのビュー・オブジェクトの対話型テスト」では、ビュー・オブジェクトの行の挿入、更新および削除を試行し、テスター・ツールの重要性をより深く理解します。ただし、読取り専用のビュー・オブジェクトの場合でも、ツールはいくつかの有用な機能を備えています。まず、UIヒントとフォーマット・マスクが正しく定義されているかを検証できます。属性は定義された「ラベル・テキスト」 コントロール・ヒントとともにプロンプトとして表示され、UserIdフィールドは、5.4.1項「属性コントロール・ヒントの追加方法」で定義された00000フォーマット・マスクのため00303として表示されます。次に、ツールバー・ボタンを使用してデータをスクロールできます。
3番目に、Query-By-Example基準を入力して、検査対象のデータのある特定の行を検索します。ツールバーで「ビュー基準の指定」ボタンをクリックすると、図5-16に示すように、「ビジネス・コンポーネント・ビュー基準」ダイアログが表示されます。「姓」属性にH%などの問合せ基準を入力して「検索」をクリックし、検索を文字Hで始まる姓のユーザー(Hunold、Himuro、HartsteinおよびHiggins)のみに絞り込むことができます。
Business Component Browserを使用する際は、現在の実行に対する構成オプションをカスタマイズできます。また、ADF Business Componentデバッグ診断を有効にして、コンソールにメッセージを出力できます。これらの機能は両方とも、アプリケーションの様々な部分のテストや問題の検出に役立ちます。
図5-14に示すように、Business Component Browserの「接続」ダイアログで、事前定義された構成を選択し、実行時構成プロパティの名前付きセットを使用してツールを実行できます。また、「接続」ダイアログには、選択済の構成設定の参照、およびブラウザの現在の実行に対する構成の設定のオーバーライドが可能な「プロパティ」タブがあります。たとえば、「プロパティ」タブを開いて次の2つのプロパティを設定することにより、単一のBusiness Component Browser実行でUIコントロール・ヒントのイタリア語翻訳をテストできます。
jbo.default.country = IT
jbo.default.language = it
永続的な変更を行う場合は、Configuration Managerを使用して、現在のUserServiceLocal構成をコピーし、これらの2つの追加プロパティ・セットを持つUserServiceLocalItalianを新たに作成できます。この方法により、イタリア語のテストを行う場合はいつでも、デフォルトのUserServiceLocal構成ではなくUserServiceLocalItalian構成を使用するように選択するだけですみます。
Business Component Browserを起動する際、プロジェクトの現在の実行構成がJavaシステム・パラメータjbo.debugoutput=consoleを含むように設定されている場合、コンソールに移動するよう指示するメッセージとともに、ADF Business Componentsデバッグ診断を有効化できます。これらはJDeveloperのログ・ウィンドウに表示されます。
|
注意: 名前は類似していますが、JDeveloperプロジェクトの実行構成は、ADFアプリケーション・モジュールの構成とは異なります。JDeveloperの構成はプロジェクト・プロパティの一部であり、ADFアプリケーション・モジュールの構成はbc4j.xcfgファイル内のアプリケーション・モジュール・コンポーネントとともに定義され、構成エディタを使用して編集されます。 |
前述のシステム・プロパティを設定するには、モデル・プロジェクトの「プロジェクト・プロパティ」ダイアログの「実行/デバッグ」ページを開きます。「編集」をクリックして、選択した実行構成を編集し、次の文字列を追加します。
-Djbo.debugoutput=console
追加先はパネルの「Javaオプション」フィールドです。次回、Business Component Browserを実行し、UserListビュー・オブジェクトをダブルクリックすると、コンソールに詳細な診断結果が表示されます。
例5-5 Business Component Browserの診断結果
: [234] Created root application module: 'devguide.examples.UserService' [235] Stringmanager using default locale: 'en_US' [236] Locale is: 'en_US' [237] ApplicationPoolImpl.resourceStateChanged wasn't release related. [238] Oracle SQLBuilder: Registered driver: oracle.jdbc.driver.OracleDriver [239] Creating a new pool resource [240] Trying connection/2: url='jdbc:oracle:thin:@localhost:1521:XE' ... [241] Successfully logged in [242] JDBCDriverVersion: 10.1.0.5.0 [243] DatabaseProductName: Oracle [244] DatabaseProductVersion: Oracle Database 10g Release 10.2.0.1.0 [245] Column count: 4 [246] ViewObject: UserList Created new QUERY statement [247] UserList>#q computed SQLStmtBufLen: 110, actual=70, storing=100 [248] select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS order by EMAIL :
診断を使用して、アプリケーションに対するフレームワーク・コンポーネントの処理をすべて参照できます。
このプロパティの他の有効な値は、silent(指定されていない場合、デフォルト)およびfileです。fileオプションを選択すると、診断はsystem tempディレクトリに書き込まれます。ベスト・プラクティスとしては、複数のJDeveloper実行構成の作成がありますが、これにはADF Business Componentsデバッグ診断の設定をオンにして作成する場合と、オフにして作成する場合があります。このため、適切なプロジェクト実行構成を選択することにより、デバッグ診断を参照するかしないかを簡単に変更できます。
UserListという名前のインスタンスを含む操作中のアプリケーション・モジュールがあるため、単純なテスト・クライアント・プログラムを構築して、UserListビュー・オブジェクト・インスタンスにあるデータを使用したプログラムによる操作の基礎を示すことができます。
oracle.jboパッケージのViewObjectインタフェースで、任意のデータ取得タスクの操作を迅速に行うメソッドが提供されます。例に使用されるこれらのメソッドの一部には、次のものが含まれます。
executeQuery(): ビュー・オブジェクトの問合せを実行し、その結果の行セットを移入します。
setWhereClause(): 実行時に動的条件を追加して検索を絞り込みます。
setNamedWhereClauseParam(): 名前付きバインド変数の値を設定します。
hasNext(): 行セット・イテレータが結果の最終行に到達したかどうかをテストします。
next(): 行セット・イテレータを行セット内の次の行へ進めます。
getEstimatedRowCount(): ビュー・オブジェクトの問合せが戻す行の数をカウントします。
第27章「ビュー・オブジェクトの高度な手法」では、複数ある固有の結果行セットを生成するために単一のビュー・オブジェクトが必要であるという状況が示されています。ただし、多くの場合はビュー・オブジェクトに対して単一の結果行セットのみを1回で操作します。同じ章の後半では、行セットに固有の行セット・イテレータを複数作成する必要があるというシナリオについて説明しています。しかし、多くの場合は単一のイテレータのみが必要です。この決定的な共通のユースケースを単純化するために、図5-17に示すように、ビュー・オブジェクトにデフォルトのRowSetが含まれ、これに順番にデフォルトのRowSetIteratorが含まれます。下の例に示すように、デフォルトのRowSetIteratorにより、デフォルト行セットに自動的に適用されることを認識しながら、コンポーネントViewObject自体で前述のメソッドのすべてを直接コールできるようになります。
|
注意: このガイドで「ビュー・オブジェクトの行を使用して操作中」という表現が出てきた場合、正確にはビュー・オブジェクトの行セットの行を使用して操作するということを意味しています。同様に、「ビュー・オブジェクトの行全体で反復処理する」という表現は、正確にはビュー・オブジェクトのデフォルト行セットのデフォルト行セット・イテレータを使用して、その行をループするという意味です。 |
概念を適切に使用すると、テスト・クライアント・プログラムを作成して概念を実行できます。
getEstimatedRowCount()メソッドは、含まれている行数を判断するために、RowSetで使用されます。
long numReqs = reqs.getEstimatedRowCount();
getEstimatedRowCount()の実装によって最初にSELECT COUNT(*)の問合せが発行され、問合せから戻される行の数を計算します。問合せは、次のような文のビュー・オブジェクトの問合せ全体をラップすることにより、計算式で表されます。
SELECT COUNT(*) FROM ( ... your view object's SQL query here ... )
このアプローチにより、必ずしもすべての行自体を取得しなくても、ビュー・オブジェクトの行数にアクセスできるようになります。これは、大量の行数を戻す問合せを使用して操作したり、問合せの結果を使用して操作する前に問合せから戻される行数を先にテストする場合に重要な最適化です。
推定行数が計算されると、後続のメソッドへのコールではCOUNT(*)問合せを再実行しません。その値は次回にビュー・オブジェクトの問合せが実行されるまでキャッシュされます。これは、データベースから戻された新しい問合せ結果セットが、前回に問合せが実行されたときと比較して増減したか、または異なる行を含んでいる可能性があるためです。現行トランザクションでの保留中の変更、関連する新規行の数の追加、および戻された数から削除された行数の差引を明らかにするため、推定行数は自動的に調整されます。
テスト・クライアント・プログラムを作成するには、Javaクラスの作成ウィザードを使用して、Javaクラスを新規作成します。このクラスは、「新規ギャラリ」の下の「General」カテゴリで使用できます。TestClientなどのクラス名、devguide.examples.clientなどのパッケージ名を入力し、拡張フィールドがjava.lang.Objectとなっていることを確認します。「オプション属性」で、「デフォルトのコンストラクタを生成」の選択を解除し、「mainメソッドの生成」チェック・ボックスを選択します。次に「OK」をクリックして、TestClient.javaファイルを作成します。ファイルはソース・エディタで開き、スケルトン・コードが示されます。
例5-6 TestClient.javaのスケルトン・コード
package devguide.examples.client;
public class TestClient {
public static void main(String[] args) {
}
}
main()メソッドの本体にある空白行にカーソルを置き、bc4jclientコード・テンプレートを使用して必要なコードを数行作成します。この事前定義されたコード・テンプレートを使用するには、bc4jclientという文字を入力してから、[Ctrl]キーを押しながら[Enter]キーを押して次のようにクラスが見えるようにコード・テンプレートを拡張します。
例5-7 TestClient.javaの拡張されたスケルトン・コード
package devguide.examples.client;
import oracle.jbo.client.Configuration;
import oracle.jbo.*;
import oracle.jbo.domain.Number;
import oracle.jbo.domain.*;
public class TestClient {
public static void main(String[] args) {
String amDef = "test.TestModule";
String config = "TestModuleLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef,config);
ViewObject vo = am.findViewObject("TestView");
// Work with your appmodule and view object here
Configuration.releaseRootApplicationModule(am,true);
}
}
amDefおよびconfig変数の値を調整して、アプリケーション・モジュール定義の名前と使用する構成の名前をそれぞれ反映します。例5-7の場合、次のように読み取るために、これらの2行を変更します。
String amDef = "devguide.examples.UserService"; String config = "UserServiceLocal";
最後に、コール内のビュー・オブジェクト・インスタンス名を、作業するfindViewObject()に変更します。「アプリケーション・モジュール」エディタの「データ・モデル」パネルで「データ・モデル」ツリーに表示される名前を正確に指定します。ここで、ビュー・オブジェクト・インスタンスがUserListと名付けられるため、次のように行を変更する必要があります。
ViewObject vo = am.findViewObject("UserList");
この時点で、UserServiceアプリケーション・モジュールに対して作業するスケルトン・テスト・クライアントがあり、このソース・コードは例5-8のようなコードになります。
|
注意: 8.5項「アプリケーション・モジュール・クライアント・インタフェースのプログラム的操作」では、このテスト・クライアント・サンプルコードが拡張され、カスタム・アプリケーション・モジュール・サービス・メソッドのコールも説明されます。 |
例5-8 アプリケーション・モジュール・テスト・クライアント・プログラムの作業中スケルトン・コード
package devguide.examples.client;
import oracle.jbo.client.Configuration;
import oracle.jbo.*;
import oracle.jbo.domain.Number;
import oracle.jbo.domain.*;
public class TestClient {
public static void main(String[] args) {
String amDef = "devguide.examples.UserService";
String config = "UserServiceLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef,config);
ViewObject vo = am.findViewObject("UserList");
// Work with your appmodule and view object here
Configuration.releaseRootApplicationModule(am,true);
}
}
ビュー・オブジェクトの問合せを実行するには、戻される行の数を表示し、データをフェッチしてそれをコンソールに出力するために結果をループし、// Work with your appmodule and view object hereを例5-9のコードに置換します。
例5-9 ビュー・オブジェクトのループおよびコンソールへの結果の出力
System.out.println("Query will return "+
vo.getEstimatedRowCount()+" rows...");
vo.executeQuery();
while (vo.hasNext()) {
Row curUser = vo.next();
System.out.println(vo.getCurrentRowIndex()+". "+
curUser.getAttribute("UserId")+" "+
curUser.getAttribute("Email"));
}
最初の行によってgetEstimatedRowCount()がコールされ、問合せから取得する行の数が示されます。次の行では、executeQuery()メソッドをコールしてビュー・オブジェクトの問合せを実行します。この問合せでは、ビュー・オブジェクトのhasNext()メソッドがfalseを戻すまで反復処理を行うwhile文を使用して、ループできるゼロまたは1行以上の行セットを生成します。ループ内では、コードで現行のRowをcurUserという名前の変数に入れてから、getAttribute()メソッドを2回呼び出します。その現行Rowオブジェクト上でUserIdおよびEmail属性の値を取得して、それらをコンソールに出力します。
ソース・エディタのポップアップ・メニューから「実行」を選択することによってTestClientクラスを実行する際、ログ・ウィンドウにテストの結果が表示されます。例5-10で使用されるgetCurrentRowIndex()は、行セットの行索引がゼロベースの行数であることを示していることに注意してください。
例5-10 テスト・クライアントの実行からのログ出力
Query will return 27 rows... 0. 303 ahunold 1. 315 akhoo : 25. 306 vpatabal 26. 326 wgietz
ConfigurationオブジェクトでのcreateRootApplicationModule()へのコールによって、使用するUserServiceアプリケーション・モジュールのインスタンスが戻されます。デバッグ診断出力で示されるとおり、ADF Business Components実行時クラスによって、必要に応じてXMLコンポーネント定義をロードして、設計時にデータ・モデルで定義されたアプリケーション・モジュールとビュー・オブジェクト・コンポーネントのインスタンスをインスタンス化します。アプリケーション・モジュール上のfindViewObject()メソッドによって、アプリケーション・モジュールのデータ・モデルからビュー・オブジェクト・インスタンスを名前で検索します。例5-9で説明されたループの後、ConfigurationオブジェクトでのreleaseRootApplicationModule()へのコールによって、アプリケーション・モジュールを使用して処理したことが通知され、アプリケーション・モジュールに使用されたデータベース接続のように、フレームワークでリソースをクリーン・アップできるようになります。
createRootApplicationModule()およびreleaseRootApplicationModule()メソッドは、アプリケーション・モジュール・コンポーネントへのコマンドライン・アクセスの場合に非常に便利ですが、通常、ADFベースのWebまたはSwingアプリケーションのコンテキストでは、これら2行のコードを記述する必要はありません。レイヤーをバインドするADF ModelのデータとADF Business Componentsレイヤーを自動的に連携させることによって、これらのシナリオでユーザーのアプリケーション・モジュール・コンポーネントを取得およびリリースします。
実行時にエンド・ユーザーによって提供された検索基準に基づいてビュー・オブジェクトが生成する問合せ結果をフィルタリングする必要がある場合、ビュー・オブジェクトにViewCriteriaを適用できます。ビュー基準は1つ以上のビュー基準行の行セットであり、この属性によってビュー・オブジェクト内の属性がミラーリングされます。問合せ結果のビュー行とビュー基準行の主な相違点は、ビュー基準行の各属性のデータ型がStringであり、たとえば、Query-By-Example演算子を> 304のように入力できるようになるということです。
ビュー基準を使用するには、例5-11のTestClientViewCriteriaクラスに示される手順に従って次のものをコールします。
ビュー・オブジェクト上のcreateViewCriteria(): 空のビュー基準行セットを作成するためにフィルタリングされます。
ビュー基準上のcreateViewCriteriaRow(): 1つ以上の空のビュー基準行を作成します。
ビュー基準行上のsetAttribute()(適宜): フィルタにかける属性値を設定します。
ビュー基準上のadd(): ビュー基準行をビュー基準行セットに追加します。
applyViewCriteria(): ビュー基準をビュー・オブジェクトに適用します。
ビュー基準上のexecuteQuery(): 適用されたフィルタ基準で問合せを実行します。
新しく適用されたビュー基準は次の実行でビュー・オブジェクトのSQL問合せにのみ適用されるため、問合せの実行の最後の手順は重要です。
例5-11 ビュー基準の作成および適用
package devguide.examples.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.ViewCriteria;
import oracle.jbo.ViewCriteriaRow;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
public class TestClientViewCriteria {
public static void main(String[] args) {
String amDef = "devguide.examples.UserService";
String config = "UserServiceLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef, config);
ViewObject vo = am.findViewObject("UserList");
// 1. Create a view criteria rowset for this view object
ViewCriteria vc = vo.createViewCriteria();
// 2. Use the view criteria to create one or more view criteria rows
ViewCriteriaRow vcr1 = vc.createViewCriteriaRow();
ViewCriteriaRow vcr2 = vc.createViewCriteriaRow();
// 3. Set attribute values to filter on in appropriate view criteria rows
vcr1.setAttribute("UserId","> 304");
vcr1.setAttribute("Email","d%");
vcr1.setAttribute("UserRole","technician");
vcr2.setAttribute("UserId","IN (324,326)");
vcr2.setAttribute("LastName","Baer");
// 4. Add the view criteria rows to the view critera rowset
vc.add(vcr1);
vc.add(vcr2);
// 5. Apply the view criteria to the view object
vo.applyViewCriteria(vc);
// 6. Execute the query
vo.executeQuery();
while (vo.hasNext()) {
Row curUser = vo.next();
System.out.println(curUser.getAttribute("UserId") + " " +
curUser.getAttribute("Email"));
}
Configuration.releaseRootApplicationModule(am, true);
}
}
例5-11のTestClientViewCriteriaの例を実行すると、次の出力が生成されます。
305 daustin 307 dlorentz 324 hbaer
1つ以上のビュー基準行を含むビュー基準をビュー・オブジェクトに適用する場合は、次に実行されるときに、ビュー基準行に移入したQuery-By-Example基準に対応する追加のWHERE句条件を使用してビュー・オブジェクトのSQL問合せを拡張します。図5-18のように、複数のビュー基準行を含むビュー基準を適用する場合、ビュー・オブジェクトでは、各ビュー基準行の非nullの例の基準属性に基づいて付加的な実行時WHERE句を追加することによって、その設計時WHERE句を拡張します。
Query-By-Example基準についていくつか知っておく必要があります。これには、Business Component Browserでのビュー基準のテスト方法、複数のビュー基準行を使用する複合検索条件の変更、属性値がNULLである行の検索、大/小文字を区別しない検索、有効なビュー基準のクリア、および適用するビュー基準が問合せの再解析の原因になる理由などが含まれます。
5.6.1項「ビュー・オブジェクトのデフォルト行セットを使用した作業の共通メソッド」で、setWhereClause()メソッドによって動的WHERE句をビュー・オブジェクトに追加できることがわかりました。この章の後の例で説明するように、setWhereClause()を使用する場合、次のようなリテラル・データベース列名を含む文字列を渡します。
vo.setWhereClause("LAST_NAME LIKE UPPER(:NameToFind)");
これに対して、ビュー基準メカニズムを使用する場合は、前述の例5-11で説明されているように、次のような句のかわりにビュー・オブジェクト属性名を参照します。
criteriaRow.setAttribute("LastName","B%");
前述の例のように、ビュー・オブジェクトによって、ビュー基準行が当該の列名を参照する当該のWHERE句条件に変換されます。
図5-19のように、参照する任意のビュー・オブジェクト・インスタンスについては、「ビュー基準の指定」ツールバー・アイコンをクリックすると、「ビジネス・コンポーネント・ビュー基準」ダイアログが表示されます。ダイアログで、1つ以上のビュー基準行から構成されるビュー基準を作成できます。単一のビュー基準行から基準属性を適用するには、該当するフィールドにQuery-By-Example基準を入力し、「検索」をクリックします。付加的なビュー基準行を追加するには、「OR」をクリックして、それぞれが固有のビュー基準行を表すページを切り替えるために表示される追加タブを使用します。 「検索」をクリックすると、Business Component Browserでは前述されたものと同じAPIを使用して、結果をフィルタリングするためのビュー基準を作成および適用します。
複数ビュー基準行を追加する場合、ビュー基準行のsetConjunction()メソッドをコールして、その行に対応する条件とその前のビュー基準行に対する条件の間で使用される結合を変更できます。引数として渡す有効な定数は、次のとおりです。
ViewCriteriaRow.VCROW_CONJ_AND
ViewCriteriaRow.VCROW_CONJ_NOT
ViewCriteriaRow.VCROW_CONJ_OR(デフォルト)
次のようなフィルタ基準を作成するために、NOT値をANDまたはORと組み合せることができます。
( PredicateForViewCriteriaRow1) AND ( NOT( PredicateForViewCriteriaRow2 ) )
または
( PredicateForViewCriteriaRow1) OR (NOT( PredicateForViewCriteriaRow2 ) )
これらを実現する構文は、次のようにJavaのビット単位OR演算子を使用する必要があります。
vcr2.setConjunction(ViewCriteriaRow.VCROW_CONJ_AND | ViewCriteriaRow.VCROW_CONJ_NOT);
大/小文字を区別しないで検索するには、区別しない検索を適用するビュー基準行のsetUpperColumns(true)をコールします。これは、ビュー・オブジェクトのString値の属性に対して生成されたWHERE句条件に影響を与え、COLUMN_NAMEではなくUPPER(COLUMN_NAME)が条件に使用されます。これらのString値の属性に対して提供されるビュー基準行属性の値は、大文字であるか、または条件が一致しないものである必要があります。
有効な任意のビュー基準をクリアするには、ビュー・オブジェクトでgetViewCriteria()をコールし、それからremove()メソッドを使用してビュー基準行をすべて削除し、削除する基準のゼロベースの索引を渡します。他のビュー基準行に戻って追加しない場合、ビュー・オブジェクトでapplyViewCriteria(null)をコールするのみで、すべての有効なビュー基準をクリアすることもできます。
前述のビュー基準機能の当然の結果として、新規ビュー基準を適用(または既存のビュー基準を削除)するたびに、ビュー・オブジェクトのSQL問合せのテキストが効率的に変更されます。SQL問合せの変更は、データベースが次に実行されるときに文を再解析する原因になります。ビュー基準フィルタリング機能を使用して、毎回基本的に同じ基準属性に対して異なる基準値を適用する場合、5.9項「名前付きバインド変数の使用」で説明されるように、WHERE句に名前付きバインド変数を含むビュー・オブジェクトを使用することにより、パフォーマンスが向上します。ビュー基準フィルタリング機能とは対照的に、名前付きバインド変数を使用して、これらの値が変わるたびにビュー・オブジェクトのSQL文のテキストを変更せずに、検索基準の値を変更できます。
問合せのWHERE句が実行のたびに変化することのある値を含む場合は、常に名前付きバインド変数を使用できます。これらは、SQL文字列内のプレースホルダであり、その値はSQL文字列自体のテキストを変更することなく、実行時に簡単に変更できます。問合せは変更しないため、データベースでは、同一の解析済問合せ表現を複数の実行で効率的に再使用できます。これにより、アプリケーションの実行時パフォーマンスが向上します。
名前付きバインド変数をビュー・オブジェクトに追加するには、ビュー・オブジェクトの作成ウィザードまたはビュー・オブジェクト・エディタの「バインド変数」タブを使用します。必要な数の名前付きバインド変数を追加できます。図5-20に示すように、各バインド変数に対して、その変数の名前、データ型、およびデフォルト値を指定します。変数には任意の名前を付けることができますが、これらの変数はビュー・オブジェクト属性と同じネームスペースを共有するため、既存のビュー・オブジェクト属性名と競合しない名前を選択する必要があります。ビュー・オブジェクト属性と同様に、バインド変数名は最初が大文字という規則で作成されます。
「コントロール・ヒント」タブ上で、「ラベル・テキスト」、「フォーマットの種類」、「フォーマット」マスクなどのようなUIヒントを指定することもできます。これらのバインド変数のコントロール・ヒントは、名前付きバインド変数の値を入力できる検索ページのようなユーザー・インタフェースを構築する場合、ビュー・レイヤーによって自動的に使用されます。「更新可能」チェック・ボックスでは、エンド・ユーザーがユーザー・インタフェースを使用してバインド変数の値を変更できるようになるかどうかを制御します。バインド変数が更新可能でない場合、その値は開発者のみがプログラムで変更できます。
バインド変数を定義した後、次のステップではそれらの変数をSQL文で参照します。SQL構文によりバインド変数がSELECTリストとWHERE句の両方に表示されるようになりますが、通常はWHERE句の一部としてWHERE句でバインド変数を使用します。前述で作成されたUserListビュー・オブジェクトを編集し、「SQL文」ページを開いて次のように名前付きバインド変数を導入できます。
select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL
:TheNameや:LowUserIdなどのようにコロンを名前の前に付けることによって、SQL文でバインド変数を参照していることに注意してください。任意の順序でバインド変数を参照し、SQL文の中で必要な回数のみ繰り返すことができます。
ビュー・オブジェクトに1つ以上の名前付きバインド変数を追加すると、これらの変数の値を実行時に簡単に参照および設定する機能を使用できます。各バインド変数の名前、型およびデフォルト値の情報は、ビュー・オブジェクトのXMLコンポーネント定義ファイルに保存されます。バインド変数に対してUIコントロール・ヒントを定義した場合、この情報は他のビュー・オブジェクトのコントロール・ヒントとともにビュー・オブジェクトのコンポーネント・メッセージ・バンドル・ファイルに保存されます。
Business Component Browserにより、ビュー・オブジェクトに対する名前付きバインド変数の値を対話型で検査および変更できるようになります。また、これを使用して、名前付きバインド・パラメータが関連する場合に、アプリケーション・モジュールのデータ・モデルを非常に簡単に試すことができます。テスターでビュー・オブジェクトを初めて実行する場合、図5-21に示すように「バインド変数」ダイアログが表示されます。リスト内の特定のバインド変数を選択することにより、その変数の名前をデフォルト値および現在の値とともに参照できます。バインド変数の値を変更するには、その変数に対応する「値」フィールドを更新してから「OK」をクリックし、バインド変数値を設定して問合せを実行するのみです。ツールバーの「バインド・パラメータの編集」ボタン(アイコンが:idのように表示されます)を使用して、現在のパネルのビュー・オブジェクトに対するバインド変数を検査および設定できます。
「ラベル・テキスト」、「フォーマットの種類」または「フォーマット」コントロール・ヒントを定義した場合、「バインド変数」ダイアログで「バインド変数」リストのラベル・テキスト・ヒントを表示し、それぞれのフォーマット・マスクを使用して「値」属性をフォーマットすることにより、これらが正しく設定されていることを確認できます。図5-21で、ラベル・テキスト・ヒントがリスト内の3つのバインド変数に対して表示されていることがわかります。
名前付きバインド変数について知っておく必要のあることがいくつかあります。これには、バインド変数の名前が不一致である場合に表示される実行時エラー、バインド変数のデフォルト値、実行時の既存のバインド変数値の設定方法、および実行時の名前付きバインド変数の新規追加方法が含まれます。
SQL文で参照する名前付きバインド変数のリストが、ビュー・オブジェクト・エディタの「バインド変数」ページで定義した名前付きバインド変数のリストと必ず一致する必要があります。これらの2つの変数を正しく一致させることができない場合、実行時に次の2つのエラーのどちらかが発生します。
SQL文で名前付きバインド変数を使用しても、それを定義していない場合は、次のようなエラー・メッセージを受け取ります。
(oracle.jbo.SQLStmtException) JBO-27122: SQL error during statement preparation. ## Detail 0 ## (java.sql.SQLException) Missing IN or OUT parameter at index:: 1
一方、名前付きバインド変数を定義しても、その変数をSQLで参照するのを忘れたか、名前の入力ミスをした場合、次のようなエラーが表示されます。
oracle.jbo.SQLStmtException: JBO-27122: SQL error during statement preparation. ## Detail 0 ## java.sql.SQLException: Attempt to set a parameter name that does not occur in the SQL: LowUserId
いずれの場合も、SQLの名前付きバインド変数のリストが「バインド変数」ページの名前付きバインド変数のリストと一致しているかダブルチェックすることが解決方法です。
名前付きバインド変数にデフォルト値を適用しない場合、変数は実行時にNULL値にデフォルト設定されます。つまり、次のようなWHERE句を持つ場合です。
USER_ID = :TheUserId
このとき、バインド変数TheUserIdにデフォルト値を指定しない場合は、デフォルトでNULL値になるように設定されます。これが問合せから行が戻されない原因になります。アプリケーションに対して意味を持つ場合は、必要とする状況を処理するために、NVL()、CASE、DECODE()などのSQL関数またはその他の関数を活用できます。実際に、ビュー・オブジェクトUserListでは次のようなWHERE句のフラグメントを使用します。
upper(FIRST_NAME) like upper(:TheName)||'%'
これにより、:TheNameがnullの場合、問合せは任意の名前に一致します。
実行時に名前付きバインド変数を設定するには、ViewObjectインタフェース上でsetNamedWhereClauseParam()メソッドを使用します。JDeveloperの「リファクタ」→「複製」機能を使用して、5.7項「コマンドラインJavaテスト・クライアントの作成方法」からの既存TestClient.javaクラスに基づいてTestClientBindVarsクラスを新規作成できます。この新規テスト・クライアント・クラスでは、例5-12に示すコードの追加行を数行使用してHighUserIdおよびTheNameバインド変数の値を設定できます。
// changed lines in TestClient class
ViewObject vo = am.findViewObject("UserList");
vo.setNamedWhereClauseParam("TheName","alex%");
vo.setNamedWhereClauseParam("HighUserId", new Number(315));
vo.executeQuery();
// etc.
TestClientBindVarsというクラスの実行では、バインド変数がデータをフィルタリングしていることと、結果行がAlexander HunoldおよびAlexander Khooの2つの一致した行のみであることが示されます。
303 ahunold 315 akhoo
ビュー・オブジェクトの問合せが実行されるときは必ず、実行時デバッグ診断で、使用される実際のバインド変数値が次のように表示されます。
[256] Bind params for ViewObject: UserList [257] Binding param "LowUserId": 0 [258] Binding param "HighUserId": 315 [259] Binding param "TheName": alex%
この情報は、アプリケーションの問題を切り分ける上でとても重要です。コードでLowUserIdバインド変数の値が設定されなかったために、設計時にデフォルト値として0(ゼロ)が指定されたことに注意してください。さらに、WHERE句およびバインド変数の周囲でUPPER()関数を使用することにより、TheNameのバインド変数値を使用した照合が大/小文字の区別なしで確実に実行されたことに注意してください。例のコードは、バインド変数値を小文字のaを使用するalex%に設定し、その結果がAlexanderと一致したことを示します。
ビュー・オブジェクトのsetWhereClause()メソッドを使用して、実行時にフィルタリングの句をさらに追加できます。この実行時に追加されたWHERE句条件では、設計時に追加されたWHERE句が置換されませんが、既存の設計時WHERE句に追加して適用されることにより、問合せ結果がさらに絞り込まれます。動的に追加された句が、アプリケーションの有効期間中に変化することのある値を参照するときは、WHERE句条件にリテラル値を連結するのではなく、常に名前付きバインド変数を使用してください。
たとえば、表のUSER_ROLE列の値に基づいて実行時にビュー・オブジェクトUserListをさらにフィルタリングすると仮定します。さらに、USER_ROLE = 'technician'である場合、またはUSER_ROLE = 'User'である場合の行を検索すると仮定します。コード行は少なくなりますが、同じUSER_ROLE列の異なる2つの値を問い合せるためだけにWHERE句を2回変更することから、次のような実行例は好ましくありません。
// Don't use literal strings if you plan to change the value!
vo.setWhereClause("user_role = 'technician'");
// execute the query and process the results, and then later...
vo.setWhereClause("user_role = 'user'");
かわりに、次のような、実行時に定義する名前付きバインド変数を参照するWHERE句条件を追加します。
vo.setWhereClause("user_role = :TheUserRole");
vo.defineNamedWhereClauseParam("TheUserRole", null, null);
vo.setNamedWhereClauseParam("TheUserRole","technician");
// execute the query and process the results, and then later...
vo.setNamedWhereClauseParam("TheUserRole","user");
これにより、問い合せる必要があるUSER_ROLEの値にかかわらず、SQL文のテキストが同じままになります。問合せテキストが複数の実行全体で同じ状態の場合は、問合せの再解析を行わない結果がデータベースによって与えられます。
動的に追加されたWHERE句とバインド変数を後から削除する必要がある場合、次のようなコードを使用できます。
vo.setWhereClause(null);
vo.removeNamedWhereClauseParam("TheUserRole");
これらのテクニックを示している更新済TestClientBindVarsクラスは、例5-13のようになります。この場合、結果を数回ループする機能は、別個のexecuteAndShowResults()メソッドにリファクタされています。プログラムでは、最初に付加的なWHERE句のuser_id = :TheUserIdが追加されてから、後で第2句のuser_role = :TheUserRoleに置換されます。
例5-13 名前付きバインド変数テクニックを実行するTestClientプログラム
package devguide.examples.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.Number;
public class TestClient {
public static void main(String[] args) {
String amDef = "devguide.examples.UserService";
String config = "UserServiceLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef,config);
ViewObject vo = am.findViewObject("UserList");
// Set the two design time named bind variables
vo.setNamedWhereClauseParam("TheName","alex%");
vo.setNamedWhereClauseParam("HighUserId", new Number(315));
executeAndShowResults(vo);
// Add an extra where clause with a new named bind variable
vo.setWhereClause("user_id = :TheUserId");
vo.defineNamedWhereClauseParam("TheUserId", null, null);
vo.setNamedWhereClauseParam("TheUserId",new Number(303));
executeAndShowResults(vo);
vo.removeNamedWhereClauseParam("TheUserId");
// Add an extra where clause with a new named bind variable
vo.setWhereClause("user_role = :TheUserRole");
vo.defineNamedWhereClauseParam("TheUserRole", null, null);
vo.setNamedWhereClauseParam("TheUserRole","user");
// Show results when :TheUserRole = 'user'
executeAndShowResults(vo);
vo.setNamedWhereClauseParam("TheUserRole","technician");
// Show results when :TheUserRole = 'technician'
executeAndShowResults(vo);
Configuration.releaseRootApplicationModule(am,true);
}
private static void executeAndShowResults(ViewObject vo) {
System.out.println("---");
vo.executeQuery();
while (vo.hasNext()) {
Row curUser = vo.next();
System.out.println(curUser.getAttribute("UserId")+" "+
curUser.getAttribute("Email"));
}
}
}
ただし、このテスト・プログラムを実行すると、実際には次のような実行時エラーになります。
oracle.jbo.SQLStmtException: JBO-27122: SQL error during statement preparation. Statement: SELECT * FROM (select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL) QRSLT WHERE (user_role = :TheUserRole) ## Detail 0 ## java.sql.SQLException: ORA-00904: "USER_ROLE": invalid identifier
根本原因はスタック・トレースの## Detail 0 ##の後に表示されますが、これはUSER_ROLE列が存在しないというデータベースのレポートによるSQL解析エラーです。USERS表にはUSER_ROLE列が確かに存在するため、このエラーは正しくありません。この問題は、ADFビュー・オブジェクトが読取り専用問合せの最初の部分で追加実行時WHERE句を適用するためにデフォルトで使用するメカニズムによって発生します。5.9.3.5項「読取り専用問合せのインライン・ビューのデフォルト使用の概要」で、この問題の解決策を説明します。
実行時に付加的なWHERE句を読取り専用ビュー・オブジェクトに動的に追加する場合、その問合せは付加的なWHERE句を適用する前にインライン・ビューにネストされた状態になります。たとえば、問合せが次のように定義されたと仮定します。
select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL
実行時に、例5-13のテスト・プログラムで実行されたように、user_role = :TheUserRoleのような付加的なWHERE句を設定する場合、フレームワークで次のようにインライン・ビューに元の問合せをネストします。
SELECT * FROM( select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL) QRSLT
次に、データベースが確認する最終問合せが次のようになるように、動的WHERE句条件を最後に追加します。
SELECT * FROM( select USER_ID, EMAIL, FIRST_NAME, LAST_NAME from USERS where (upper(FIRST_NAME) like upper(:TheName)||'%' or upper(LAST_NAME) like upper(:TheName)||'%') and USER_ID between :LowUserId and :HighUserId order by EMAIL) QRSLT WHERE user_role = :TheUserRole
元の問合せの複雑さは任意に設定できるため、通常の場合、この問合せのラップが必要です。これには、複数の問合せを単一の結果に結合するSQL UNION、INTERSECT、MINUSまたはその他の演算子が含まれます。このような場合に、追加ランタイムを問合せテキストの末尾に単純に貼り付けると、UNIONが実行されたいくつかの文の最後にのみこれが適用されたりするため、予期せぬ結果が生じることがあります。元の問合せをそのままインライン・ビューにネストすることにより、ビュー・オブジェクトでは、どんなに複雑であっても、元の問合せの結果をフィルタリングするために付加的なWHERE句が確実に正しく使用されます。ORA-904エラーについてここで示されているマイナス面は、動的に追加されたWHERE句が元の問合せで選択されている列しか参照できないということです。
27.3.3.7項「実行時のインライン・ビュー・ラッピングの使用の無効化」では、この問合せのネストを必要としない場合の無効化の方法について説明していますが、当面、ビュー・オブジェクトUserListを編集して、「SQL文」ページの問合せのSELECTリストの最後にUSER_ROLE列を追加することが、最も単純な解決策です。ジョブの実行には、既存のSELECTリストの最後に新規の列名を追加する(カンマを前に付ける)のみで十分です。ビュー・オブジェクト・エディタでは、問合せ文と同期してビュー・オブジェクトの属性リストを自動的に保持します。
例5-13で変更されたテスト・プログラムによって、予想した結果が生成されます。
--- 303 ahunold 315 akhoo --- 303 ahunold --- 315 akhoo --- 303 ahunold
ここまでは、単一のUSERS表を問い合せる単一のビュー・オブジェクトを使用してきました。実際には、作業する必要のある問合せの多くには、外部キーによって関連付けられる複数の表が含まれています。次の2つの方法のいずれかを行って、ビュー・オブジェクトを使用してこの状況を処理できます。
メインの問合せで複数の表を結合し、追加の説明情報をメイン問合せ結果の各行に示します。
関連情報を問い合せる別個のビュー・オブジェクトを作成してから、マスター/ディテール階層を形成するために、ソース・ビュー・オブジェクトを1つ以上のターゲット・ビュー・オブジェクトにリンクします。
図5-22は、これら2つのオプションが生成する異なった形を示しています。結合は単一の平坦化された結果です。リンクされたマスター/ディテール問合せによって、マルチレベルの結果が生成されます。
2つの表を結合する読取り専用ビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。例として、SERVICE_REQUEST表とUSER表を結合するOpenOrPendingServiceRequestsという名前のビュー・オブジェクトを作成します。サービス・リクエストごとに、リクエストを作成したユーザーの電子メールを表示します。
ステップ1では「読取り専用アクセス」が選択されていることを確認し、ステップ2では「SQL文」ページで、該当する表を結合するSQL問合せ文を入力します。正しいSQL文を構築するために対話型支援が必要な場合は、「クエリー・ビルダー」ボタンをクリックします。
図5-23に示すように、クエリー・ビルダー・ダイアログの「クイックピック・オブジェクト」ページで、他の表に関連する外部キーも含め、自分のスキーマで表を参照できます。問合せの選択リストにある列を含めるには、「選択可能」リストでそれらの列をクリックし、「選択済」リストにそれらを移動します(逆方向の移動も可能)。図5-23は、SVR_ID、PROBLEM_DESCRIPTIONおよびASSIGNED_TOの各列をSERVICE_REQUESTS表から選択した結果を、USERS表からのEMAIL列とともに示しています。SERVICE_REQUESTS表では、SVR_CREATED_BY_USR_FK外部キーの下でUSERS表からEMAIL列を選択し、クエリー・ビルダーによって自動的に必要な結合句が決定されます。
クエリー・ビルダーの「WHERE句」ページで、STATUS列を「WHERE句」句ボックスに移動し(逆方向の移動も可能)、IN ('Open','Pending')を自分で追加して作業を完了します。クエリー・ビルダーで「OK」をクリックして、次の問合せを作成します。
例5-14 SQL Builderを使用した問合せの作成
SELECT
SERVICE_REQUESTS.SVR_ID SVR_ID,
SERVICE_REQUESTS.PROBLEM_DESCRIPTION PROBLEM_DESCRIPTION,
SERVICE_REQUESTS.ASSIGNED_TO ASSIGNED_TO,
USERS.EMAIL EMAIL
FROM
SERVICE_REQUESTS INNER JOIN USERS
ON SERVICE_REQUESTS.CREATED_BY = USERS.USER_ID
WHERE
SERVICE_REQUESTS.STATUS IN ('Open', 'Pending')
問合せ内のEMAIL列に注目してください。これは、サービス・リクエストを作成した人物の電子メールを表しますが、その列名は説明が十分ではありません。5.2.3.2項「SQL式を含む問合せの使用」で、列に別名を割り当てることによってビュー・オブジェクト属性のJavaで簡単に使用できるデフォルト名に影響を与える1つの方法を学習しました。ここでは、別のテクニックを採用できます。ビュー・オブジェクトの作成ウィザードの後のパネルの1つを使用して、作成プロセスの一部としてビュー・オブジェクト属性の名前を直接変更します。ここでビュー・オブジェクトの名前を変更すると、使用する予定の様々な属性名がわかっている場合にビュー・オブジェクトを再度編集する必要がなくなります。
「次へ」を4回クリックして、「属性の設定」ページを表示します。ページ上部の「属性の選択」ドロップダウン・リストからEmail属性を選択して、「名前」フィールドの値をCreatedByEmailに変更します。次に「終了」をクリックして、OpenOrPendingServiceRequestsビュー・オブジェクトを作成します。OpenOrPendingServiceRequests.xmlコンポーネント定義ファイルが作成され、ビュー・オブジェクトの宣言設定が保存されます。
新規ビュー・オブジェクトをテストするには、UserServiceアプリケーション・モジュールを編集し、「データ・モデル」ページでOpenOrPendingServiceRequestsのインスタンスをデータ・モデルに追加します。デフォルトのOpenOrPendingServiceRequests1インスタンス名を受け入れるのではなく、インスタンス名をAllOpenOrPendingServiceRequestsに変更します。これを行った後、Business Component Browserを起動し、結合問合せが予期したとおりに動作しているか検証できます。
ユーザーにマスター行のセットを表示し、調整されたディテール行のセットをマスター行ごとに表示するコールが必要な場合、ビュー・リンクを作成して、マスター・ビュー・オブジェクトとディテール・ビュー・オブジェクトをどのように関連付けるかを定義できます。UserListビュー・オブジェクトをOpenOrPendingServiceRequestsビュー・オブジェクトにリンクして、ユーザーのマスター/ディテール階層と割り当てられている未処理または保留中のサービス・リクエストの関連したセットを作成すると仮定します。
ビュー・リンクを作成するには、ビュー・リンクの作成ウィザードを使用します。このウィザードは、「新規ギャラリ」の「Business Tier」→「ADF Business Components」カテゴリから起動できます。ステップ1では、「名前」ページでビュー・リンクの名前とビュー・リンクの定義が格納されるパッケージを提供します。その目的を考えた場合、RequestsAssignedToのような名前は問題ありませんが、簡単にするために、その名前をビュー・オブジェクトと同じdevguide.examplesパッケージに保持します。
ステップ2では、「ビュー・オブジェクト」ページで、マスターとして動作するビュー・オブジェクトから使用するためにソース属性を選択します。図5-24は、このロールでUsersビュー・オブジェクトからUserId属性を選択することを示しています。次に、ディテールとして動作するビュー・オブジェクトから対応する関連先の属性を選択します。現在選択されているユーザーに割り当てられたサービス・リクエストを示すためにディテール問合せが必要であるため、OpenOrPendingServiceRequestsでAssignedTo属性を選択し、この役割を果します。最後に、「追加」をクリックして、一致する属性ペアを下のソース属性と関連先の属性のペアの表に追加します。マスターとディテールの間のリンクを定義するために必要な属性ペアが複数あった場合、これらのステップを繰り返して付加的なソース/ターゲット属性ペアを追加できます。この例の場合、必要なのは1組(UserId、AssignedTo)のみです。
ステップ3では、「SQL設定」ページで、実行時に使用されるビュー・リンクのSQL述語をプレビューし、ソース・ビュー・オブジェクトの現在行のリンク先ビュー・オブジェクトから相関ディテール行にアクセスできます。
ステップ4では、「ビュー・リンク・プロパティ」ページで、ビュー・リンクが一方向と双方向のどちらを表すのかを制御できます。図5-25で、OpenOrPendingServiceRequestsビュー・オブジェクトの「関連先」グループ・ボックスで、「アクセッサの生成」の「ビュー・オブジェクト: Users」ボックスが選択されることに注意してください。これとは対照的に、ビュー・オブジェクトUsersの「関連元」グループ・ボックスでは、「アクセッサの生成」の「ビュー・オブジェクト: OpenOrPendingServiceRequests」ボックスは選択されていません。デフォルトでは、ビュー・リンクは、ソース(マスター)の現在行をリンク先(ディテール)ビュー・オブジェクトの関連する行のセットにアクセスできるようにする一方向関係です。これらのチェック・ボックス設定は、ビュー・オブジェクトUsersの現在行のOpenOrPendingServiceRequestsから行のディテール・コレクションにアクセスできることを示しますが、逆方向のアクセスはできません。この例の場合、デフォルトの一方向ビュー・リンクは問題ないため、他のチェック・ボックスは選択されないままになります。
「関連先」グループ・ボックスの「アクセッサ名」フィールドは、Usersの現在行に対するOpenOrPendingServiceRequests行の関連するコレクションにプログラムでアクセスする際に使用できる名前を示します。デフォルトでは、アクセッサ名は、リンク先ビュー・オブジェクトの名前と一致する、OpenOrPendingServiceRequestsになります。サービス・リクエストの関連コレクションが現行ユーザーに割り当てられるリクエストのコレクションであることをより明確にするには、図5-25に示すように、アクセッサの名前をAssignedRequestsに変更できます。
ビュー・リンクを作成するには、「終了」をクリックします。
ビュー・リンクを作成する場合、JDeveloperでその宣言的設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリ内に保存されます。5.10.2項で、ビュー・リンクにdevguide.examplesパッケージでRequestsAssignedToという名前が付けられたため、作成されたXMLファイルは、プロジェクトのソース・パスの下の./devguide/examples/RequestsAssignedTo.xmlにあります。このXMLファイルには、指定したソース属性とターゲット属性のペアに関する宣言情報が含まれています。
ビュー・リンク・コンポーネント定義自体を保存するのみでなく、JDeveloperはビュー・リンク関係でのビュー・オブジェクトsourceのXML定義を更新して、定義したビュー・リンク・アクセッサに関する情報を追加します。この確認として、Application Navigatorでビュー・オブジェクトUsersを選択し、構造ウィンドウでその詳細を検査できます。図5-26に示すように、「ViewLink Accessor」カテゴリにAssignedRequestsアクセッサが新たに表示されます。
ビュー・リンクを効果的に使用するために、知っておく必要があることがまだいくつかあります。これには、ビュー・リンク・アクセッサによってRowSetが戻されること、ビュー・リンク・アクセッサを使用してディテール・コレクションにアクセスする方法、およびデータ・モデルでアクティブなマスター/ディテール調整を有効化する方法が含まれます。
実行時に、RowにあるgetAttribute()メソッドにより、ビュー・オブジェクトの名前別の結果セットで行の属性にアクセスできます。ビュー・リンク・アクセッサは、ソース・ビュー・オブジェクトの現在行の追加属性のような動作をするため、同じgetAttribute()メソッドを使用してその値を取得できます。通常のビュー属性とビュー・リンク・アクセッサ属性との実際の相違点は、そのデータ型のみです。通常のビュー属性は、一般に303またはahunoldのような値を持つスカラー・データ型であるのに対し、ビュー・リンク・アクセッサ属性の値はゼロまたは1つ以上の相関ディテール行の行セットです。curUserがUsersビュー・オブジェクトのいくつかのインスタンスからのRowであると仮定して、未処理または保留中の割り当てられたリクエストのディテール行セットを取得するための、次のコードを1行作成することができます。
RowSet reqs = (RowSet)curUser.getAttribute("AssignedRequests");
|
注意: ビュー行のカスタムJavaクラスを生成する場合、ビュー・リンク・アクセッサのタイプはRowIteratorになります。実行時に、戻り値が必ずRowSetになるため、ビュー・リンク属性値をRowSetにキャストするのが安全です。 |
ビュー・リンク・アクセッサを使用してディテール行のRowSetを取得すると、ビュー・オブジェクトの結果と手順の組合せの行セットを使用したパターンと同じものを使用して、それが含まれる行全体をループできます。
while (reqs.hasNext()) {
Row curReq = reqs.next();
System.out.println("--> (" + curReq.getAttribute("SvrId") + ") " +
curReq.getAttribute("ProblemDescription"));
}
既存のTestClient.javaクラスでJDeveloperの「リファクタ」→「複製」機能を使用する場合、それを簡単にクローニングし、例5-15に示すように変更して、これらの新規テクニックを活用するTestClient2.javaクラスを作成できます。main()メソッドに残される行では、UserListビュー・オブジェクト・インスタンスを制限してUSER_ROLEの値がtechnicianであるユーザーのみ表示する、動的なWHERE句を設定しています。2番目の変更では、ビュー・リンク・アクセッサ属性にアクセスするexecuteAndShowResults()メソッドを拡張し、それぞれに対してリクエスト番号(SvrId)およびProblemDescription属性を出力しました。
例5-15 ビュー・リンク・アクセッサを使用したディテール行へのプログラムによるアクセス
package devguide.examples.client;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
public class TestClient2 {
public static void main(String[] args) {
String amDef = "devguide.examples.UserService";
String config = "UserServiceLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef, config);
ViewObject vo = am.findViewObject("UserList");
// Add an extra where clause with a new named bind variable
vo.setWhereClause("user_role = :TheUserRole");
vo.defineNamedWhereClauseParam("TheUserRole", null, null);
vo.setNamedWhereClauseParam("TheUserRole", "technician");
// Show results when :TheUserRole = 'technician'
executeAndShowResults(vo);
Configuration.releaseRootApplicationModule(am, true);
}
private static void executeAndShowResults(ViewObject vo) {
System.out.println("---");
vo.executeQuery();
while (vo.hasNext()) {
Row curUser = vo.next();
// Access the row set of details using the view link accessor attribute
RowSet reqs = (RowSet)curUser.getAttribute("AssignedRequests");
long numReqs = reqs.getEstimatedRowCount();
System.out.println(curUser.getAttribute("UserId") + " " +
curUser.getAttribute("Email")+" ["+
numReqs+" requests]");
while (reqs.hasNext()) {
Row curReq = reqs.next();
System.out.println("--> (" + curReq.getAttribute("SvrId") + ") " +
curReq.getAttribute("ProblemDescription"));
}
}
}
}
TestClient2を実行すると、ログ・ウィンドウに次の結果が表示されます。各技術者がリストされ、未処理または保留中のサービス・リクエストを持つ技術者ごとに、これらのリクエストに関する情報が技術者の名前の下に表示されます。
--- 303 ahunold [0 requests] 304 bernst [2 requests] --> (102) Washing Machine does not turn on --> (108) Freezer full of frost 305 daustin [1 requests] --> (104) Spin cycle not draining 307 dlorentz [0 requests] 306 vpatabal [2 requests] --> (107) Fridge is leaking --> (112) My Dryer does not seem to be getting hot
デバッグ診断を有効にしてTestClient2を実行すると、ビュー・オブジェクトによって実行されたSQL問合せが表示されます。ビュー・リンクのWHERE句条件は、UserListビュー・オブジェクトで現在行のディテール・サービス・リクエストのフィルタリングを自動実行するために使用されます。
ビュー・リンクを定義するプロセスによってソース・ビュー・オブジェクトのビュー・リンク属性が導入され、それにより、相関ディテールの行セットにプログラムで移動できるようになることがわかりました。このシナリオでは、ビュー・リンクは調整されたディテール行セットをコードがリクエストする場合にそれを取得するために必要な情報を定義するのみ、という受動的な役割を担います。ビュー・リンク・アクセッサ属性が存在し、ビュー・リンクのソース・ビュー・オブジェクトの任意のインスタンスから任意の結果行でプログラムによるアクセスが可能です。つまり、プログラムでのアクセスはアプリケーション・モジュールUserServiceのデータ・モデルの変更を必要としません。
ただし、マスター/ディテール・ユーザー・インタフェースは、エンタープライズ・アプリケーション内で頻繁に発生するため、マスター/ディテール画面をプログラムで調整する必要を回避するために、ビュー・リンクは、よりアクティブな形でも使用されます。ビュー・リンクされたビュー・オブジェクトのインスタンスをアプリケーション・モジュールのデータ・モデルに明示的に追加することにより、実行されたこのアクティブなマスター/ディテール調整を行うことを選択します。
これを遂行するには、UserServiceアプリケーション・モジュールを編集し、「データ・モデル」ページを開きます。図5-27に示すように、「選択可能なビュー・オブジェクト」リストにOpenOrPendingServiceRequestsビュー・オブジェクトが2回表示されます。1回はそのオブジェクト自体に、もう1回はRequestsAssignedToビュー・リンクを介してディテール・ビュー・オブジェクトとして表示されます。
ビュー・オブジェクトのディテール・インスタンスを追加する方法:
右側の「データ・モデル」リストで、アクティブに調整するマスターにする必要がある「データ・モデル」リストのUsersビュー・オブジェクトのインスタンスを選択します。
データ・モデルにはUserListという名前のUsersビュー・オブジェクトのインスタンスしかないため、UserListインスタンスを選択します。
「選択可能なビュー・オブジェクト」リストで、Usersビュー・オブジェクトの下でインデントされたOpenOrPendingServiceRequestsノードを選択します。
「選択可能なビュー・オブジェクト」リストの下の「名前」フィールドに、作成しようとしているディテール・インスタンスの名前を入力します。 図5-27に示すように、RequestsAssignedインスタンスをコールします。
インスタンスの追加ボタン(>)をクリックして、データ・モデル内で現在選択されているマスター・インスタンスに、自分が選択した名前でディテール・インスタンスを追加します。
これらの手順を行った後、「データ・モデル」リストは、図5-28のようなリストになります。
このアクティブ・マスター/ディテール調整の効果を確認する最も簡単な方法は、アプリケーション・ナビゲータでそのポップアップ・メニューから「テスト」を選択し、UserServiceでBusiness Component Browserを起動することです。図5-29は、表示されるブラウザ・ウィンドウを示しています。データ・モデル・ツリーは、RequestsAssignedビュー・オブジェクト・インスタンスでUserListビュー・オブジェクト・インスタンスをアクティブに調整するビュー・リンク・インスタンスを示します。このインスタンスの名前は、RequestsAssignedTo1というデフォルトのビュー・リンク・インスタンス名です。ツリーでこのビュー・リンク・インスタンス・ノードをダブルクリックすると、図5-29に示すマスター/ディテール・パネルが開きます。ツールバー・ボタンを使用してマスター・ビュー・オブジェクト内で移動する(結果としてビュー・オブジェクトの現在行を変更する)場合に、調整されたディテールのセットが自動的にリフレッシュされ、ユーザー・インタフェースは同期を維持します。
以前に追加したAllOpenOrPendingServiceRequestsビュー・オブジェクト・インスタンスの上でダブルクリックする場合も、2番目のタブが開いてそのインスタンスのデータを表示します。これは、同じdevguide.examples.Usersビュー・オブジェクトの別のインスタンスですが、ビュー・リンクによってアクティブに調整されていないため、その問合せはUserListの現在行によって制約されません。
ここまでは、2つのビュー・オブジェクト間の基本のマスター/ディテール関係を定義するビュー・リンクを見てきました。より多くのビュー・リンクを作成することにより、次のものを含む複雑なマスター/ディテール階層を実現できます。
マルチレベルのマスター/ディテール/ディテール
複数の(ピア)ディテールがあるマスター
複数のマスターがあるディテール
これらのより複雑な階層を定義する手順は、ここで説明された手順と同じです。1回に1つのビュー・リンクを作成するだけですみます。
これまで説明してきたように、ビュー・オブジェクトの基本的な問合せ機能はすべて、カスタムJavaコードを使用せずに実現できます。クライアントは、ビュー・オブジェクト開発者の役割でカスタム・コードを実行しなくても、SQL問合せのデータを取得して繰り返すことができます。つまり、多くの読取り専用ビュー・オブジェクトの場合、SQL文を定義するのみで完了します。ただし、必要な場合に備え、ビュー・オブジェクトに対するカスタムJava生成を有効にする方法を理解することが大切です。通常、カスタム・ビュー・オブジェクトおよびビュー行クラスで記述、使用およびオーバーライドする一般的なコードの詳細は、付録D「ADF Business Componentsのよく使用されるメソッド」を参照してください。後半の各章では、SRDemoアプリケーションがこれらのクラスで同様にカスタム・コードを使用する方法について個々の例を使用して説明します。
ビュー・オブジェクトのカスタムJavaクラスの生成を有効にするには、ビュー・オブジェクト・エディタの「Java」ページを使用します。図5-30のように、ビュー・オブジェクトには3つのオプションのJavaクラスを関連付けることができます。リストの最初の2つが、最も一般的に使用されます。
ビュー・オブジェクト・クラス: 問合せを実行するコンポーネントを表します。
ビュー行クラス: 問合せ結果の各行を表します。
カスタム・ビュー・オブジェクト・クラスの生成を有効にする場合、「バインド変数アクセッサ」チェック・ボックスも選択すると、JDeveloperではビュー・オブジェクト・クラスにgetterメソッドおよびsetterメソッドが生成されます。Usersビュー・オブジェクトには3つの名前付きバインド変数(TheName、LowUserIdおよびHighUserId)があるため、カスタムのUsersImpl.javaビュー・オブジェクト・クラスは次のような対応するメソッドを持ちます。
public Number getLowUserId() {...}
public void setLowUserId(Number value) {...}
public Number getHighUserId(){...}
public void setHighUserId(Number value) {...}
public String getTheName() {...}
public void setTheName(String value){...}
これらのメソッドを使用してバインド変数を設定すると、コンパイル時の型チェックで適切な型の値を設定しているかどうかが確認されます。つまり、LowUserIdの値を設定するために次のようなコードを記述するかわりに、
vo.setNamedWhereClauseParam("LowUserId",new Number(150));
次のようなコードを記述できます。
vo.setLowUserId(new Number(150));
後者の方法では、setLowUserIdではなく誤ってsetLowUserNameと入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error vo.setLowUserName(new Number(150));
または、Number値ではなくABCのような型を渡すように、不適切なデータ型の値を渡す場合は、次のようになります。
// passing String where number expected gives compile error
vo.setLowUserId("ABC");
バインド変数アクセッサが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both variable name and value wrong, but compiler cannot catch it
vo.setNamedWhereClauseParam("LowUserName","ABC");
これには、スペルが正しくないバインド変数名とデータ型が誤っているバインド変数値が両方とも含まれています。ViewObjectインタフェース上で汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
カスタム・ビュー行クラスの生成を有効にする場合に、「アクセッサ」チェック・ボックスも選択すると、JDeveloperではビュー行の属性ごとにgetterメソッドおよびsetterメソッドが生成されます。Usersビュー・オブジェクトの場合、対応するカスタムUsersRowImpl.javaクラスには次のようなメソッドが生成されます。
public Number getUserId() {...}
public void setUserId(Number value) {...}
public String getEmail() {...}
public void setEmail(String value) {...}
public String getFirstName() {...}
public void setFirstName(String value) {...}
public String getLastName() {...}
public void setLastName(String value) {...}
public String getUserRole() {...}
public void setUserRole(String value) {...}
これらのメソッドを使用して行データを操作すると、コンパイル時にデータ型の使用方法が正しいかどうかがチェックされます。つまり、UserId属性の値を取得するために次のようなコードを記述するかわりに、
Number userId = (Number)row.getAttribute("UserId");
次のようなコードを記述できます。
Number userId = row.getUserId();
後者の方法では、UserIdではなく誤ってUserIdentifierと入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error Number userId = row.getUserIdentifier();
ビュー行のアクセッサ・メソッドが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both attribute name and type cast are wrong, but compiler cannot catch it
String userId = (String)row.getAttribute("UserIdentifier");
これには、スペルが正しくない属性名とキャストの型が誤っている戻り値getAttribute()が両方とも含まれています。Rowインタフェースで汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
カスタム・ビュー行クラスの生成を有効にする場合に、ビュー行属性アクセッサを生成すると、オプションで「クライアントへのアクセッサの公開」チェック・ボックスも選択できます。これにより、生成される付加的なカスタム行インタフェース(アプリケーション・クライアントで使用可能)が、実装クラスに直接依存することなく行のカスタム・メソッドにアクセスできるようになります。第4章「ADF Business Componentsの概要」で学習したように、具体的なクラスのかわりにビジネス・サービス層インタフェースをクライアント・コードで使用することが、サーバー側で実装を行う際にクライアント・コードを変更する必要がないことを確認するベスト・プラクティスです。
Usersビュー・オブジェクトの場合、クライアントへのアクセッサの公開によって、UsersRowという名前のカスタム行インタフェースが生成されます。このインタフェースは、ビュー・オブジェクトが存在するパッケージのcommonサブパッケージに作成されます。行インタフェースを持つことにより、クライアントでは、強く型付けされた方式で問合せ結果の属性にアクセスするコードを記述できるようになります。例5-16は、getUserId()とgetEmail()をコールできるように、UsersRowインタフェースへのnext()メソッドの結果をキャストするTestClient3サンプル・クライアント・プログラムを示します。
例5-16 アクセッサによるクライアント行インタフェースの使用の単純な例
package devguide.examples.client;
import devguide.examples.common.UsersRow;
import oracle.jbo.ApplicationModule;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.Number;
public class TestClient3 {
public static void main(String[] args) {
String amDef = "devguide.examples.UserService";
String config = "UserServiceLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef, config);
ViewObject vo = am.findViewObject("UserList");
vo.executeQuery();
while (vo.hasNext()) {
// Cast next() to a strongly-typed UsersRow interface
UsersRow curUser = (UsersRow)vo.next();
Number userId = curUser.getUserId();
String email = curUser.getEmail();
System.out.println(userId+ " " + email);
}
Configuration.releaseRootApplicationModule(am, true);
}
}
これまでの説明で、ビュー・オブジェクトの実行時動作をカスタマイズする必要がある場合や、バインド変数またはビュー行属性へ強く型付けされたアクセスのみ行う場合のビュー・オブジェクトのカスタムJavaクラスの生成方法を示しました。
ADF Business ComponentsのカスタムJava生成のデフォルト設定を構成するには、「ツール」→「設定」を選択し、「ビジネス・コンポーネント」ページを開き、後で作成するビジネス・コンポーネントを使用する設定を設定します。ADF Business Componentsを初めて使用する開発者には、デフォルトではカスタムJavaクラスを生成しないよう設定することをお薦めします。必要な状況になった場合、その1つのコンポーネントに必要なカスタムJavaコードのみを有効にできます。経験を積むにつれ、どのようなデフォルト設定の組合せが最適であるかがわかるようになります。
1つ以上のカスタムJavaクラスを生成する場合、JDeveloperにより指定したJavaファイルが作成されます。devguide.examples.Usersという名前のビュー・オブジェクトの場合、カスタムJavaファイルのデフォルト名は、ビュー・オブジェクト・クラスについてはUsersImpl.java、ビュー行クラスについてUsersRowImpl.javaになります。これらのファイルは両方とも、コンポーネントのXMLコンポーネント定義ファイルと同じ./devguide/examplesディレクトリに作成されます。
ビュー・オブジェクトのJava生成オプションは、ビュー・オブジェクト・エディタの「Java」ページに後でアクセスしてもそのまま反映されています。XML定義ファイルの場合と同様、このエディタでどのような変更を行っても、カスタムJavaクラスで生成されたコードは最新の状態に保たれます。後でなんらかの理由により、カスタムJavaファイルが必要なくなった場合、「Java」ページで関連するオプションの選択を解除すると、カスタムJavaファイルを削除できます。
すべてのADFコンポーネントと同様、アプリケーション・ナビゲータでビュー・オブジェクトを選択すると、構造ウィンドウには、そのビュー・オブジェクトが構成する実装ファイルがすべて表示されます。唯一必要なファイルは、XMLコンポーネント定義ファイルです。前に説明したように、コンポーネントに翻訳可能なUIコントロール・ヒントが定義されている場合、コンポーネント・メッセージ・バンドル・ファイルも存在します。図5-31のように、カスタムJavaクラスの生成を有効にした場合、これらのクラスは、ビュー・オブジェクトのソース・フォルダの下にも表示されます。カスタムJavaファイルのソース・コードを表示または操作する必要がある場合、ソース・エディタでこのファイルを開く方法には2通りあります。
図5-31のようにポップアップ・メニューで関連する「移動先」オプションを選択します。
構造ウィンドウのソース・フォルダでファイルをダブルクリックします。
カスタムJavaクラスの使用に役立つ詳細は、次の各項を参照してください。
XML専用ビュー・オブジェクトを使用する場合、実行時には、その機能はデフォルトのADF Business Components実装クラスによって提供されます。生成される各カスタムJavaクラスによって適切なADF Business Componentsのベース・クラスが自動的に拡張されるため、コードでは、デフォルトの動作を継承し、このクラスを簡単に追加またはカスタマイズできます。ビュー・オブジェクト・クラスはViewObjectImplを拡張し、ビュー行クラスはViewRowImplを拡張します(これらは両方とも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」または「適用」ボタンをクリックしても、ファイルが知らぬ間に再生成されることはありません。かわりに、管理が必要なメソッドはスマートに更新され、独自のカスタム・コードはそのまま残されます。
これまで説明したように、ビュー・オブジェクトは、XML専用モードで機能するか、XMLコンポーネント定義とカスタムJavaクラスの組合せを使用して機能するように設計されています。属性値はビュー行クラスのプライベート・メンバー・フィールドには格納されないため、XML専用モードの場合、このようなクラスは存在しません。かわりに、属性には、名前のみならず、ビュー・オブジェクトのXMLコンポーネント定義ファイル内のViewAttributeおよびアソシエーション関連のViewLinkAccessorタグのゼロベースの順序に基づいて、このファイルの数値索引も割り当てられます。実行時には、ビュー行の属性値は、ビュー・オブジェクトの属性リスト内における属性の数値的位置による索引が付けられた状態で、ViewRowImplベース・クラスによって管理される構造に格納されます。
多くの場合、このプライベート実装に関する詳細は重要ではありません。ただし、ビュー行のカスタムJavaクラスを有効にする場合、ビュー行クラスで自動的に管理される生成コードの一部にこの実装の詳細が関連し、コードの使用目的を理解できます。たとえば、Usersビュー行のカスタムJavaクラスの場合、例5-17のように、属性またはビュー・リンク・アクセッサ属性ごとに対応する生成済の整数定数があります。JDeveloperでは、これらの定数の値がXMLコンポーネント定義内の属性の順序を正しく反映しているかどうかが確認されます。
例5-17 カスタム・ビュー行Javaクラスで自動的に管理される属性定数
public class UsersRowImpl extends ViewRowImpl implements UsersRow {
public static final int USERID = 0;
public static final int EMAIL = 1;
public static final int FIRSTNAME = 2;
public static final int LASTNAME = 3;
public static final int USERROLE = 4;
public static final int ASSIGNEDREQUESTS = 5;
// etc.
また、自動的に管理される、ビュー行クラスで強く型付けされたgetterメソッドおよびsetterメソッドでは、これらの属性定数は次のように使用されます。
// In devguide.examples.UsersRowImpl class
public String getEmail() {
return (String) getAttributeInternal(EMAIL); // <-- Attribute constant
}
public void setEmail(String value) {
setAttributeInternal(EMAIL, value);// <-- Attribute constant
}
ビュー行属性定数に関連する自動管理コードの最後の2つの側面は、getAttrInvokeAccessor()およびsetAttrInvokeAccessor()メソッドです。これらのメソッドにより、数値索引を使用した属性アクセスのパフォーマンスが最適化されます。これは、ViewRowImplベース・クラスの汎用コードが属性値にアクセスするときの通常のパフォーマンスです。getAttrInvokeAccessor()メソッドの例は、次のServiceRequestImpl.javaクラスのようになります。もう1つのsetAttrInvokeAccessor()メソッドの場合も同様です。
// In devguide.examples.UsersRowImpl class
protected Object getAttrInvokeAccessor(int index,AttributeDefImpl attrDef)
throws Exception {
switch (index) {
case USERID: return getUserId();
case EMAIL: return getEmail();
case FIRSTNAME: return getFirstName();
case LASTNAME: return getLastName();
case USERROLE: return getUserRole();
case ASSIGNEDREQUESTS: return getAssignedRequests();
default:
return super.getAttrInvokeAccessor(index, attrDef);
}
}
属性と索引に関連して生成されたこのコードに関する経験則は、次のとおりです。
必要に応じて、強く型付けされた属性のgetterメソッドおよびsetterメソッドの内部にカスタム・コードを追加してください。
ビュー・オブジェクト・エディタを使用して、ビュー・オブジェクト属性の順序または型を変更してください。
getterメソッドおよびsetterメソッドのJavaシグネチャや関連するXMLコンポーネント定義は、自動的に変更されます。
getAttrInvokeAccessor()メソッドおよびsetAttrInvokeAccessor()メソッドは変更しないでください。
属性索引番号の値は手動で変更しないでください。
|
注意: ソース・コントロールのマージ競合などにより、生成された属性定数を手動で編集する必要がある場合、対応するビュー・オブジェクトのXMLコンポーネント定義のViewAttributeおよびViewLinkAccessorタグの順序がゼロベースの順序に反映されていることを確認する必要があります。 |