UIX開発者ガイド | ![]() 目次 |
![]() 前へ |
![]() 次へ |
OracleのBusiness Components for Java(BC4J)は、JDBCを利用して、Oracle9i データベースに格納されている情報の、最適化されたスケーラブルなオブジェクト・リレーショナル・マッピングを提供します。表示関連のアクティビティをWeb層に残し、ビジネス・ロジックを中間層に集中化させることができます。
BC4JとUIXのテクノロジ統合により、開発者は、属性がBC4Jデータソースにバインドされた要素を含むuiXMLページを宣言的に指定できます。UIX開発者はまた、ユーザーによってイベントがWeb層に送信された際に、これらのデータソースを検証および更新する方法も宣言的に指定できます。
このトピックでは、各項を通してこの統合についての理解を深め、最終的には、従業員データベースという、完全に機能する宣言的なWebアプリケーションを作成します。
ここでは、次の項目について説明します。
BC4J UIXテクノロジ統合により、開発者はカスタムJavaコードをまったく記述せずに、BC4J Webアプリケーション全体を宣言的に定義できます。UIXからBC4Jの機能を直接利用できることにより、Webアプリケーション開発者はこれら2つのテクノロジを統合する煩雑な作業ではなく、顧客のニーズへの対応に集中できます。
BC4Jでは、Oracle9i データベースに格納された情報を記述するために、オブジェクト指向の概念が数多く採用されています。最も中心となる概念はルート・アプリケーション・モジュールです。これは特定のタスクに関連するオブジェクトをまとめて格納する論理コンテナで、ビジネス・ロジックが付属することもあります。通常、ルート・アプリケーション・モジュールはBC4J SessionCookieを介してアクセスされ、ユーザーはこれにより、Web層との多くの対話の中から同じアプリケーション・モジュールを見つけることができます。各ルート・アプリケーション・モジュールはOracle9i データベースに接続し、この接続はBC4Jの構成という概念によって構成され、単純に名前によって参照されます。
各アプリケーション・モジュールには、ネストされたアプリケーション・モジュール以外に、名前付きのビュー・オブジェクトを含めることができます。ビュー・オブジェクトは、Oracle9i データベースからSQL文で選択された1つ以上のエンティティ・オブジェクトの、構造的な組合せを定義します。SQL文には、ORDER BY句またはWHERE句をオプションで使用できます。各エンティティ・オブジェクトは、表やビューなどのデータベース・オブジェクトから取得したデータをオブジェクト指向で表したものです。表間のデータベース外部キー関係は、エンティティ・オブジェクト間の名前付きAssociationとして取得されます。
問合せが行われた後のビュー・オブジェクトでは、各行を順に処理し、その属性を名前で取得することで結果にアクセスできます。属性名は、設計段階でビュー・オブジェクトの構造を定義する際に決定します。この、構造的に定義されるビュー・オブジェクト内の各属性は、AttributeDefと呼ばれます。ディテール・ビュー・オブジェクトの内容が、マスター・ビュー・オブジェクトの現在の行に依存する場合があります。実行時におけるこの2つのビュー・オブジェクト問合せ間の関係は、名前付きのビュー・リンクで取得されます。ビュー・リンクは、各ビュー・オブジェクトのエンティティ・オブジェクト(それぞれ1つ)間のAssociationによって定義できます。
ビジネス・ロジックによっては、異なるORDER BY句またはWHERE句を使用して2回以上ビュー・オブジェクトの問合せを実行し、かつ両方の結果を同時に使用可能にすることが必要な場合があります。このような要件は、BC4Jの行セットという概念で達成されます。行セットは名前でアクセスされ、ビュー・オブジェクトに論理的に含まれます。
BC4Jが提供するオブジェクト指向マッピングの基本を理解したところで、次にサンプル・アプリケーション用ビジネス・コンポーネントの作成へと進みます。
従業員データベースでは、 oracle.cabo.data.jbo.demo.employee
JavaパッケージにBC4Jコンポーネントを定義して使用します。これらのコンポーネントは、Oracle9i JDeveloperのビジネス・コンポーネント・ウィザードをSCOTTスキーマのDEPT表とEMP表に対して使用して作成する、次のコンポーネントです。
ビジネス・コンポーネント | 説明 |
---|---|
EmployeeModule |
従業員データベース・アプリケーション・モジュール |
DeptView |
部門ビュー・オブジェクト |
EmpView |
従業員ビュー・オブジェクト |
EmpForeignKeyLink |
部門番号によるDepView からEmpView へのビュー・リンク |
EmpSelfKeyLink |
マネージャ番号によるEmpView からEmpView へのビュー・リンク |
Dept |
部門エンティティ・オブジェクト |
Emp |
従業員エンティティ・オブジェクト |
EmpForeignKeyAssoc |
部門番号によるDept からEmp へのAssociation |
EmpSelfKeyAssoc |
マネージャ番号によるEmp からEmp へのAssociation |
デフォルトでは、EmpView
ビュー・オブジェクトを参照する際のデフォルトの名前は、EmpView1、EmpView2およびEmpView3となります。次のように EmployeeModule
を編集して、これらの名前をより意味のある内容に変更できます。
EmployeeModule
EmpForeignKeyLink
によるEmpView)EmpSelfKeyLink
によるEmpView)太字の箇所が変更した部分です。
従業員データベースでは、従業員が自分の従業員番号を後から変更できないようにします。したがって、Emp
エンティティ・オブジェクトを編集し、「新規の間」の場合のみEmpno属性を更新できるようにする必要があります。これにより、新しい従業員には従業員番号を定義できますが、後日誤って更新しようとしても受け付けられません。
これで必要なBC4Jビジネス・コンポーネントはすべて準備できました。次にuiXMLページを作成します。
BC4J UIXテクノロジ統合を利用するuiXMLページの作成時には、対応するネームスペースを次のように定義する必要があります。
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:bc4j="http://xmlns.oracle.com/uix/bc4j" >
...
</page>
ネームスペースに推奨される接頭辞はbc4jで、このトピックの例ではすべてこの接頭辞を使用します。
すでに説明したように、BC4Jプロジェクトにはいくつもの異なるビジネス・コンポーネントを含めることができます。ただし、Webアプリケーションのすべてのページで、これらのビジネス・コンポーネントをすべて使用する必要はありません。特定のページで使用するビジネス・コンポーネントを定義するには、新たに採用されたセクション<bc4j:registryDef>
を使用します。
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:bc4j="http://xmlns.oracle.com/uix/bc4j" >
<bc4j:registryDef>
...
</bc4j:registryDef>
</page>
<bc4j:registryDef>
セクションには、そのページで使用するルート・アプリケーション・モジュールと、関連するネストされたアプリケーション・モジュールおよびビュー・オブジェクトのリストを記述します。各ルート・アプリケーション・モジュールは、レンダリングの開始前にプールからチェックアウトされ、レンダリングの完了時に再度解放されます。解放モードがステートレスではない場合、ユーザー・インタフェースのコンテンツが生成される前に、これに対応してCookieが記述されます。その後、ユーザーからWeb層へのリクエストの送信時には、このCookieによって同じアプリケーション・モジュールが検索されます。
従業員データベースWebアプリケーションは、次の3つのページで構成されています。
createEmp.uixとmodifyEmp.uixはどちらもDeptEmpsVO
ビュー・オブジェクトのみを使用するため、ページ定義はよく似ています。
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="EmpAppModule"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="CurrentDeptEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView" >
<bc4j:rowDef name="CreateEmp" >
<bc4j:propertyKey name="key" />
</bc4j:rowDef>
</bc4j:viewObjectDef>
</bc4j:rootAppModuleDef>
</bc4j:registryDef>
name
属性は、UINodeツリーおよびイベント・ハンドラからルート・アプリケーション・モジュール(またはビュー・オブジェクト、行)を参照する際に使用されます。ルート・アプリケーション・モジュールの名前は、アプリケーションのIDとしても使用され、同一アプリケーション内のすべてのページにわたって一貫している必要があります。defFullName
属性はアプリケーション・モジュールまたはビュー・オブジェクトの完全な定義名を指定し、configName
属性はBC4Jのランタイム・エンジンにOracle9i データベースへの接続に使用する構成を伝え、releaseMode
属性は状態とリソースの管理方法を指定します。
viewEmps.uixページはAllDeptsVO
とCurrentDeptEmpsVO
の両方を使用するため、次のようなページ定義となります。
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="DeptEmpApp"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="AllDeptsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.DeptView" >
<bc4j:rowDef name="CurrentDept" />
</bc4j:viewObjectDef>
<bc4j:viewObjectDef name="CurrentDeptEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView" />
</bc4j:rootAppModuleDef>
</bc4j:registryDef>
これでページ定義が作成されました。次は、作成したページ定義を使用してユーザー・インタフェース・コンテンツを定義し、ユーザーの操作で生成されるイベントを処理します。
BC4J UIXテクノロジ統合におけるユーザー・インタフェースの定義は、次の3つに分類されます。
各ビジネス・コンポーネントの概念はUINode
のスコープを使用して表現され、索引付けされた子はそのビジネス・コンポーネントのスコープ内に入ります。<bc4j:attrScope>
はその一例です。
スコープ内のビジネス・コンポーネントのプロパティには、 <bc4j:attrProperty>
などのBC4Jのデータ・バインド値を使用してアクセスできます。
対応するBC4Jメタデータと各フォーム要素の該当する属性をバインドする標準テンプレート要素が用意されています。<bc4j:inlineMessage>
がその例で、これは基礎となるスコープ内の属性の必須プロパティからrequired
などの属性値を導出します。
レンダリングの際、BC4J UIX統合では、UINode
のスコープを使用して各ビジネス・コンポーネントを表現します。たとえば、あるルート・アプリケーション・モジュールをスコープするUINode
の索引付けされた子は、指定したルート・アプリケーション・モジュールのコンテキスト内にあります。その名前は、BC4Jのレジストリ定義におけるルート・アプリケーション・モジュールと一致する必要があります。ネストされたアプリケーション・モジュールのスコープは、<bc4j:rootAppModuleScope>
または別の<bc4j:appModuleScope>
のコンテキスト内で設定できます。その場合、BC4Jレジストリ定義の同じレベルにあるアプリケーション・モジュールと名前が一致する必要があります。
次に例を示します。
<bc4j:rootAppModuleScope name="root" >
<contents>
...
<!-- indexed children scoped by the root Application Module
named "root" -->
...
<bc4j:appModuleScope name="alpha" >
<contents>
...
<bc4j:appModuleScope name="beta" >
<contents>
...
<!-- indexed children scoped by nested Application Module "alpha"
in nested Application Module "beta", in the root Application
Module named "root" -->
...
</contents>
</bc4j:appModuleScope>
...
</contents>
</bc4j:appModuleScope>
...
</contents>
</bc4j:rootAppModuleScope>
ビュー・オブジェクト・スコープは<bc4j:rootAppModuleScope>
または<bc4j:appModuleScope>
のいずれかのコンテキスト内で設定でき、ビュー・オブジェクト名で識別されます。行セット・スコープは<bc4j:viewObjectScope>
のコンテキスト内でのみ設定でき、名前で参照されます。BC4Jビュー・オブジェクトは行セットでもあるため、ビュー・オブジェクトを作成すると、ビュー・オブジェクト自体に対する<bc4j:rowSetScope>
が作成されます。行スコープは、BC4Jレジストリ定義で定義された名前付きのRowDefを使用して、現在の行を作成します。デフォルトでは、最初の<bc4j:viewObjectScope>
または<bc4j:rowSetScope>
によって現在の行が自動的にスコープ内に入ります。
次に例を示します。
<bc4j:appModuleScope name="alpha" >
<contents>
<bc4j:viewObjectScope name="beta" >
<contents>
...
<!-- indexed children scoped by View Object "beta",
in Application Module "alpha". -->
...
<bc4j:rowSetScope name="gamma" >
<contents>
...
<!-- indexed children scoped by Row Set "gamma", of
View Object "beta", in Application Module "alpha". -->
...
<bc4j:rowScope name="delta" >
<contents>
...
<!-- indexed children scoped by Row "delta" of
Row Set "gamma", of View Object "beta",
in Application Module "alpha". -->
...
</contents>
</bc4j:rowScope>
</contents>
</bc4j:rowSetScope>
</contents>
</bc4j:viewObjectScope>
</contents></bc4j:appModuleScope>
BC4Jビュー・オブジェクトの構造情報は、BC4J概念であるAttributeDefによって記述されます。これまでの説明から、現在のスコープ内の属性定義を設定する構文が予測可能な場合があります。属性の更新可能性など、BC4Jメタデータの中には、AttributeDefだけでなく現在の行にも依存するものがあります。AttributeDefの更新可能性が「新規の間」としてマークされている場合、この属性は新規作成された行では更新できますが、既存の行では更新できません。このような場合、現在の行の現在の属性をスコープに配置する必要があります。この操作により、対応するAttributeDefも暗黙的にスコープに配置されます。
次に例を示します。
<bc4j:viewObjectScope name="alpha" >
<contents>
<bc4j:attrDefScope name="beta" >
<contents>
...
<!-- indexed children scoped by AttributeDef "beta",
in View Object "alpha". -->
...
</contents>
</bc4j:attrDefScope>
<bc4j:rowScope>
<contents>
<bc4j:attrScope name="gamma" >
<contents>
...
<!-- indexed children scoped by both Attribute "gamma"
in the current Row and AttributeDef "gamma" in
View Object "alpha". -->
...
</contents>
</bc4j:attrScope>
</contents>
</bc4j:rowScope>
</contents>
</bc4j:viewObjectScope>
City、StreetおよびZip codeという属性を持つAddress属性のような構造化されている属性の場合、ネストされた<bc4j:attrScope>
要素を次のように使用してスコープに配置できます。
...
<bc4j:rowScope>
<contents>
<bc4j:attrScope name="Address" >
<contents>
<bc4j:attrScope name="City" >
<contents>
...
<!-- indexed children scoped by Attribute and AttributeDef "City"
in structured attribute "Address" in the current Row. -->
...
</contents>
</bc4j:attrScope>
</contents>
</bc4j:attrScope>
</contents>
</bc4j:rowScope>
...
現在の行に多数の構造化属性がある場合、この方法では煩雑になるため、より簡単なピリオド表記も使用できます。
...
<bc4j:rowScope>
<contents>
<bc4j:attrScope name="Address.City" >
<contents>
...
<!-- indexed children scoped by Attribute and AttributeDef "City"
in structured attribute "Address" in the current Row. -->
...
</contents>
</bc4j:attrScope>
</contents>
</bc4j:rowScope>
...
これで、ユーザー・インタフェースの中で、対応すべきスコープを設定することができるようになります。次のセクションでは、現在のスコープに関する情報を使用して、UIXコンポーネントの属性値を割り当てる方法について説明します。
各BC4J概念であるスコープを設定後、これらのスコープを使用して様々なUIX要素に属性値をバインドします。これには、標準のUIXデータ・バインドを使用します。BC4J UIX統合では、各スコープのプロパティにアクセスするための、標準のデータ・バインド要素が提供されます。つまり、現在のAttributeDefが必須かどうか、現在の属性が更新可能かどうかという情報や、現在スコープ内にある属性値などのデータをバインドしたUIX要素を作成できます。
次に例を示します。
<bc4j:viewObjectScope name="alpha" >
<contents>
<bc4j:attrScope name="beta" >
<contents>
<messageTextInput prompt="Beta" >
<!-- bind the required attribute to the BC4J
"mandatory" AttributeDef metadata. -->
<boundAttribute name="required" >
<bc4j:attrDefProperty name="mandatory" />
</boundAttribute>
<!-- bind the readOnly attribute to the inverse
of the BC4J "updatable" Attribute metadata. -->
<boundAttribute name="readOnly" >
<not>
<bc4j:attrProperty name="updatable" />
</not>
</boundAttribute>
<!-- bind the text attribute to the
BC4J "value" Attribute metadata. -->
<boundAttribute name="text" >
<bc4j:attrProperty name="value" />
</boundAttribute>
</messageTextInput>
</contents>
</bc4j:attrScope>
</contents>
</bc4j:viewObjectScope>
<bc4j:attrProperty>
は非常に柔軟性がありますが、繰り返し属性スコープを作成するのは少々煩雑な作業です。<bc4j:attrValue>
要素では1回のステップで、属性をスコープに入れ値を取得します。
<bc4j:viewObjectScope name="alpha" >
<contents>
<messageTextInput prompt="Beta" >
<!-- bind the text attribute to the value of the
"beta" attribute. -->
<boundAttribute name="text" >
<bc4j:attrValue name="beta" />
</boundAttribute>
</messageTextInput>
</contents>
</bc4j:viewObjectScope>
これは便利なだけではありません。<bc4j:attrValue>
は、<bc4j:attrScope>
を配置できない場所にも配置できます。特に、<bc4j:attrScope>
は<boundAttribute>
内に配置できないため、Beanの2つの値を2つの異なる属性にバインドすることが困難になります。<bc4j:attrValue>
を使用すればこれを解決できます。たとえば、ある属性にリンクのtextをバインドし、別の属性にdestinationをバインドできます。
<bc4j:viewObjectScope name="alpha" >
<contents>
<link>
<!-- bind the text attribute to the value of the
"someText" attribute. -->
<boundAttribute name="text" >
<bc4j:attrValue name="someText" />
</boundAttribute>
<!-- bind the destination attribute to the value of the
"someURL" attribute. -->
<boundAttribute name="destination" >
<bc4j:attrValue name="someURL" />
</boundAttribute>
<link>
</contents>
</bc4j:viewObjectScope>
UIXデータ・バインドは柔軟性があるため、任意のUIX属性値と、スコープ内の任意のビジネス・コンポーネント・プロパティとをバインドできます。ただし、各UIX属性値を個別にバインドする作業は単調で時間を要し、エラーの原因となるため、一般的なケースについては標準のBC4Jテンプレートが用意されています。UIX開発者にとっては、これらのテンプレートはBC4JネームスペースにあるUIX要素として映ります。
次に例を示します。
<bc4j:viewObjectScope name="alpha" >
<contents>
<bc4j:messageTextInput prompt="Beta" attrName="beta" />
</contents>
</bc4j:viewObjectScope>
<textInput>
、<dateField>
、<choice>
および<messageXXX>
などのUIX Componentsの各フォーム要素には、すべてattrName属性を持つ対応するBC4J UIX要素があります。これによって、指定されたattrNameとともに<bc4j:attrScope>
が内部的に設定され、BC4Jメタデータがフォーム要素の該当する属性にバインドされます。
<bc4j:table>
テンプレートには、スコープ内のBC4Jビュー・オブジェクト属性メタデータから一連の列を導出する機能があります。これは自動モードと呼ばれます。名前の付けられた子であるcolumnStampには、通常<bc4j:column>
が含まれます。列ヘッダーは現在のAttributeDefのコンテキスト内でレンダリングされ、列の内容は現在の属性のコンテキスト内で行ごとに1回レンダリングされます。<bc4j:table>
のもう1つの名前の付けられた子である<bc4j:keyStamp>
は、行を識別するために行ごとに1回レンダリングされます。<bc4j:rowKey>
をここで使用し、BC4Jの各行に対して、それらを一意に識別するためのキーを外部的に作成できます。このキー値はフォーム要素として出力できます。
次に例を示します。
<bc4j:table name="alpha" automatic="true" >
<!-- the key identifying the current row in the table -->
<bc4j:keyStamp>
<bc4j:rowKey name="key" />
</bc4j:keyStamp>
<!-- the column rendered for each attribute -->
<bc4j:columnStamp>
<!-- the attrScope is automatic -->
<bc4j:column>
<columnHeader>
<!-- the sortableHeader defaults its attrName
from the current attrScope -->
<bc4j:sortableHeader/>
</columnHeader>
<contents>
<!-- renders the current attribute as read only -->
<bc4j:input readOnly="true" />
</contents>
</bc4j:column>
</bc4j:columnStamp>
</bc4j:table>
自動モードでは、各BC4J属性値のレンダリングが可能なコンポーネントが<columnStamp>内に含まれている必要がありますが、これはすべての列で同じであるとはかぎりません。このため、BC4J UIX統合では、<bc4j:input>
と<bc4j:messageInput>
という2つの新しいフォーム要素が使用されます。これらの要素は、属性タイプに関するBC4Jメタデータを使用して、<bc4j:dateField>
や<bc4j:textInput>
などの適切なフォーム要素への切替えを行います。
自動モードは表以外でも使用できます。ここでもう1つの新しい要素<bc4j:region>
を紹介します。これには、スコープ内のBC4Jビュー・オブジェクトに含まれる各AttributeDefに対して、名前の付けられた子attrStampを繰り返す機能があります。この機能は通常、フォームに表示されるフィールドの作成を自動化するために使用されます。attrStampの繰返しの前に、Keyという名前の付けられた子がレンダリングされます。
次に例を示します。
<bc4j:region automatic="true" >
<!-- the key identifying the current row -->
<bc4j:key>
<bc4j:rowKey name="key" />
</bc4j:key>
<!-- the stamp for each attribute -->
<bc4j:attrStamp>
<bc4j:messageInput/>
</bc4j:attrStamp>
</bc4j:region>
従業員データベースのユーザー・インタフェースに、自動モードを使用しましょう。サマリー・ページには部門とその従業員が表示され、新規の従業員データの作成、既存の従業員データの更新、または退職した従業員の削除を行うためのリンクがあるようにします。
サマリー・ページの最初のセクションでは、次のように現在の部門行に対するリージョンを、読取り専用のmessageInput
を含むattrStamp
を付けて自動モードで使用します。
<bc4j:rootAppModuleScope name="EmpAppModule" >
<contents>
<bc4j:viewObjectScope name="AllDeptsVO" >
<contents>
<bc4j:region automatic="true" >
<bc4j:attrStamp>
<bc4j:messageInput readOnly="true" />
</bc4j:attrStamp>
</bc4j:region>
</contents>
</bc4j:viewObjectScope>
</contents>
</bc4j:rootAppModuleScope>
従業員データの作成ページおよび変更ページに対しても同じような<bc4j:region>
が必要です。ただし、スコープをCurrentDeptEmpsVOビュー・オブジェクトに変更し、messageInputにreadOnly属性は設定しません。
現在の部門の従業員表は、columnStampにある列の内容を読取り専用に設定した状態で、<bc4j:table>
を自動モードで使用します。
<bc4j:rootAppModuleScope name="EmpAppModule" >
<contents>
<bc4j:viewObjectScope name="CurrentDeptEmpsVO" >
<contents>
<bc4j:table name="emps" automatic="true" >
<bc4j:keyStamp>
<bc4j:rowKey name="key" />
</bc4j:keyStamp>
<bc4j:columnStamp>
<bc4j:column>
<columnHeader>
<bc4j:sortableHeader/>
</columnHeader>
<contents>
<bc4j:input readOnly="true" />
</contents>
</bc4j:column>
</bc4j:columnStamp>
</bc4j:table>
</contents>
</bc4j:viewObjectScope>
</contents>
</bc4j:rootAppModuleScope>
これにより、各属性別に列のある表がレンダリングされ、列ヘッダー・テキストには属性名がデフォルトで使用され、内容は読取り専用で表示されます。ユーザーが列ヘッダーをクリックすると、その列がソートされます。ユーザーがCurrentDeptEmpsVOビュー・オブジェクト内のデータをナビゲートできるよう、ナビゲーション・バーも表示されます。
これでユーザー・インタフェースの定義が終了しました。次に、各ページによって起動される各UIX Controllerイベントの動作も定義する必要があります。
UIXでは、UIX Componentsの宣言的定義は非常に簡単で、イベント・ハンドラのあるuiXMLページのみでなく、再利用可能なコンポーネントもuiXMLテンプレートを使用して簡単に定義できますが、イベント・ハンドラの動作の宣言的定義は、現在のところ部分的にしかサポートされていません。一般的に、開発者は独自のイベント処理Javaコードを記述し、<method/>
要素または<instance/>
要素を使用して参照します。BC4J UIXテクノロジ統合には標準のイベント・ハンドラ要素が用意されており、通常のケースについてはカスタムJavaコードを作成する必要はありません。
次に例を示します。
<handlers>
<event name="goto" >
<bc4j:findRootAppModule name="root" >
<bc4j:findViewObject name="alpha" >
<bc4j:goto/>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
</handlers>
最初のスコープ設定と同じ手法がイベント処理にも当てはまります。<bc4j:goto>
イベント・ハンドラはUIX Componentsのgotoイベントの構造を解釈し、スコープ内のBC4J行セット(または行セットであるビュー・オブジェクト)に適用します。同様に、<bc4j:sort>
イベント・ハンドラはUIX Componentsのsortイベントを解釈して適用します。
<bc4j:table>
を含む従業員データベースのサマリー・ページには、次のようにsortイベントとgotoイベント両方のエントリが必要です。
<handlers>
<event source="emps" name="goto" >
<bc4j:findRootAppModule name="EmpAppModule" >
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<bc4j:goto/>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
<event source="emps" name="sort" >
<bc4j:findRootAppModule name="EmpAppModule" >
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<bc4j:sort/>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
...
</handlers>
従業員データ作成ページには、すべての属性に対するフォーム要素を自動作成するためにリージョンを組み込み、フォーム送信を処理し、各パラメータ値を対応するBC4J行に適用するエントリを追加します。
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="EmpAppModule"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="CurrentDeptEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView" >
<!-- setting the "autoCreate" attribute allows a default Row
to be created when the Key does not locate an existing
Row. -->
<bc4j:rowDef name="CreateEmp" autoCreate="true" >
<bc4j:propertyKey name="key" />
</bc4j:rowDef>
</bc4j:viewObjectDef>
</bc4j:rootAppModuleDef>
</bc4j:registryDef>
...
<handlers>
<event name="apply" >
<bc4j:findRootAppModule name="EmpAppModule" >
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- find the row identified by the RowDef "CreateEmp" above, then
set the region automatically, insert the row and commit the
changes. If no validation errors occur, navigate to the
"viewEmps" summary page -->
<bc4j:findRow name="CreateEmp" >
<!-- the Row Key can change during form submission
specifically when the Row is automatically created.
transfer current key value to page property
for "CreateEmp" RowDef above -->
<bc4j:setPageProperty name="key" >
<bc4j:stringKey/>
</bc4j:setPageProperty>
<bc4j:setRegion automatic="true" />
<bc4j:insertRow/>
<bc4j:commit/>
<ctrl:go name="viewEmps" />
</bc4j:findRow>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
...
</handlers>
同様に、従業員データ変更ページにもすべての属性に対するフォーム要素作成のためのリージョンの組込みに加えて、類似したエントリが必要ですが、現在の行のキーは最初にページがアクセスされる際に、ページ・プロパティから取得されます。このキーはフォーム送信中も変わらないため、行キーをフォーム・パラメータとしてレンダリングする必要はありません。
これでBC4Jイベント・ハンドラの定義が終了しました。次に、ページ・ナビゲーション・ロジックを追加して、従業員データベース・アプリケーションを完成させます。
従業員データベース・アプリケーションの最後の作業は、ページ間のナビゲーション・ロジックの追加です。サマリー・ページには、従業員データ作成ページに直接リンクするボタンと、選択されたBC4J行のキーをページ・プロパティとして渡して従業員データ変更ページへと移動するUpdateボタンを用意します。
作成ページと変更ページには、変更内容を適用するボタンと、現在の変更をロールバックしてサマリー・ページに戻るCancelボタンを用意します。
BC4J UIX要素が認識される前にBC4J UIExtentionを登録するよう、UIX Controllerを構成する必要があります。これを行うには、UI拡張機能をuix-config.xml
ファイルに追加します。
注意:
アプリケーションのweb.xmlファイルに定義されるUIXサーブレットのoracle.cabo.ui.UIExtensions初期化パラメータにoracle.cabo.data.jbp.ui.jboUIExtensionの値がセットされている場合、以下の手順は必要ありません。
<?xml version="1.0" encoding="ISO-8859-1"?>
<configurations xmlns="http://xmlns.oracle.com/uix/config">
<application-configuration>
<ui-extensions>
<ui-extension>oracle.cabo.data.jbo.ui.JboUIExtension</ui-extension>
</ui-extensions>
</application-configuration>
</configurations>
uix-config.xml
の詳細は、「構成」のトピックを参照してください。
次に、従業員データベース・アプリケーションの完全なソースを示します。
<?xml version="1.0" encoding="UTF-8" ?>
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:ui="http://xmlns.oracle.com/uix/ui"
xmlns:bc4j="http://xmlns.oracle.com/uix/bc4j" >
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="EmpAppModule"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="AllDeptsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.DeptView" />
<bc4j:viewObjectDef name="CurrentDeptEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView"
rangeSize="3" />
</bc4j:rootAppModule>
</bc4j:registryDef>
<content>
<pageLayout xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
title="View Emps" >
<contents>
<!-- include the view source link -->
<link text="View Source" ctrl:event="viewSource" />
<!-- this will contain any validation errors after form
submission -->
<messageBox automatic="true" />
<form name="emp" >
<contents>
<tableLayout>
<contents>
<bc4j:rootAppModuleScope name="EmpAppModule" >
<contents>
<bc4j:viewObjectScope name="AllDeptsVO" >
<contents>
<!-- placing the region in automatic mode will cause the key
named child to be rendered, followed by each attribute
in the ViewObject using the attrStamp named child. -->
<bc4j:region automatic="true" >
<!-- the stamp for each attribute -->
<bc4j:attrStamp>
<bc4j:messageInput readOnly="true" />
</bc4j:attrStamp>
</bc4j:region>
<bc4j:viewObjectScope name="CurrentDeptEmpsVO" >
<contents>
<!-- placing the table in automatic mode will cause the table
to contain the keyStamp named child followed by the
columnStamp named child for each attribute in the
ViewObject. -->
<bc4j:table name="emps" automatic="true" width="80%" >
<tableSelection>
<!-- single selection for each row in the table -->
<singleSelection>
<contents>
<!-- the update button causes the currently selected
row to be sent to the update page -->
<submitButton text="Update"
ctrl:event="update" />
<!-- the delete button causes the currently selected
row to be removed -->
<submitButton text="Delete"
ctrl:event="delete" />
</contents>
</singleSelection>
</tableSelection>
<!-- the key identifying the current row in the table -->
<bc4j:keyStamp>
<bc4j:rowKey name="key" />
</bc4j:keyStamp>
<!-- the column rendered for each attribute -->
<bc4j:columnStamp>
<!-- the attrScope is automatic -->
<bc4j:column>
<columnHeader>
<!-- the sortableHeader defaults its attrName
from the current attrScope -->
<bc4j:sortableHeader/>
</columnHeader>
<contents>
<!-- renders the current attribute as read only -->
<bc4j:input readOnly="true"/>
</contents>
</bc4j:column>
</bc4j:columnStamp>
</bc4j:table>
</contents>
</bc4j:viewObjectScope>
</contents>
</bc4j:viewObjectScope>
</contents>
</bc4j:rootAppModuleScope>
</contents>
</tableLayout>
</contents>
</form>
</contents>
<contentFooter>
<!-- the create button redirects to the create page -->
<button text="Create" ctrl:event="create" />
</contentFooter>
</pageLayout>
</content>
<handlers>
<event name="sort" source="emps" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- establish the ViewObject scope -->
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- sort by the submitted attribute name -->
<bc4j:sort/>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
<event name="goto" source="emps" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- establish the ViewObject scope -->
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- navigate to the submitted range -->
<bc4j:goto/>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
<event name="create" >
<!-- forward to the create page -->
<go name="createEmp" />
</event>
<event name="update" >
<!-- forward to the update page, passing the selected key
as a page property -->
<go name="modifyEmp" >
<property name="key" >
<selection name="emps" key="key" />
</property>
</go>
</event>
<event name="delete" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- establish the ViewObject scope -->
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- find the selected ViewObject row -->
<bc4j:findRowByKey>
<bc4j:keyBinding>
<bc4j:selectionKey name="emps" key="key" />
</bc4j:keyBinding>
<bc4j:handlers>
<!-- remove the selected ViewObject row -->
<bc4j:removeRow />
<!-- execute the query to eliminate dead row access -->
<bc4j:executeQuery/>
</bc4j:handlers>
</bc4j:findRowByKey>
</bc4j:findViewObject>
<!-- commit the transaction, forwards to self automatically -->
<bc4j:commit/>
</bc4j:findRootAppModule>
</event>
</handlers>
</page>
<?xml version="1.0" encoding="UTF-8" ?>
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:ui="http://xmlns.oracle.com/uix/ui"
xmlns:bc4j="http://xmlns.oracle.com/uix/bc4j" >
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="EmpAppModule"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="CurrentDeptEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView" >
<!-- setting the "autoCreate" attribute allows a default Row
to be created when the Key does not locate an existing
Row. -->
<bc4j:rowDef name="CreateEmp" autoCreate="true" >
<bc4j:propertyKey name="key" />
</bc4j:rowDef>
</bc4j:viewObjectDef>
</bc4j:rootAppModuleDef>
</bc4j:registryDef>
<content>
<pageLayout xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
title="Create Emp" >
<contents>
<!-- include the view source link -->
<link text="View Source" ctrl:event="viewSource" />
<!-- this will contain any validation errors after form
submission -->
<messageBox automatic="true" />
<form name="emp" >
<contents>
<!-- we cannot implicitly determine that events
will be triggered because submit buttons are
outside the form scope, so add the placeholder
explicitly -->
<formParameter name="event" />
<!-- layout the fields in two columns -->
<tableLayout>
<contents>
<bc4j:rootAppModuleScope name="DeptEmpApp" >
<contents>
<bc4j:viewObjectScope name="CurrentDeptEmpsVO" >
<contents>
<bc4j:rowScope name="CreateEmp" >
<contents>
<!-- placing the region in automatic mode will cause the key
named child to be rendered, followed by each attribute
in the ViewObject using the attrStamp named child. -->
<bc4j:region automatic="true" >
<!-- the key identifying this region -->
<bc4j:key>
<bc4j:rowKey name="key" />
</bc4j:key>
<!-- the stamp for each attribute -->
<bc4j:attrStamp>
<!-- the attrScope is automatic -->
<bc4j:messageInput/>
</bc4j:attrStamp>
</bc4j:region>
</contents>
</bc4j:rowScope>
</contents>
</bc4j:viewObjectScope>
</contents>
</bc4j:rootAppModuleScope>
</contents>
</tableLayout>
</contents>
</form>
</contents>
<contentFooter>
<!-- place a row of buttons below the content -->
<pageButtonBar>
<contents>
<!-- the cancel button performs a transaction rollback -->
<button text="Cancel" ctrl:event="cancel" />
<!-- the create button submits the user-entered
form data -->
<submitButton text="Create" formName="emp"
ctrl:event="apply" />
</contents>
</pageButtonBar>
</contentFooter>
</pageLayout>
</content>
<handlers>
<event name="cancel" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- rollback the current transaction -->
<bc4j:rollback/>
<!-- forward to the summary page -->
<go name="viewEmps" />
</bc4j:findRootAppModule>
</event>
<event name="apply" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- establish the ViewObject scope -->
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- find the row identified by the RowDef "CreateEmp" above, then
set the region automatically, insert the row and commit the
changes. If no validation errors occur, navigate to the
"viewEmps" summary page -->
<bc4j:findRow name="CreateEmp" >
<!-- the Row Key can change during form submission
specifically when the Row is automatically created.
transfer current key value to page property
for "CreateEmp" RowDef above -->
<bc4j:setPageProperty name="key" >
<bc4j:stringKey/>
</bc4j:setPageProperty>
<bc4j:setRegion automatic="true" />
<bc4j:insertRow/>
<bc4j:commit/>
<ctrl:go name="viewEmps" />
</bc4j:findRow>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
</handlers>
</page>
<?xml version="1.0" encoding="UTF-8" ?>
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:ui="http://xmlns.oracle.com/uix/ui"
xmlns:bc4j="http://xmlns.oracle.com/uix/bc4j" >
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="EmpAppModule"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="CurrentDeptEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView" >
<!-- the current row is keyed by the key page property -->
<bc4j:rowDef name="CurrentEmp" >
<bc4j:propertyKey name="key" />
</bc4j:rowDef>
</bc4j:viewObjectDef>
</bc4j:rootAppModuleDef>
</bc4j:registryDef>
<content>
<pageLayout xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
title="Update Emp" >
<contents>
<!-- include the view source link -->
<link text="View Source" ctrl:event="viewSource" />
<!-- this will contain any validation errors after form
submission -->
<messageBox automatic="true" />
<form name="edit" >
<contents>
<!-- we cannot implicitly determine that events
will be triggered because submit buttons are
outside the form scope, so add the placeholder
explicitly -->
<formParameter name="event" />
<!-- layout the fields in two columns -->
<tableLayout>
<contents>
<bc4j:rootAppModuleScope name="EmpAppModule" >
<contents>
<bc4j:viewObjectScope name="CurrentDeptEmpsVO" >
<contents>
<bc4j:rowScope name="CurrentEmp" >
<contents>
<!-- placing the region in automatic mode will cause the key
named child to be rendered, followed by each attribute
in the ViewObject using the attrStamp named child. -->
<bc4j:region automatic="true" >
<!-- the key identifying the current row -->
<bc4j:key>
<bc4j:rowKey name="key" />
</bc4j:key>
<!-- the stamp for each attribute -->
<bc4j:attrStamp>
<!-- the attrScope is automatic -->
<bc4j:messageInput/>
</bc4j:attrStamp>
</bc4j:region>
</contents>
</bc4j:rowScope>
</contents>
</bc4j:viewObjectScope>
</contents>
</bc4j:rootAppModuleScope>
</contents>
</tableLayout>
</contents>
</form>
</contents>
<contentFooter>
<!-- place a row of buttons below the content -->
<pageButtonBar>
<contents>
<!-- the cancel button performs a transaction rollback -->
<button text="Cancel" ctrl:event="cancel" />
<!-- the update button submits the user-entered
form data -->
<submitButton text="Update" formName="edit"
ctrl:event="apply" />
</contents>
</pageButtonBar>
</contentFooter>
</pageLayout>
</content>
<handlers>
<event name="cancel" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- rollback the current transaction -->
<bc4j:rollback/>
<!-- forward to the summary page -->
<go name="viewEmps" />
</bc4j:findRootAppModule>
</event>
<event name="apply" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- establish the ViewObject scope -->
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- find the row by key -->
<bc4j:findRow name="CurrentEmp" >
<!-- set the region of attributes automatically -->
<bc4j:setRegion automatic="true" />
<!-- commit the transaction -->
<bc4j:commit/>
<!-- forward to the summary page -->
<ctrl:go name="viewEmps" />
</bc4j:findRow>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
</handlers>
</page>
これまでに示した例では、<bc4j:table>
および<bc4j:region>
のautomaticプロパティを使用しました。このプロパティは、ViewObjectの属性のすべてを自動的に反復するため、デモ用のコードやクイック・アプリケーションには便利です。しかし同時に、制限もあります。列やフィールドを非表示にすることはできません。また、フィールドや列を並べ替えたり、レイアウトを変更することはできません。さらに、使用するのは<bc4j:messageInput>
および<bc4j:input>
だけなので、特定のコンポーネント・タイプの特殊なプロパティにはアクセスできません。これらの機能をすべて利用するには、自動モードをオフに切り替え、必要な列を明示的に指定する必要があります。
まず、viewEmps.uixの例から始めます。最初に、表の自動モードをオフにします。automaticをfalseに設定するか、またはこの部分をすべて除去します。自動モードがオフになっている場合、<bc4j:columnStamp>
は無視されるので、<contents>
セクションを使用する必要があります。
<!-- Our table; automatic mode is off -->
<bc4j:table name="emps"
width="80%" alternateText="No Employees found" >
<tableSelection>
<!-- single selection for each row in the table -->
<singleSelection>
<contents>
<submitButton text="Update"
ctrl:event="update" />
<submitButton text="Delete"
ctrl:event="delete" />
</contents>
</singleSelection>
</tableSelection>
<bc4j:keyStamp>
<bc4j:rowKey name="key" />
</bc4j:keyStamp>
<contents>
<!-- We'll add the columns explicitly -->
</contents>
</bc4j:table>
自動モードはオフにしましたが、表が空です。2つの列を追加します。
<!-- Our table; automatic mode is off -->
<bc4j:table name="emps"
width="80%" alternateText="No Employees found" >
.......
<bc4j:keyStamp>
<bc4j:rowKey name="key" />
</bc4j:keyStamp>
<contents>
<!-- We'll add the columns explicitly -->
<bc4j:column attrName="Job">
<columnHeader>Job</columnHeader>
<contents>
<bc4j:styledText styleClass="OraDataText"/>
</contents>
</bc4j:column>
<bc4j:column attrName="Ename"/>
<columnHeader>
<!-- the sortableHeader defaults its attrName
from the current attrScope -->
<bc4j:sortableHeader text="Employee Name"/>
</columnHeader>
<contents>
<bc4j:styledText/>
</contents>
</bc4j:column>
</contents>
</bc4j:table>
これでこの表の2列を追加しました。他の列はすべて、完全に非表示になります。ただし、拡張された柔軟性を示すためこのシナリオではさらに多くの処理を行っています。
また、ViewObject内のどの行にも関連付けられていない列や、コードで許可される組込みコンポーネント表よりも複雑な方法で導出された列を追加できます。
表または編集不可リージョンに対して行う操作は、これですべてです。編集可能リージョンについては、編集対象の属性だけがViewObjectに書き戻されるように、追加の操作を行う必要があります。modifyEmp.uixのapplyイベント・ハンドラを次に示します。
<event name="apply" >
<!-- finding the ApplicationModule causes it to be checked out from the
ApplicationPool. It is released after rendering completes. -->
<bc4j:findRootAppModule name="EmpAppModule" >
<!-- establish the ViewObject scope -->
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<!-- find the row by key -->
<bc4j:findRow name="CurrentEmp" >
<!-- set the region of attributes automatically -->
<bc4j:setRegion automatic="true" />
<!-- commit the transaction -->
<bc4j:commit/>
<!-- forward to the summary page -->
<ctrl:go name="viewEmps" />
</bc4j:findRow>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
<bc4j:setRegion automatic="true"/>
を使用するかわりに、これを削除し、使用される各属性に対して1つずつ<bc4j:setAttribute>
を使用します。
<event name="apply" >
<bc4j:findRootAppModule name="EmpAppModule" >
<bc4j:findViewObject name="CurrentDeptEmpsVO" >
<bc4j:findRow name="CurrentEmp" >
<!-- set two attributes explicitly -->
<bc4j:setAttribute name="Job" />
<bc4j:setAttribute name="Ename" />
<bc4j:commit/>
<ctrl:go name="viewEmps" />
</bc4j:findRow>
</bc4j:findViewObject>
</bc4j:findRootAppModule>
</event>
アプリケーションが複雑になれば、必然的に、すべてを宣言によって実現することは不可能になります。Javaコードを記述する必要がある場合、BC4J UIXでは、標準のUIX DataProviderおよびEventHandlerからすべてのBC4Jオブジェクトにアクセスできます。この例では、EMPデータベースからカスタムDataObjectList
を作成する方法を順に説明します。
最初に、DataObjectList
そのものを記述します。これは非常に単純で、ほとんど機能を持たないクラスになります。このクラスは、厳密にRowIteratorの現行範囲でのみ動作し(ViewObjectsはRowIteratorのサブクラスであることに注意)、属性名をDataObject
キーに直接マップします。
import oracle.cabo.ui.RenderingContext;
import oracle.cabo.ui.data.DataObject;
import oracle.cabo.ui.data.DataObjectList;
import oracle.jbo.NoDefException;
import oracle.jbo.Row;
import oracle.jbo.RowIterator;
public class JBODataObjectList implements DataObjectList
{
/**
* Create a data object list based on the current range of
* this row iterator.
*/
public JBODataObjectList(RowIterator iterator)
{
this(iterator, Integer.MAX_VALUE);
}
/**
* Create a data object list based on the current range of
* this row iterator, with a restricted size.
*/
public JBODataObjectList(RowIterator iterator, int maxSize)
{
_iterator = iterator;
_size = Math.min(_iterator.getRowCountInRange(), maxSize);
}
/**
* Return the size of the list.
*/
public int getLength()
{
return _size;
}
/**
* Get an item from the list.
*/
public DataObject getItem(int index)
{
// Make sure we're in range
if ((index < 0) || (index >= _size))
throw new IndexOutOfBoundsException();
Row row = _iterator.getRowAtRangeIndex(index);
if (row == null)
return null;
return new DO(row);
}
// An inner class that handles a single row
static private final class DO implements DataObject
{
public DO(Row row)
{
_row = row;
}
public Object selectValue(RenderingContext context, Object key)
{
// Make sure that the key is non-null
if (key != null)
{
try
{
// Treat the key as an attribute name
return _row.getAttribute(key.toString());
}
catch (NoDefException nde)
{
// Do nothing. It's legal to pass an invald key to DataObjects.
}
}
return null;
}
private final Row _row;
}
private final RowIterator _iterator;
private final int _size;
}
確かに簡単なコードですが、コンストラクタに渡すViewObjectはどのようにして取得するのでしょうか。UIXでは、このような場合に特に役立つ2つのクラスが用意されています。
oracle.cabo.data.jbo.ui.bind.UIBindingUtils
: スコープ内のBC4JオブジェクトをRenderingContextから取得します。
oracle.cabo.data.jbo.servlet.bind.ServletBindingUtils
: スコープ内のBC4JオブジェクトをBajaContextから取得します。
重要なのは、これらのクラスが取得できるのはスコープ内のBC4Jオブジェクトのみということです。このため、ルートApplicationModuleを取得する必要がある場合は、<bc4j:rootAppModuleScope>
内部から取得してください。
<bc4j:rootAppModuleScope name="EmpAppModule"
xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui">
<contents>
<dataScope>
<provider>
<!-- YourClass.getSomeData will be able to get at the application
module -->
<data name="fromJava">
<method class="YourClass" method="getSomeData">
</data>
</provider>
<contents>
...
</contents>
</dataScope>
</contents>
</bc4j:rootAppModuleScope>
次に、現在のビュー・オブジェクトにアクセスしてDataObjectList
に変換するデータ・プロバイダを記述します。
import java.util.HashMap;
import oracle.jbo.ViewObject;
import oracle.cabo.data.jbo.ui.bind.UIBindingUtils;
import oracle.cabo.ui.RenderingContext;
public class JavaHook
{
static public Object getList(
RenderingContext context,
String ns,
String name)
{
ViewObject vo = UIBindingUtils.getViewObject(context);
if (vo == null)
return null;
HashMap values = new HashMap();
values.put("list", new JBODataObjectList(vo));
return values;
}
}
UIBindingUtils.getViewObject()
は、現在スコープ内にあるViewObjectを返します。このViewObjectが見つかったら、それをDataObjectList
に変換し、listキーの下のHashMapに挿入します。
最後に、uiXMLページ内のコードについてまとめます。
<?xml version="1.0" encoding="UTF-8" ?>
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ui="http://xmlns.oracle.com/uix/ui"
xmlns:bc4j="http://xmlns.oracle.com/uix/bc4j" >
<bc4j:registryDef>
<bc4j:rootAppModuleDef name="EmpAppModule"
defFullName="oracle.cabo.data.jbo.demo.employee.EmployeeModule"
configName="EmployeeModuleLocal"
releaseMode="stateful" >
<bc4j:viewObjectDef name="AllEmpsVO"
defFullName="oracle.cabo.data.jbo.demo.employee.EmpView"
rangeSize="10" />
</bc4j:rootAppModuleDef>
</bc4j:registryDef>
<content>
<bc4j:rootAppModuleScope name="EmpAppModule"
xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui">
<contents>
<form name="emp">
<contents>
<!-- Move inside of the ViewObject scope -->
<bc4j:viewObjectScope name="EmpView">
<contents>
<dataScope>
<provider>
<!-- Attach the getList DataProvider -->
<data name="fromJava">
<method class="JavaHook" method="getList"/>
</data>
</provider>
<contents>
<choice name="foo">
<!-- Use the "list" to derive a repeating series of
elements -->
<contents data:childData="list@fromJava">
<option data:text="Ename" data:value="Empno"/>
</contents>
</choice>
</contents>
</dataScope>
</contents>
</bc4j:viewObjectScope>
</contents>
</form>
</contents>
</bc4j:rootAppModuleScope>
</content>
</page>
このページのほとんどは、他のページで使用した内容と同じです。異なる点は、<method>
要素を使用して<dataScope>
を追加している点と、<choice>
の<contents>
の内部にdata:childData
を追加している点のみです。data:childData
構文の知識が不足しているユーザーは、「uiXMLページの動的構造」のトピックを参照してください。
イベント・ハンドラ内でも同様の手法を使用できますが、BC4Jオブジェクトを取得する場合、UIBindingUtils
ではなくServletBindingUtils
を使用します。
Javaベースのアクセスに関してこれまで示した手法を使用すると、ViewObject内の各行につき1回ずつUIコンテンツを繰り返すことができますが、これよりもっと簡単な方法があります。<bc4j:viewObjectScope>
要素では、このパターンを自動化する子<bc4j:rowStamp>
をサポートしています。UIXでは、ビュー・オブジェクトの現在の範囲内の各行について1回ずつ<bc4j:rowStamp>
のコンテンツが自動的に繰り返されます。これは、<bc4j:choice>
または<bc4j:list>
の子をバインドする場合に特に便利です。選択する子のセットは明示的にリストできます。
<bc4j:viewObjectScope name="EmpView">
<contents>
<bc4j:choice attrName="Job">
<contents>
<option text="Clerk" value="CLERK"/>
<option text="Manager" value="MANAGER"/>
<!-- etc. -->
</contents>
</bc4j:choice>
</contents>
</bc4j:viewObjectScope>
ただし、このようなリストはデータベース表から導出する方が便利なことが多く、<bc4j:rowStamp>
を使用するとこれはさらに容易になります。
<bc4j:viewObjectScope name="EmpView">
<contents>
<bc4j:choice attrName="Job">
<contents>
<bc4j:viewObjectScope name="JobListView">
<bc4j:rowStamp>
<option>
<boundAttribute name="text">
<bc4j:attrValue name="JobName"/>
</boundAttribute>
<boundAttribute name="value">
<bc4j:attrValue name="JobId"/>
</boundAttribute>
</option>
</bc4j:rowStamp>
</bc4j:viewObjectScope>
</contents>
</bc4j:choice>
</contents>
</bc4j:viewObjectScope>
各<option>
をすべてリストするのではなく、可能なジョブすべてのリストが含まれた新しいビュー・オブジェクトを使用し、JobNameおよびJobId属性から<option>
のtextおよびvalueを取り除きます。
この例は、<bc4j:option>
要素を使用するとさらにすっきりします。
<bc4j:viewObjectScope name="EmpView">
<contents>
<bc4j:choice attrName="Job">
<contents>
<bc4j:viewObjectScope name="JobListView">
<bc4j:rowStamp>
<bc4j:option attrName="JobId" textAttrName="JobName"/>
</bc4j:rowStamp>
</bc4j:viewObjectScope>
</contents>
</bc4j:choice>
</contents>
</bc4j:viewObjectScope>
BC4J UIXテクノロジ統合により、開発者は、実際に存在するデータソースであるOracle9i データベースとBC4Jを介して対話的なuiXMLページを、短時間で簡単に定義することが可能になります。