Oracle® Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11g リリース2(11.1.2.3.0) B69399-02 |
|
![]() 前 |
![]() 次 |
この章では、ADF Business Componentsデータ・モデル・プロジェクトのADFビュー・オブジェクトを設計および利用するときに使用できる高度な手法について説明します。
この章の内容は次のとおりです。
ここでは、これまでの章で解説されていないビュー・オブジェクトの興味深い概念と機能について説明します。
注意: この章で説明する例を試すには、2.4.5項「AdvancedViewObjectExamplesアプリケーション・ワークスペースのスタンドアロン・アプリケーション」の説明に従ってFusion Order Demoアプリケーションの |
ビュー・オブジェクトのデフォルトの最大フェッチ・サイズは-1で、フェッチできる行数に制限がないことを示します。デフォルトでは、行は必要に応じてフェッチされるので、-1という値がビュー・オブジェクトがすべての行をフェッチする必要があることを意味しているわけではないことに留意してください。問合せ結果のすべての行を反復処理しようとした場合、すべての行が取得されることを意味するのみです。
ただし、ビュー・オブジェクトが取得する行の最大数に上限を設けることが必要になる場合があります。次の設定を使用できます。
adf-config.xml
ファイルの概要エディタの「ビジネス・コンポーネント」ページで、「行フェッチ制限」プロパティを使用して、すべてのビュー・オブジェクト問合せのグローバルしきい値を構成できます。このファイルを見つけるには、「アプリケーション・リソース」ペインでDescriptors-ADF META-INFフォルダを展開します。
注意: 行フェッチ制限ではそのアプリケーションのすべての問合せ操作のグローバルしきい値が指定されるため(イテレータ結果セットの予測行カウントを決定するイテレータ・バインディング・プロパティRowCountThresholdを含む)、このプロパティを使用すると、その操作のデフォルト動作ではすべての行をフェッチできる場合に、個々の問合せ操作の設定を変更しなくて済みます。個々のビュー・オブジェクトにフェッチ制限を指定する場合、行フェッチ制限設定は無視されます。
ビュー・オブジェクトの概要エディタの「一般」ページの「チューニング」セクションで「行番号までのみ」フィールドを選択し、「最大フェッチ・サイズ」を使用して、特定のビュー・オブジェクト問合せのローカルしきい値を構成できます。
ヒント: 「行フェッチ制限」を使用して問合せ操作のグローバルしきい値を設定する際、特定のビュー・オブジェクト問合せで使用可能なすべての行が戻されるようにする場合は、該当するビュー・オブジェクトの「行番号までのみ」フィールドを非常に大きい数値にして「最大フェッチ・サイズ」を設定できます。 |
たとえば、ORDER BY
句を含む問合せを作成し、ページに上位N項目を表示するために最初のn行を返す場合は、ビュー・オブジェクトの概要エディタを使用して、「一般」ページの「チューニング」セクションにある「行番号までのみ」フィールドの値を指定できます。たとえば、最初の5行のみをフェッチするには、このフィールドに5を入力します。これは、ビュー・オブジェクトでsetMaxFetchSize()
メソッドをコールして最大フェッチ・サイズを5
に設定するのと同じです。ビュー・オブジェクトは、最大フェッチ・サイズに達すると行のフェッチを停止します。通常は、この手法と組み合せて、概要エディタの「一般」ページの「チューニング」セクションで、FIRST_ROWSの「問合せオプティマイザ・ヒント」
も指定します。これにより、すべての行の取得の最適化を試みるのではなく、できるかぎり早く最初の何行かを取得するよう、データベースにヒントが提供されます。
1つのアプリケーション・モジュールで、エンティティ・ベースのビュー・オブジェクトの複数インスタンスが、同じエンティティ・オブジェクトに基づいているときは、1つのインスタンスで作成された新しい行を、他のインスタンスの行セットに(再問合せを行うことなく)自動的に追加して、ユーザー・インタフェースの整合性を保つか、または保留中のトランザクションの異なるアプリケーション・ページに新しい行を一貫して反映することができます。顧客の注文書が表示されるFusion Order DemoアプリケーションのorderSummary.jspx
ページについて考えます。顧客が新しいオーダーを作成する場合、このタスクは別のビュー・オブジェクトを介して実行され、カスタム・アプリケーション・モジュールのメソッドによって処理されます。このビュー・オブジェクトの新規行の整合機能を使用すると、新しく作成されたオーダーが、データベースに再度問い合せることなく、orderSummary.jspx
ページにある顧客の未処理オーダーのリストに自動的に表示されます。
歴史的な理由から、この機能はビュー・リンクの一貫性機能と呼ばれています。これは、Oracle Application Development Framework(Oracle ADF)の以前のリリースでは、アソシエーションに基づくビュー・リンクでの詳細ビュー・オブジェクト・インスタンスのサポートは、他の関連する行セットに対する新規行の追加にかぎられていたためです。現在では、このビュー・リンク一貫性機能は、ビュー・リンクに関連性があるかどうかには関係なく、この機能が有効になっているすべてのビュー・オブジェクトで機能します。
同じOrders
エンティティ・オブジェクトに基づく2つのエンティティ・ベースのビュー・オブジェクトOrdersViewSummary
とOrdersView
について考えます。一方のビュー・オブジェクト(OrdersView
など)の行セットで新しい行が作成され、行の主キー行が設定されると、同じOrders
エンティティ・オブジェクトに基づくビュー・オブジェクト(OrdersViewSummary
など)の他の行セットは、新しい行が作成されたことを示すイベントを受け取ります。ビュー・リンク一貫性フラグが有効になっていると、新しい行のコピーの行セットへの挿入も行われます。
「ビジネス・コンポーネント構成の編集」ダイアログを使用し、jbo.viewlink.consistent
構成パラメータを使用して、ビュー・リンク一貫性機能のデフォルト設定を制御できます。図42-1を参照してください。
構成エディタを表示するには、アプリケーション・ナビゲータでアプリケーション・モジュールを右クリックして、「コンフィギュレーション」を選択します。続いて「コンフィギュレーションの管理」ダイアログで構成を選択し、「編集」をクリックします。「ビジネス・コンポーネント・コンフィギュレーションの編集」ダイアログで、「プロパティ」タブを選択します。このパラメータのデフォルト設定はDEFAULT
で、次のような意味があります。ビュー・オブジェクトが次の状態であるものとします。
単一のエンティティ・オブジェクトの慣用名、ビュー・リンク一貫性は有効
複数のエンティティ・オブジェクトの慣用名、かつ
すべてのセカンダリ・エンティティ・オブジェクトの慣用名が寄与する参照情報としてマークされている場合は、ビュー・リンク一貫性が有効
いずれかのセカンダリ・エンティティ・オブジェクトの慣用名が参照としてマークされていない場合は、ビュー・リンク一貫性が無効
この場合は、構成でjbo.viewlink.consistent
に値false
を設定することで、この機能をグローバルに無効にできます。逆に、jbo.viewlink.consistent
に値true
を設定するとこの機能をグローバルに有効にできますが、このように設定することはお薦めしません。有効に設定すると、現時点ではビュー・リンク一貫性機能がサポートされていない参照としてマークされていないセカンダリ・エンティティ・オブジェクトの慣用名を持つビュー・オブジェクトに対しても、強制的にビュー・リンク一貫性が設定されます。
この機能をプログラムで設定するには、任意のRowSet
でsetAssociationConsistent()
APIを使用します。ビュー・オブジェクトでこのメソッドを呼び出すと、デフォルトの行セットに適用されます。
ビュー・オブジェクトでビュー・リンク一貫性が有効になっている場合は、同じエンティティ・オブジェクトに基づく別のビュー・オブジェクトで作成された新しい行が、行セットに追加されます。デフォルトでは、新しい行は無条件に追加されます。ビュー・オブジェクトに、行の特定のサブセットのみを問い合せる設計時WHERE
句がある場合は、ビュー・オブジェクトにRowMatch
オブジェクトを適用して、同じフィルタ処理をメモリー内で実行できます。指定するRowMatch
オブジェクトのフィルタ式により、そのオブジェクトに表示しても意味のない新規行は追加されません。
たとえば、OrdersByStatus
ビュー・オブジェクトには、次のような設計時WHERE
句が含まれることがあります。
WHERE /* ... */ AND STATUS LIKE NVL(:StatusCode,'%')
そのカスタムJavaクラスでは、create()
メソッドが例42-1に示すようにオーバーライドされており、ビュー・リンク一貫性を強制的に有効にします。また、Status
属性が:StatusCode
名前付きバインド変数の値と一致する行(または:StatusCode
='%
'の場合はすべての行)に一致するフィルタ式を含むRowMatch
オブジェクトを適用します。ビュー・リンク一貫性メカニズムは、このRowMatch
フィルタを使用して、行セットに追加する候補の行を限定します。RowMatch
で適格とされた行は追加されます。そうでない行は追加されません。
例42-1 追加される新規行を制御するためのカスタムRowMatchの提供
// In OrdersByStatusImpl.java protected void create() { super.create(); setAssociationConsistent(true); setRowMatch(new RowMatch("Status = :StatusCode or :StatusCode = '%'")); }
RowMatch
オブジェクトの作成と使用の詳細は、42.5.4項「RowMatchによるメモリー内フィルタ処理の実行」を参照してください。サポートされているSQL演算子のリストは、表42-2を参照してください。サポートされているSQL関数のリストは、表42-3を参照してください。
注意:
|
ビュー・オブジェクトでsetWhereClause()
を呼び出して動的WHERE
句を設定すると、そのビュー・オブジェクトのビュー・リンク一貫性機能は無効になります。行セットに追加する新しい行を限定するために適切なカスタムRowMatch
オブジェクトを提供していた場合は、setWhereClause()
の後でsetAssociationConsistent(true)
を呼び出すことで、ビュー・リンク一貫性を再び有効にできます。
行セットでビュー・リンク一貫性が有効になっている場合、他の行セットで作成されたために追加される新しい行は、行セットの最後に追加されます。
行セットでビュー・リンク一貫性が有効になっている場合、executeQuery()
メソッドを呼び出すと、適格なポストされていない新規行は、問合せによってデータベースから取得された行が追加される前に、行セットの先頭に追加されます。
ビュー・オブジェクトは、次の2種類のスタイルのマスター/ディテール調整をサポートします。
データ・モデルのマスター/ディテール調整のためのビュー・リンク・インスタンス。42.1.3.1項「アクティブなマスター/ディテール調整による動的ディテール行セットの有効化」を参照してください。
ディテール行セットにオン・デマンドでプログラム的にアクセスするためのビュー・リンク・アクセッサ属性。42.1.3.2項「ビュー・リンク・アクセッサ属性を使用した固定ディテール行セットへのアクセス」を参照してください。
両方のスタイルを組み合せることができます。42.1.3.3項「アクセッサ属性が作成する内部ビュー・オブジェクトに基づく個別の行セット」を参照してください。
ビュー・リンク・インスタンスをアプリケーション・モジュールのデータ・モデルに追加するときは、2つの特定のビュー・オブジェクト・インスタンスを接続します。ビュー・リンク・インスタンスの使用は、2つの間にアクティブなマスター/ディテール調整が必要なことを示します。実行時には、データ・モデル内のビュー・リンク・インスタンスにより、このコーディネーションを有効にするイベント処理が容易になります。マスター・ビュー・オブジェクト・インスタンスで現在行が変更されるたびに、イベントが発生し、マスター・ビュー・オブジェクトの新しい現在行に対する新しいバインド・パラメータのセットでexecuteQuery()
が自動的に呼び出されることで、ディテール・ビュー・オブジェクトがリフレッシュされます。
このデータ・モデル・マスター/ディテールの重要な特徴は、マスターおよびディテール・ビュー・オブジェクト・インスタンスが、クライアントのユーザー・インタフェースがバインディングを確立できる固定のオブジェクトであることです。マスターで現在行が変化すると、新しいディテール・ビュー・オブジェクト・インスタンスが生成されるのではなく、既存のディテール・ビュー・オブジェクト・インスタンスが、マスターの新しい現在行に関連する行のセットを含むように、デフォルトの行セットを更新します。さらに、ユーザー・インタフェースのバインディング・オブジェクトはイベントを受け取り、ディテール・ビュー・オブジェクトのリフレッシュされた行セットを含むように表示を更新できます。
データ・モデル階層のみが備えるもう1つの重要な機能は、1つのディテール・ビュー・オブジェクト・インスタンスが複数のマスター・ビュー・オブジェクト・インスタンスを持つことができることです。たとえば、PaymentOptions
ビュー・オブジェクト・インスタンスは、Customers
ビュー・オブジェクト・インスタンスとOrders
ビュー・オブジェクト・インスタンスの両方のディテールになることができます。Customers
またはOrders
のビュー・オブジェクト・インスタンスの現在行が変わるたびに、ディテールPaymentOptions
ビュー・オブジェクト・インスタンスのデフォルトの行セットは、現在の顧客および現在のオーダーの支払情報の行を含むようにリフレッシュされます。複数マスターのディテール・ビュー・オブジェクト・インスタンスを設定する方法の詳細は、42.1.6項「複数マスターのデータ・モデルの設定」を参照してください。
ビュー・リンクによりビュー・オブジェクト行に関係するディテール行セットにプログラムでアクセスする必要がある場合は、ビュー・リンク・アクセッサ属性を使用できます。ビュー・リンク・アクセッサ属性のファインダ名は、ビュー・リンクの概要エディタから指定します。「関連」ページの「アクセッサ」セクションにある「編集」アイコンをクリックし、「ビュー・リンク・プロパティの編集」ダイアログでビュー・リンク・アクセッサ属性の名前を編集します。
例42-2は、<Attr>
要素の_findername
値を定義するビュー・リンクのXMLを示しています。
例42-2 ビュー・リンク・アクセッサ属性名
<ViewLinkDefEnd Name="Orders" Cardinality="1" Owner="devguide.advanced.multiplemasters.Orders" Source="true"> <AttrArray Name="Attributes"> <Item Value="devguide.advanced.multiplemasters.Orders.PaymentOptionId"/> </AttrArray> <DesignTime> <Attr Name="_minCardinality" Value="1"/> <Attr Name="_isUpdateable" Value="true"/> <Attr Name="_finderName" Value="Orders"/> </DesignTime> </ViewLinkDefEnd>
アクセッサ属性の名前がAccessorAttrName
であるとすると、次のような汎用getAttribute()
APIを使用してディテール行セットにアクセスできます。
RowSet detail = (RowSet)currentRow.getAttribute("AccessorAttrName");
マスター・ビュー・オブジェクトに対するカスタム・ビュー行クラスを生成し、クライアント・ビュー行インタフェースでビュー・リンク・アクセッサ属性に対するgetterメソッドを公開してある場合は、次のような強く型付けされたコードを使用してディテール行セットにアクセスできます。
RowSet detail = (RowSet)currentRow.getAccessorAttrName();
データ・モデル・マスター/ディテールとは異なり、ビュー・リンク・アクセッサ属性のプログラムによるアクセスでは、アプリケーション・モジュールのデータ・モデルにディテール・ビュー・オブジェクト・インスタンスは必要ありません。ビュー・リンク・アクセッサ属性を呼び出すたびに、呼出しを行ったマスター行に関連するディテール行のセットを含むRowSet
が返されます。
ビュー・リンク・アクセッサ属性を使用すると、ディテール・データ行は不変になります。マスター行のビュー・リンク定義に関係する属性値が変わらないかぎり、ディテール・データ行は変化しません。マスターの現在行が変化しても、特定のマスター行にアタッチされているディテール行セットは影響を受けません。このため、ビュー・リンク・アクセッサ属性は、ディテール行の汎用的なプログラム・アクセスに便利なだけでなく、ツリー・コントロールのようなUIオブジェクトにも適しています。ツリー・コントロールでは、ツリー内の各マスター・ノードのデータは、個別のディテール行のセットを維持する必要があります。
データ・モデル・マスター/ディテールと、ビュー・リンク・アクセッサを使用するディテール行セットのプログラム・アクセスを組み合せるときは、これらが個別のメカニズムであることを理解することがいっそう重要になります。たとえば、次のことを行うものとします。
PersonsVO
およびOrdersVO
ビュー・オブジェクトを定義します。
これらの間のビュー・リンクを定義し、ビュー・リンク・アクセッサにPersonsToOrders
という名前を付けます。
これらのインスタンスを、ビュー・リンク・インスタンスによってアクティブに調整されるmaster
(PersonsVO
型)およびdetail
(OrdersVO
型)という名前の、アプリケーション・モジュールのデータ・モデルに追加します。
master
ビュー・オブジェクトのインスタンスで人物を検索すると、detail
ビュー・オブジェクトのインスタンスが更新され、予測どおりに対応する順序が表示されます。この時点で、現在のPersonsVO
行のPersonsToOrders
ビュー・リンク・アクセッサ属性にプログラムでアクセスするカスタム・メソッドを呼び出すと、OrdersVO
行のセットを含むRowSet
が取得されます。一般には、プログラムでアクセスされたこのRowSet
はデータ・モデルのdetail
ビュー・オブジェクト・インスタンスに由来するものと考えられますが、そうではありません。
ビュー・リンク・アクセッサによって返されるRowSet
は、常に内部的に作成されたビュー・オブジェクト・インスタンスからのものであり、ユーザーがデータ・モデルに追加したものではありません。この内部的なビュー・オブジェクト・インスタンスは、必要に応じて作成され、システムが定義する名前でルート・アプリケーション・モジュールに追加されます。
内部的に作成される個別のビュー・オブジェクト・インスタンスが使用される最大の理由は、データ・モデル内のビュー・オブジェクト・インスタンスに対して開発者が行う変更によって、インスタンスが影響を受けないようにするためです。たとえば、ビュー行が、ビュー・リンク・アクセッサのRowSet
に対してデータ・モデル内のディテール・ビュー・オブジェクトを使用する場合、開発者が次のようなことを動的に行うと、結果の行セットが誤って影響を受ける可能性があります。
新しい名前付きバインド・パラメータを含むWHERE
句を追加する。
このようなビュー・オブジェクト・インスタンスがビュー・リンク・アクセッサの結果に使用されると、動的に追加されたWHERE
句のバインド・パラメータ値はビュー・リンク・アクセッサのRowSet
に提供されていないため、予想外の結果またはエラーが発生します。このような値が提供されるのは、データ・モデル内のディテール・ビュー・オブジェクト・インスタンスのディテール行セットのみです。
データ・モデルのディテール・ビュー・オブジェクト・インスタンスにマスター・ビュー・オブジェクト・インスタンスを追加する。
このシナリオでは、アクセッサのセマンティクスが変更されます。アクセッサが現在のPersonsVO
行に対するOrdersVO
行を返すかわりに、たとえば現在ログインしている顧客によって作成された現在のPersonsVO
に対するOrdersVO
行のみを突然返し始めます。
ディテール・ビュー・オブジェクト・インスタンスまたはそれを含むアプリケーション・モジュール・インスタンスを削除する。
このシナリオでは、プログラムでアクセスされたディテールRowSet
のすべての行が無効になります。
さらに、Oracle ADFは、特定の操作について、データ・モデル・マスター/ディテールとビュー・リンク・アクセッサの行セットを区別する必要があります。たとえば、ディテール・ビュー・オブジェクトで新しい行を作成すると、フレームワークは、ビュー・リンクに含まれる属性に、マスターの対応する値を自動的に移入します。データ・モデル・マスター/ディテールの場合は、このような値を、データ・モデルの場合によっては複数のマスター・ビュー・オブジェクト・インスタンスの現在行から取得します。ビュー・リンク・アクセッサによって返されるRowSet
に新しい行を作成する場合は、アクセッサが呼び出されたマスター行からの値を移入します。
1ページずつデータを提示およびスクロールするために、適切なサイズの行範囲を管理するようにビュー・オブジェクトを構成できます。範囲機能を使用すると、クライアントは、行セット内の行のサブセットを簡単に表示および更新でき、一度にn行ずつ簡単に後のページにスクロールできます。1ページに表示するデータの行数を定義するには、setRangeSize()
を呼び出します。デフォルトの範囲サイズは1
行です。-1
という範囲サイズは、範囲が行セットのすべての行を含むことを示します。
注意: ADF Modelレイヤーの宣言的データ・バインディングを使用する場合、ページ定義のイテレータ・バインディングに |
1より大きい範囲サイズを設定するときは、イテレータ・モードを使用して行セットのページ移動動作を制御します。次の2つのイテレータ・モード・フラグを、setIterMode()
メソッドに渡すことができます。
RowIterator.ITER_MODE_LAST_PAGE_PARTIAL
このモードでは、最終ページに含まれる行数は、範囲サイズより少なくてもかまいません。たとえば、範囲サイズを10に設定し、行セットに23行が含まれる場合は、3ページ目には3行のみ含まれます。これは、Fusion Webアプリケーションに対して最善のスタイルです。
RowIterator.ITER_MODE_LAST_PAGE_FULL
このモードでは、最終ページにも1ページ分の行数が表示され、前のページの下部に表示されていた行が、最後のページの上部に表示される場合があります。たとえば、範囲サイズを10に設定し、行セットに23行が含まれる場合は、3ページ目にも10行が表示され、3ページ目の最初から7行には、2ページ目の最後の7行が表示されます。これは、Swingを使用するデスクトップ・フィデリティ・アプリケーションに最適なスタイルです。
一般的な規則として、パフォーマンスを最高にするため、非常に大きな問合せ結果をエンド・ユーザーがスクロールすることがないような方法でアプリケーションを作成することをお薦めします。この推奨事項を実現するには、実際に問合せを実行してユーザーが操作を継続できるようにする前に、ビュー・オブジェクトでgetEstimatedRowCount()
メソッドを呼び出して、ユーザーの問合せで返される行数を判定します。推定される行カウントが不当に大きい場合は、さらに検索基準を提供するようエンド・ユーザーに要求できます。
ただし、非常に大きな結果セット(通常、100行超)を処理する必要がある場合は、範囲ページ移動と呼ばれるビュー・オブジェクトのアクセス・モードを使用して、パフォーマンスを向上することができます。この機能を使用すると、大きなデータ・セットについてデフォルトのスクロール可能アクセス・モードより効率的な方法で、一度にある行範囲のページ単位で、前方および後方にデータを移動できます。
範囲ページ移動アクセス・モードは、通常、読取り専用行セットのページ移動に使用され、読取り専用ビュー・オブジェクトで使用されるのが普通です。ユーザーは範囲ページ移動アクセス・モードを使用し、大きな行セットをページ移動して対象行を検出できます。その後、その行のKey
を使用し、編集用に別のビュー・オブジェクトで選択されている行を検索できます。
ビュー・オブジェクトの場合の範囲ページ移動は、標準アクセス・モードと、範囲ページ移動と結果セット・スクロールの利点を組み合せてデータベース・アクセス回数を最小限に抑える標準アクセス・モードのバリエーションをサポートしています。次に、ビュー・オブジェクト範囲ページ移動機能のこれらのモードについて説明します。
RANGE_PAGING
: 標準アクセス・モードで、範囲サイズで指定された行数をフェッチします。このモードでは、データベースに再問合せを行わずにスクロールできる行数は、設定した範囲サイズで決まります。デフォルトでは1行をフェッチしますが、範囲サイズは、ユーザーが次の結果セットまでスクロールせずに表示できる行数と同じ数に設定するものと想定されています。エンド・ユーザーが範囲外の行にアクセスするたびに、アプリケーションはデータベースに再問合せを行います。このため、行セットを前後にスクロールすると、データベースに再問合せが行われます。このデータベース中心のページ移動方針の説明は、42.1.5.1項「上位N問合せのサポート方法」を参照してください。
RANGE_PAGING_INCR
: インクリメンタル・アクセス・モードを使用して、一度に表示できる行数をUI設計者がより柔軟に決定でき、データベース問合せ回数も最小限に抑えます。このモードでは、UIは結果セットをメモリー・キャッシュから逐次表示するため、スクロールは1回のデータベース問合せの範囲内でサポートします。エンド・ユーザーが1回の問合せでスクロールできる行数は、範囲サイズと、設定した範囲ページ移動キャッシュ・ファクタによって決まります。たとえば、範囲サイズを4、キャッシュ・ファクタを5に設定したとします。この場合、メモリーにキャッシュする最大行数は、4*5 = 20です。キャッシュの動作の詳細は、42.1.5.4項「範囲ページ移動を使用するときのビュー行のキャッシュ時の処理」を参照してください。
注意: さらに、ビュー・オブジェクトは、行セットの行の挿入と削除に対応するために、 |
Oracleデータベースは、問合せで順序付けされた最初のn行を効率よく返すための、上位N問合せと呼ばれる機能をサポートします。たとえば、次のような問合せがあるものとします。
SELECT EMPNO, ENAME,SAL FROM EMP ORDER BY SAL DESC
給与順に上位5人の従業員を取得する場合は、問合せを次のように記述できます。
SELECT * FROM ( SELECT X.*,ROWNUM AS RN FROM ( SELECT EMPNO,ENAME,SAL FROM EMP ORDER BY SAL DESC ) X ) WHERE RN <= 5
結果は次のようになります。
EMPNO ENAME SAL RN ---------- -------- ------ ---- 7839 KING 5000 1 7788 SCOTT 3000 2 7902 FORD 3000 3 7566 JONES 2975 4 7698 BLAKE 2850 5
この機能は、順番に先頭のn行を取得できるだけではありません。一番外側のWHERE
句の基準を調節することにより、問合せのソートされた順序で任意の範囲の行を効率よく取得できます。たとえば、問合せを次のように変更することで、6
行目から10
行目までを取得できます。
SELECT * FROM ( SELECT X.*,ROWNUM AS RN FROM ( SELECT EMPNO,ENAME,SAL FROM EMP ORDER BY SAL DESC ) X ) WHERE RN BETWEEN 6 AND 10
この考え方を一般化して、問合せ結果のP
ページ目を見たい場合は、1ページがR
行であるとすると、次のような問合せを作成します。
SELECT * FROM ( SELECT X.*,ROWNUM AS RN FROM ( SELECT EMPNO,ENAME,SAL FROM EMP ORDER BY SAL DESC ) X ) WHERE RN BETWEEN ((:P - 1) * :R) + 1 AND (:P) * :R
考えられる結果セットがより大きくなる場合、この手法を使用したページ移動の効率がさらに上がります。何百または何千もの行をデータベースからネットワーク経由で取得し、そのうちの10行のみをページに表示するかわりに、P
ページ目のR
行のみをデータベースから取得する賢明な問合せを作成できます。この方法を使用すると、ネットワーク経由で返す必要のある行数はほんのわずかで済みます。
このデータベース中心のページ移動方法をアプリケーションで実装するには、適切な問合せを自分で作成し、バインド変数:R
および:P
の適切な値を管理するコードを記述する必要があります。または、ビュー・オブジェクトの範囲ページ移動アクセス・モードを使用すると、このような機能が自動的に実装されます。
ビュー・オブジェクトの概要エディタの「チューニング」パネルを使用して、アクセス・モードを、標準の範囲ページ移動とインクリメンタル範囲ページ移動のいずれかに設定できます。「範囲ページング・インクリメンタル」を選択すると、「範囲ページング・キャッシュ・ファクタ」フィールドは編集のみ可能になります。図42-2に、ビュー・オブジェクトのアクセス・モードを示します。「範囲ページング」(標準モード)に設定されており、範囲サイズはデフォルトの1です。2つのアクセス・モードでの行セット・キャッシュの動作の詳細は、42.1.5.4項「範囲ページ移動を使用するときのビュー行のキャッシュ時の処理」を参照してください。
ビュー・オブジェクトの標準範囲ページ移動をプログラム的に有効にするには、最初にsetRangeSize()
をコールして1ページの行数を定義した後、必要に応じていずれかのモードで次のメソッドをコールします。
yourViewObject.setAccessMode(RowSet.RANGE_PAGING | RANGE_PAGING_INCR);
RANGE_PAGING_INCR
を設定した場合、次のメソッドもコールして、定義済の範囲サイズに対してキャッシュ・ファクタを設定する必要があります。
yourViewObject.setRangePagingCacheFactor(int f);
ビュー・オブジェクトのアクセス・モードをRANGE_PAGING
に設定すると、ビュー・オブジェクトは次のような問合せをデフォルトとして使用します。
SELECT EMPNO, ENAME, SAL FROM EMP ORDER BY SAL DESC
そして、これを自動的にラップし、上位N問合せを生成します。
最善のパフォーマンスのため、文ではBETWEEN
演算子のかわりに、より大と、より小の基準の組合せが使用されますが、論理的な結果は、先に示した上位Nのラッピングの問合せと同じになります。基本になる問合せは次のようなものです。
SELECT EMPNO, ENAME, SAL FROM EMP ORDER BY SAL DESC
これがラップされて、実際には次のような問合せが生成されます。
SELECT * FROM ( SELECT /*+ FIRST_ROWS */ IQ.*, ROWNUM AS Z_R_N FROM ( SELECT EMPNO, ENAME, SAL FROM EMP ORDER BY SAL DESC ) IQ WHERE ROWNUM < :0) WHERE Z_R_N > :1
2つのバインド変数は、次のようにバインドされます。
:1
は、現在のページの先頭行のインデックスです。
:0
は、現在のページの最終行にバインドされます。
ビュー・オブジェクトがRANGE_PAGING
アクセス・モードで動作するとき、ビュー行キャッシュのメモリー内に一度に保持するのは、現在の行範囲(つまりページ)のみです。つまり、一度に10ずつ結果をページ移動する場合、最初のページでは、1から10行がビュー行キャッシュに保持されます。2ページ目に移動すると、キャッシュには11から20行が格納されます。これは、大きな行セットの場合に、単に前後にスクロールできるようにするという理由で大量の行がキャッシュされないようにするためにも役立ちます。
ビュー・オブジェクトがRANGE_PAGING_INCR
アクセス・モードで動作するとき、特定の範囲サイズに対して、メモリーにキャッシュする行数は、キャッシュ・ファクタにより決まります。たとえば、範囲サイズを4、キャッシュ・ファクタを5に設定したとします。この場合、メモリーはコレクション中に最大4*5 = 20行を保持します。この例では、範囲が初めてリフレッシュされるとき、範囲ページ移動の問合せが行0~19(合計20行)を取得するためにバインドされていても、メモリーには4行のみが保持されます。範囲が4行目を過ぎてスクロールされると、より多くの行が現在の結果セットから読み込まれます。これが、20行すべてが問合せ結果から読み込まれるまで続きます。ユーザーのアクションにより次の行セットが取得された場合、新しい行番号バインド値で問合せが再実行されます。正確な行番号バインド値は、新しい範囲開始位置とキャッシュに保持できる行数で決まります。たとえば、20行すべてにデータが入っており、ユーザーが範囲開始位置を18(0ベース)に移動するよう指示していると想定します。これは、メモリーが行18と行19を保持でき、範囲を満たすためにはあと2行必要であることを意味します。行20および21に対して問合せが再実行されます。
ビュー・オブジェクトがRANGE_PAGING
アクセス・モードで動作しているときに、ページ番号nにスクロールするには、scrollToRangePage()
メソッドを呼び出し、パラメータ値としてnを渡します。
ビュー・オブジェクトがRANGE_PAGING
アクセス・モードで動作している場合、問合せ結果全体が生成する総ページ数の推定は、getEstimatedRangePageCount()
メソッドを使用して取得できます。
常にRANGE_PAGING
またはRANGE_PAGING_INCR
モードを使用すればよい、というわけではありません。それは、範囲ページ移動を使用すると、ビュー・オブジェクトの行を前後に移動したときに実行される問合せの総量が増加する可能性があるためです。次のような状況では、RANGE_PAGING
モードの使用を避ける必要があります。
行セットのすべての行をただちに読み取ろうと考えている場合(たとえば、ドロップダウン・リストに移入するため)。
この場合は、範囲サイズを-1
に設定し、すべての行を含む単一のページのみが存在するため、範囲ページ移動のメリットはなくなります。
小さいサイズの行セットで前後にページ移動する必要がある場合。
行数が100以下で、RANGE_PAGING
モードを使用して10行ずつページ移動する場合、前後の新しいページに移動するために問合せが実行されます。デフォルトのスクロール可能モードでは、読み取ったビュー・オブジェクト行はキャッシュされ、前のページから後方にページ移動しても、すでに表示された行を表示するために再び問合せが実行されることはありません。別の方法として、RANGE_PAGING_INCR
モードを使用して、指定した行セット・キャッシュ・ファクタに基づいてメモリー内の結果をスクロールできるようにすることもできます。
非常に大きい(または大きさを予測できない)行セットの場合、問合せ(各問合せでは、データベースから最大でRangeSize
の数だけ行が戻されます)の実行回数が増える可能性がある場合のトレードオフは、表示済の行をすべてキャッシュすることに比べれば効率的です。結果のリストの任意のページにジャンプできるようにする場合は、このことが特に顕著です。デフォルトのスクロール可能モードでこれを行うには、現在のページとジャンプ先のページの間にあるすべての行をフェッチしてキャッシュする必要があります。RANGE_PAGING
モードでは、そのページの行をデータベースに要求するのみです。その後、すでに表示したことのあるページにジャンプして戻る場合、RANGE_PAGING
モードでは現在のページの行しかメモリーに保持されないため、以前に取得した行の問合せが再び実行されます。このインクリメンタル範囲ページ移動アクセス・モード、RANGE_PAGING_INCR
では、標準の範囲ページ移動とスクロール可能アクセス・モードの両方の側面が組み合されています。アプリケーションはより多くの行をメモリーにキャッシュでき、ユーザーは再問合せする必要もなく、それらの行の任意の組合せにジャンプできます。
役に立つ場合には、同じディテール・ビュー・オブジェクトのインスタンスに対して複数のマスター・ビュー・オブジェクトのインスタンスが存在するように、データ・モデルを設定できます。Customers
、Orders
、およびPaymentOptions
という名前のビュー・オブジェクトと、次のように定義されたビュー・リンクについて考えます。
Customers
とPaymentOptions
Orders
とPaymentOptions
注意: この項の例は、Fusion Order Demoアプリケーションを基にしていません。この章の冒頭でダウンロード可能と記載したように、 |
図42-3は、Customers
とOrders
の両方のビュー・オブジェクト・インスタンスが、同じPaymentOptions
ビュー・オブジェクト・インスタンスのマスターになるように構成した場合の、データ・モデル・パネルの表示を示しています。
図42-3に示すようにデータ・モデルを設定するには、アプリケーション・モジュールの概要エディタを開き、「データ・モデル」ページの「データ・モデル・コンポーネント」セクションで次の手順を実行します。
Customers
ビュー・オブジェクトのインスタンスをデータ・モデルに追加します。
名前はCustomers
とします。
Orders
ビュー・オブジェクトのインスタンスをデータ・モデルに追加します。
名前はOrders
とします。
「データ・モデル」リストで、Customers
ビュー・オブジェクト・インスタンスを選択します。
選択可能なビュー・オブジェクト・リストで、Customers
ビュー・オブジェクトの下にインデントされているPaymentOptions
ビュー・オブジェクトを選択し、「新規インスタンス名」フィールドにビュー・オブジェクト・インスタンスの名前PaymentOptions
を入力します。次に、>をクリックして、既存のCustomers
ビュー・オブジェクト・インスタンスのディテールとしてデータ・モデルに移動します。
「データ・モデル」リストで、Orders
ビュー・オブジェクト・インスタンスを選択します。
選択可能なビュー・オブジェクト・リストで、Orders
ビュー・オブジェクトの下にインデントされているPaymentOptions
ビュー・オブジェクトを選択し、「新規インスタンス名」フィールドにビュー・オブジェクト・インスタンスの名前PaymentOptions
を入力します。次に、>をクリックして、既存のOrders
ビュー・オブジェクト・インスタンスのディテールとしてデータ・モデルに移動します。
次の警告が表示されます: PaymentOptionsという名前のビュー・オブジェクトのインスタンスはデータ・モデルですでに使用されています。同じインスタンスを使用しますか。
「はい」をクリックして、PaymentOptions
ビュー・オブジェクト・インスタンスを、Orders
ビュー・オブジェクト・インスタンスのディテールにもすることを確認します。
複数のエンティティ・オブジェクトの慣用名に基づくビュー・オブジェクトでは、部分的に移入されたキーを指定してビュー行を検索できます。部分キーは、複数属性のKey
オブジェクトで、一部の属性がnull
に設定されたものです。ただし、findByKey()
を実行するために使用できる部分キーの種類には、厳密な規則があります。
ビュー・オブジェクトがn個のエンティティ・オブジェクトの慣用名(n > 1)に基づいている場合、ビュー行のキーは、デフォルトで、関連するすべてのエンティティ・オブジェクトの慣用名のすべての主キー属性で構成されます。ビュー行のキーに参加する必要があるのは最初のエンティティ・オブジェクトの主キーのみですが、デフォルトですべての主キーが使用されます。
一部のセカンダリ・エンティティ・オブジェクトの慣用名のキー属性をビュー行レベルのキー属性として残す場合は、そのエンティティ・オブジェクトの主キーを構成するすべての属性をビュー行のキーの一部として残しておく必要があります。n個のエンティティ・オブジェクトの慣用名のうちのm
個について(m <= n)、ビュー行の1つ以上のキー属性を残した場合、findByKey()
を使用し、m個のエンティティ・オブジェクトの慣用名の任意のサブセットに基づいて行を検索できます。Key
オブジェクトで値を指定するそれぞれのエンティティ・オブジェクトの慣用名について、そのエンティティの主キーのすべての属性にNULLではない値を指定する必要があります。
ビュー・オブジェクトが1つ以上のエンティティ・オブジェクトの慣用名に基づいている場合、そのfindByKey()
メソッドは、ビュー行キーでの属性がNULLではない最初のエンティティ・オブジェクトの慣用名に対応するエンティティ定義のfindByPrimaryKey()
メソッドに委譲することで行を検索するため、この規則に従う必要があります。エンティティ定義のfindByPrimaryKey()
メソッドでキャッシュ内のエンティティ行を検索するには、特定のエンティティ・オブジェクトに対するすべてのキー属性がNULL以外である必要があります。
具体的な例としては、OrderInfoVO
ビュー・オブジェクトで、OrderEO
エンティティ・オブジェクトがそのプライマリ・エンティティ・オブジェクト慣用名で、AddressEO
エンティティがセカンダリ参照エンティティ・オブジェクト慣用名である場合を考えてみてください。さらに、次の両方のビュー行属性のキー属性プロパティを、true
に設定したままにするものとします。
OrderId
: OrderEO
エンティティの主キー
AddressId
: AddressEO
エンティティの主キー
したがって、ビュー行のキーは、OrderId
とAddressId
の組合せになります。findByKey()
を呼び出すときは、次のような内容のKey
オブジェクトを渡すことができます。
基礎となるOrderEO
エンティティの完全に指定されたキー
Key k = new Key(new Object[]{new Number(200), null});
基礎となるAddressEO
エンティティの完全に指定されたキー
Key k = new Key(new Object[]{null, new Number(118)});
両方のエンティティの完全に指定されたキー
Key k = new Key(new Object[]{new Number(200), new Number(118)});
有効な部分キーを指定すると、findByKey()
メソッドは、キー・オブジェクトで指定されていないエンティティ・オブジェクト慣用名の属性をワイルドカードとして扱い、結果として複数の行を返すことができます。
プログラム・ビュー・オブジェクト問合せが主キーに関して一時属性に依存する場合、ユーザーは、ビュー・オブジェクトのキャッシュ行の外にUIをスクロールすると、NULLポインタ例外を受け取る可能性があります。この場合、ビュー・オブジェクト問合せがデータベース表から生成されないため、ビュー・オブジェクト実装は、ViewObjectImpl
クラスのretrieveByKey()
メソッドをオーバーライドし、行を返す(または、行がない場合には空の配列を返す)必要があります。
このメソッドをオーバーライドすると、ADF Business ComponentsでfindByKey()
を実行し、キャッシュからリクエストされた行を最初に検索できます。(主キーが一時属性であるために)これに失敗した場合、ADF Business Componentsは、retrieveByKey()
オーバーライドを実行し、入力されるキーと一致する行を取得するように定義した操作を実行します。このメソッドのデフォルトの実装は、データベース問合せを発行し、データベースから行を取得しようとします。
protected Row[] retrieveByKey(ViewRowSetImpl rs, Key key, int maxNumOfRows, boolean skipWhere)
このメソッドには次の引数があります。
maxNumOfRows
は、findByKey()
の呼出しに渡したmaxNumOfRows
です。これは、1~nまたは-1です。nは、渡されたキーと一致するキーを持つ行をn行検索していることを意味します。-1はすべての行が一致することを意味します。キーが部分キーでビュー・オブジェクトが複数のエンティティ・オブジェクトに基づいている場合、ビュー・オブジェクトの複数の行がキーと一致する可能性があります。
skipWhere
は、findByKey()
が基本ビュー・オブジェクトとして同じWHERE
句を適用する必要があるかどうかを制御します。基本ビュー・オブジェクトにWHERE
句DEPTNO = 10
があり、skipWhere
がfalse
の場合、バッキング・ストアから行を検索しているときに同じWHERE
句を適用することを想定されます。skipWhere
がtrue
の場合、基本ビュー・オブジェクトからのWHERE
句は関係ありません。
addDynamicAttribute()
メソッドを使用すると、実行時に1つまたは複数の動的属性をビュー・オブジェクトに追加できます。動的属性は、値として任意のシリアライズ可能オブジェクトを保持できます。通常は、グローバルで汎用的な方法でフレームワークに追加する機能を実装するために、行ごとに追加の一時的な状態を格納する必要がある、汎用のフレームワーク拡張コードを作成するときに、動的属性の使用を検討します。
通常はビュー・オブジェクトのデフォルトの行セットを使用しますが、ViewObject
インタフェースのcreateRowSet()
メソッドを呼び出して、同じビュー・オブジェクトの問合せに基づく行セットである、二次的な名前付きの行セットを作成できます。こうすることに意味のある状況の1つは、ビュー・オブジェクトのSQL問合せに名前付きバインド変数が含まれる場合です。各RowSet
オブジェクトはバインド変数値の独自のコピーを格納するので、単一のビュー・オブジェクトを使用して、異なる組合せのバインド変数値に基づく複数の行セットを生成および処理できます。作成した名前付き行セットは、findRowSet()
メソッドを使用して検索できます。セカンダリ行セットの使用が終了したら、closeRowSet()
メソッドを呼び出します。
RowSet
に対し、通常はデフォルトの行セット・イテレータを使用しますが、RowSet
インタフェースのcreateRowSetIterator()
メソッドを呼び出して、二次的な名前付きの行セット・イテレータを作成できます。作成した名前付き行セット・イテレータは、findRowSetIterator()
メソッドを使用して検索できます。セカンダリ行セット・イテレータの使用が終了したら、closeRowSetIterator()
メソッドを呼び出します。
パフォーマンスに関するヒント: 結果セットに対してプログラム的に反復を実行する必要があるとき、デフォルトの行セット・イテレータの現在行が妨害されることを避けるために、セカンダリ・イテレータを作成します。たとえば、ADF Modelの宣言的データ・バインディング・レイヤーを通して、アプリケーションのユーザー・インタフェース・ページは、アプリケーション・モジュールのデータ・モデル内にあるビュー・オブジェクトのデフォルト行セットのデフォルト行セット・イテレータと連携します。このシナリオでは、ビュー・オブジェクトのデフォルト行セットに対して反復処理を行うビジネス・ロジックを担当する行セットのセカンダリ・イテレータを作成しない場合、結果的にユーザー・インタフェース・レイヤーによって使用されるデフォルトの行セット・イテレータの現在行が変更されます。 |
デフォルトでは、ビュー・リンク・アクセッサの行セットを取得するたびに、ビュー・オブジェクトによって新しいRowSet
オブジェクトが作成され、ユーザーが行を操作できるようにします。これは、そのたびに問合せを再実行して結果を生成するという意味ではなく、RowSet
オブジェクトの新しいインスタンスを作成してデフォルトのイテレータを先頭行の前のスロットにリセットするだけです。行セットをデータベースの行で強制的に更新するには、executeQuery()
メソッドを呼び出します。
新しいディテール行セット作成に関連する若干のオーバーヘッドがアプリケーションで発生しないようにする場合は、ビュー・リンク・アクセッサ行セットのキャッシュを有効にできます。たとえば、ビュー・アクセッサ行セットは、マスター行ビュー・アクセッサ属性が変更されないかぎりは、安定した状態を維持し、ツリー・コントロールのようなUIコンポーネントの行セットを再作成する必要がありません。この場合、ツリー内の各マスター・ノードのデータは、個別のディテール行のセットを維持する必要があります。ビュー・リンク・アクセッサのディテール行セットにプログラムでアクセスすることもできます。この場合、アプリケーションで同じビュー・リンク・アクセッサ属性に対して多数のコールを行うときに、ビュー・リンク・アクセッサ行セットのキャッシングを検討できます。マスター/ディテール関係の管理のスタイルは、データ・モデルでのビュー・リンク・インスタンス作成とは異なります(42.1.3項「ビュー・リンク・アクセッサとデータ・モデル・ビュー・リンク・インスタンス」を参照)。
ビュー・リンク・アクセッサのソースであるビュー・オブジェクトについて、概要エディタを使用してビュー・リンク・アクセッサ行セットの保存を有効にできます。ビュー・オブジェクトの概要エディタの「一般」ページの「チューニング」セクションにある「ビュー・リンク・アクセッサ行セットの保存」を選択します。
かわりに、ビュー・オブジェクトでカスタムJavaクラスを有効にし、create()
メソッドをオーバーライドして、パラメータとしてtrue
を渡して、setViewLinkAccessorRetained()
メソッドを呼び出す行を、super.create()
の後に追加できます。これは、そのビュー・オブジェクトのすべてのビュー・リンク・アクセッサ属性に適用されます。
ビュー・オブジェクトに対してこの機能を有効にすると、ビュー・リンク・アクセッサの行セットが毎回再作成されないため、デフォルトの行セット・イテレータの現在行も維持されるという副作用があります。つまり、デフォルト行セット・イテレータの現在行をリセットして先頭行の前のスロットに戻すには、ビュー・リンク・アクセッサから取得した行セットに対して、reset()
メソッドを明示的に呼び出す必要があります。
ただし、アクセッサの保持を有効にすると、アクセッサ行セットの行を反復処理する前にいつでもreset()
を呼び出すようにしないと、微妙で検出が困難なエラーがアプリケーションで発生する可能性があることに注意してください。たとえば、ビュー・リンク・アクセッサの行セットの行を次のようにして反復処理し、合計を計算するものとします。
RowSet rs = (RowSet)row.getAttribute("OrdersShippedToPurchaser"); while (rs.hasNext()) { Row r = rs.next(); // Do something important with attributes in each row }
アクセッサ行セットを最初に処理するときには、このコードは正しく動作します。しかし、行セット(およびそのデフォルトの行セット・イテレータ)が保持されるため、2回目以降の行セットへのアクセスでは、現在行はすでに行セットの末尾にあり、rs.hasNext()
がfalse
になるためwhileループはスキップされます。この機能を有効にする場合は、アクセッサの反復コードは次のように記述する必要があります。
RowSet rs = (RowSet)row.getAttribute("OrdersShippedToPurchaser");
rs.reset(); // Reset default row set iterator to slot before first row!
while (rs.hasNext()) {
Row r = rs.next();
// Do something important with attributes in each row
}
ビュー・リンク一貫性が有効になっている場合は、アクセッサが保持されると、ポストされていない新しい行は行セットの最後に表示されます。これは、アクセッサが保持されない(デフォルト)場合と若干異なり、アクセッサが保持されないときは、ポストされていない新しい行は、アクセッサの行セットの先頭に表示されます。
ビュー・オブジェクトを使用すると、データの行の読取り、一時的データの行の作成と格納、およびエンド・ユーザーが基礎のビジネス・オブジェクトに対して行う挿入、更新、削除の自動的な調整を行うことができます。ビュー・オブジェクトを設計および使用する方法が、実行時のパフォーマンスに影響を与える場合があります。ここでは、可能なかぎり最高のパフォーマンスを得るためのビュー・オブジェクトの構成に関するガイダンスを提供します。
問合せのWHERE
句に実行のたびに変化する可能性のある値が含まれる場合は常に、名前付きバインド変数を使用する必要があります。ビュー・オブジェクトの概要エディタの「問合せ」ページで表示される「ビュー基準の作成」ダイアログを使用すると、そのようなタスクを簡単に実行できます。また、名前付きバインド変数を使用することで、悪質なエンド・ユーザーによるSQLインジェクション攻撃からアプリケーションを保護することもできます。バインド変数によるビュー基準の定義の詳細は、5.11.1項「名前付きビュー基準を宣言的に作成する方法」を参照してください。
バインド変数は、SQL文字列内のプレースホルダであり、SQL文字列自体のテキストを変更することなく、実行時に簡単に値を変更できます。問合せのテキストは実行ごとに変化しないので、データベースは同じ解析済の文を毎回効率よく再利用できます。文の再解析を避けることで、データベースでは問合せ最適化プランを絶えず再決定する必要が軽減され、この解析操作の間に使用される他の貴重なデータベース・リソースに対する複数のエンド・ユーザーによる競合を除去できます。これらの減少により、実行時のアプリケーションのパフォーマンスが向上します。名前付きバインド変数の使用方法の詳細は、5.10.1項「ビュー・オブジェクト定義にバインド変数を追加する方法」を参照してください。
パラメータを使用するWHERE
句の値に対してバインド変数を使用することは、その値をアプリケーションのエンド・ユーザーが提供する場合に特に重要です。例42-3に示す例について考えてみます。この例は、ユーザーが指定したパラメータ値を文に連結して構成された動的なWHERE
句を追加します。
例42-3 バインド変数のかわりに文字列の連結を使用することによる、SQLインジェクション攻撃に対する脆弱性
// EXAMPLE OF BAD PRACTICE, Do not follow this approach! String userSuppliedValue = ... ; yourViewObject.setWhereClause("BANK_ACCOUNT_ID = "+userSuppliedValue);
悪質な意図を持つユーザーは、アプリケーションの基礎にあるデータベース・スキーマの詳細を知ることができれば、慎重に構成された銀行口座番号を、次のようにフィールド値またはURLパラメータとして提供できます。
BANK_ACCOUNT_ID
例42-3のコードがこの値を動的に適用されるWHERE句に連結すると、データベースには次のような問合せ述語が示されます。
WHERE (BANK_ACCOUNT_ID = BANK_ACCOUNT_ID)
このWHERE
句では現在のユーザーの口座だけでなくすべての銀行口座が取得され、ハッカーは他人の口座の個人情報を見ることができます。悪質な目的で作成されたパラメータ値をSQL文に提供することでアプリケーションのWHERE
句を短絡するこのような手法のことを、SQLインジェクション攻撃と呼びます。このような場合は、かわりに例42-4に示すように名前付きバインド変数を使用することで、脆弱性を回避できます。
例42-4 文字列の連結にかわる名前付きバインド変数の使用
// Best practice using named bind variables String userSuppliedValue = ... ; yourViewObject.setWhereClause("BANK_ACCOUNT_ID = :BankAcccountId"); yourViewObject.defineNamedWhereClauseParam("BankAcccountId", null, null); yourViewObject.setNamedWhereClauseParam("BankAcccountId",userSuppliedValue);
この場合に悪質なユーザーが不正な値を提供すると、予想しないデータを取得するのではなく、アプリケーションが処理できるエラーを受け取ります。
一般に、SQLベースの検証のため、およびドロップダウン・リストに有効な選択肢のリストを表示するために使用されるビュー・オブジェクトは、読取り専用でかまいません。アプリケーションに必要な機能の種類を判断し、それに応じてビュー・オブジェクトを設計する必要があります。
ベスト・プラクティス: データを参照するために読取り専用ビュー・オブジェクトを作成する必要があるときは、エンティティ・ベースのビュー・オブジェクトを使用し、ビュー・オブジェクトの概要エディタの「エンティティ・オブジェクト」ページで「更新可能」オプションを選択解除することをお薦めします。この方法では、SQL問合せの生成を支援する設計時エディタが便利です。エキスパート・モード・ビュー・オブジェクトの作成に代わる方法では、SQL問合せを作成する必要があります。エキスパート・モード問合せは、エンティティ・オブジェクトを使用してUNION問合せやGROUP BY問合せを記述することができない事例には依然として有効です。 |
ビュー・オブジェクトは、基礎になっているエンティティ・オブジェクトに関連付けることも、関連付けないこともできます。ビュー・オブジェクトが基礎になっているエンティティ・オブジェクトと関連付けられていると、デフォルト動作で新しい行の作成、および問合せで得られた行の変更や削除がサポートされます。ただし、更新機能は、エンティティ・ベースのビュー・オブジェクトの概要エディタで「更新可能」の選択を解除して無効にできます(図42-4を参照)。
かわりに、「クエリーの編集」ダイアログの「エキスパート・モード」を使用し、読取り専用ビュー・オブジェクトを作成してSQL問合せを定義します。エディタによって問合せ作成タスクが簡素化されるため、複雑なSQL構文の構築に慣れていないBusiness Components開発者にとっては、エンティティ・オブジェクトに基づいて更新不能なビュー・オブジェクトを作成する方が、常に便利です。更新不能に設定したエンティティ・ベースのビュー・オブジェクトは、読取り専用のエキスパート・モード・ベースのビュー・オブジェクトと比べても次のように有利です。
ユーザー・インタフェースに必要とされる属性のみが含まれるように、実行時に選択リストを最適化できます。
エンティティ・オブジェクトを使用したローカル・キャッシュの作成による、大幅なパフォーマンスの劣化はありません。
ビュー・オブジェクト内のデータは、読取り操作ごとにデータベースに戻す必要はなく、むしろローカル・キャッシュの状態を表します。
一貫性を維持するローカル・キャッシュ内のデータは別のビュー・オブジェクトであり、更新不能なビュー・オブジェクトのベース・エンティティ・オブジェクトに対して更新を実行する必要があると定義されます。
このため、ビュー・オブジェクト行とエンティティ・オブジェクト行の間の調整には、実行時に若干のオーバーヘッド(概算では5%未満のオーバーヘッド)が生じますが、ビュー・オブジェクト定義を完全に宣言的に維持し、カスタマイズ可能なビュー・オブジェクトをメンテナンスする機能と比較検討してください。エキスパート・モード・ベースのビュー・オブジェクトはカスタマイズ不可ですが、エンティティ・オブジェクトでは表現できないUNIONおよびGROUP BY問合せの実行に使用できます。エキスパート・モード・ベースのビュー・オブジェクトは、ビュー・オブジェクト・ベースのKey Exists Validatorによって使用されるSQLベースの検証問合せでも便利です。
データが読取り専用でないとき、最善の(そして唯一の)選択肢はエンティティ・ベースのビュー・オブジェクトを作成することです。更新可能(デフォルト動作)なエンティティ・ベースのビュー・オブジェクトは、エンティティから導出される属性のデフォルト値の取得、同じトランザクション内の他のビュー・オブジェクトを介して関連エンティティ・オブジェクトに行われた保留中の変更の反映、およびエンティティ・ベースのビュー・オブジェクトを使用するために外部キー属性値が変更されたときに更新された参照情報の反映の唯一の方法です。
ビュー・オブジェクトをエンティティにマップするかどうかを決定した後は、次に問合せ自体に注目します。ビュー・オブジェクトの概要エディタの「問合せ」ページで、「SQL問合せの編集」アイコンをクリックして「問合せの編集」ダイアログを表示します。「問合せの編集」ダイアログの「問合せ」ページの「実行計画」ボタンをクリックして、データベースの問合せオプティマイザが使用する問合せ計画を見ることができます。計画が全表スキャンを行っている場合は、索引を追加するか、または概要エディタの「一般」ページの「チューニング」セクションにある「問合せオプティマイザ・ヒント」フィールドで値を指定して使用される問合せ計画を明示的に制御することを検討する必要があります。これにより、使用される問合せ計画を明示的に制御できます。これらの機能は、個別のビュー・オブジェクトSQL文について問合せ計画を評価するための便利なツールを開発者に提供します。ただし、これらのツールの使用は、本番環境のデータ量とエンド・ユーザー数においてパフォーマンスの悪い問合せを識別するための、アプリケーション全体のSQLのトレースにかわるものではありません。
OracleデータベースのSQLトレース機能を使用すると、アプリケーションが実行する全SQL文の完全なログが生成されます。すべてのバージョンのOracleデータベースで動作する方法としては、次のコマンドを発行します。
ALTER SESSION SET SQL_TRACE TRUE
特にOracle 10gでは、このコマンドを実行するために、DBAはALTER SESSION
権限を付与する必要があります。
このコマンドを実行すると、現在のデータベース・セッションのトレースが有効になり、ALTER SESSION SET SQL_TRACE FALSE
を入力するまで、または接続を閉じるまで、すべてのSQL文がサーバー側のトレース・ファイルに記録されます。このオプションの有効化を簡単にしてFusion Webアプリケーションをトレースするには、例42-5に示すように、アプリケーション・モジュール(またはカスタム・アプリケーション・モジュール・フレームワーク拡張クラス)のafterConnect()
メソッドをオーバーライドし、Javaシステム・プロパティが存在するかどうかに基づいて、条件付きでALTER SESSION
コマンドを実行してSQLトレースを有効にします。
例42-5 アプリケーション・モジュールでの条件付きでのSQLトレースの有効化
// In YourCustomApplicationModuleImpl.java protected void afterConnect() { super.afterConnect(); if (System.getProperty("enableTrace") != null) { getDBTransaction().executeCommand("ALTER SESSION SET SQL_TRACE TRUE"); } }
トレース・ファイルを生成した後は、データベースに付属するTKPROF
ユーティリティを使用して情報を書式設定することで、次のように実行された各問合せに関する情報を理解しやすくなります。
(再)解析された回数
実行された回数
アプリケーション・サーバーとデータベースの間で行われたラウンドトリップの回数
問合せの実行時間に関する様々な定量的測定値
これらの手法を使用することで、アプリケーションが実行する特定の問合せの時間を短縮するために必要な追加索引や、問合せ最適化計画を改善するために変更できる問合せを判断できます。TKPROF
ユーティリティでの作業の詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』のSQLトレースとTKPROFについてに関する項およびSQLトレース機能とTKPROFの使用方法に関する項を参照してください。
注意: Oracleデータベースが提供する |
ビュー・オブジェクトの概要エディタの「一般」ページの「チューニング」セクションでは、問合せのパフォーマンスに劇的な影響を与える可能性のある様々なオプションを設定できます。図42-5に、新しいビュー・オブジェクトで定義されているデフォルトのオプションを示します。
「データベースから取得」グループ・ボックスでは、ビュー・オブジェクトがデータベース・サーバーから行を取得する方法を制御します。フェッチ・モードのオプションは、「すべての行」、「行番号までのみ」、「最大で1行」および「行なし」です。ほとんどのビュー・オブジェクトはデフォルトの「すべての行」オプションを使用し、「必要に応じて」(デフォルト)または「同時」のいずれか選択されているオプションに従って取得されます。
注意: 「同時」オプションでは、ビュー・オブジェクト問合せで指定された行をフェッチするためのデータベース・ラウンドトリップを一切強制しません。「必要に応じて」および「同時」オプションは、「バッチ」(フェッチ・サイズとも呼ばれます)の値と連携して、ラウンドトリップ数を決定します。データベース・アクセスのパフォーマンスを最高に保つには、42.2.4.2項「一度に1行のフェッチが適切かどうかの検討」の説明に従ってフェッチ・サイズを変更することを検討してください。 |
「必要に応じて」オプションを指定すると、ビュー・オブジェクトでのexecuteQuery()
操作は最初に、表示する最初のページに必要な数の行のみを取得します。この行数は、ビュー・オブジェクトの範囲サイズに基づいて設定されます。「必要に応じて」を使用する場合、最初の範囲サイズで指定された行数を取得するために、必要な回数だけデータベース・ラウンドトリップが要求されます。「同時」を使用する場合、アプリケーションでは、「バッチ」(フェッチ・サイズ)の値と問合せによって識別された行数に基づいて、すべての行を取得するために必要な回数のラウンドトリップが実行されます。
WHERE
句が単一の行を取得すると予想されるビュー・オブジェクトの場合は、オプションを「最大で1行」に設定すると最良のパフォーマンスが得られます。このオプションを指定すると、ビュー・オブジェクトはユーザーがそれ以上の行を期待していないことを認識し、さらに行がある場合に通常行うテストをスキップします。最後に、新しい行を作成するためにのみビュー・オブジェクトを使用する場合は、オプションを「なし」に設定すると、問合せは実行されません。
フェッチ・サイズは、データベースへの各ラウンドトリップで返される行数を制御します。デフォルトでは、フレームワークは一度に1行のバッチで行をフェッチします。複数の行をフェッチしている場合は、この「バッチ」の値を設定することで効率がよくなります。
ただし、この値を大きくすると、クライアント側で必要なバッファが大きくなるため、独断で大きな値を設定しないでください。ユーザー・インタフェースで一度にn行の結果を表示する場合は、データベースへの1回のラウンドトリップで各ページの結果を取得できるよう、フェッチ・サイズを少なくともn+1に設定するのがよい方法です。
注意: 問合せで本当に1行のみフェッチするのでないかぎり、ビュー・オブジェクトの概要エディタの「一般」ページで「チューニング」セクションの「バッチ」フィールドをデフォルトのフェッチ・サイズである |
「問合せオプティマイザ・ヒント」フィールドでは、Oracle問合せオプティマイザに対するオプションのヒントを指定して、使用される実行計画に影響を与えることができます。図42-5に示すように、このヒントは、ビュー・オブジェクトの概要エディタの「チューニング」ページで設定することができます。
実行時には、指定したヒントが問合せのSELECT
キーワードの直後に追加され、特殊なコマンド構文/*+
YOUR_HINT
*/
でラップされます。一般的な2つのオプティマイザ・ヒントを次に示します。
FIRST_ROWS
: できるかぎり早く最初の行を取得することを示します。
ALL_ROWS
: できるかぎり早くすべての行を取得することを示します。
他にも多くのオプティマイザ・ヒントがありますが、このマニュアルの範囲からは外れます。使用できるヒントの詳細は、Oracleのデータベース・リファレンス・マニュアルを参照してください。
実行時にビュー・オブジェクトを作成する場合のオーバーヘッドについて理解しておくことが重要です。ビジネス要件でどうしても必要でないかぎり、行わないようにしてください。たとえば、アプリケーションで発行する問合せで、対象の表の名前が設計時にわかっていて、取得する列のリストも固定している場合は、設計時にビュー・オブジェクトを作成します。このようにすると、SQL文は適切にカプセル化され、開発中に簡単に説明およびチューニングでき、結果の行の構造とデータ型を検出するために実行時にオーバーヘッドをかけることがなくなります。
これに対し、実行時にApplicationModule
インタフェースでcreateViewObjectFromQueryStmt()
APIを使用すると、問合せはコードに埋もれてしまい、前もってSQLをチューニングすることが困難になり、ビュー・オブジェクトを作成するたびにパフォーマンスが犠牲になります。理論上、動的に作成されるビュー・オブジェクトのSQL問合せ文は、このAPIを使用して新しいインスタンスが作成されるたびに異なるので、実行中に問合せ結果の形状を検出するために、余分なデータベース・ラウンドトリップが必要になります。問い合せる表の名前が実行時までわからない場合にのみ、問合せを動的に作成します。それ以外のほとんどの要件には、設計時に作成するビュー・オブジェクトと、固定のWHERE句にバインド変数を設定する、または実行時にWHERE
句(オプションのバインド変数を含む)を追加するランタイムAPIを組み合せることで対応できます。
ビュー・オブジェクトの結果をプログラムで反復処理するコードを作成することがよくあります。よくある状況は、問合せ結果の複数の行を処理して属性またはエンティティが有効かどうかを判定する必要があるカスタム検証コードです。このような場合、行セット内の各行を1回のみ読み取り、後方にスクロールしたり、後で行セットを再び反復処理したりする必要がない場合は、前方のみモードを使用することで、取得した行のキャッシュを避けることができます。前方のみモードを有効にするには、ビュー・オブジェクトでsetForwardOnly(true)
を呼び出します。
注意: 前方のみモードで読取り専用ビュー・オブジェクト(エンティティ・オブジェクトの慣用名がない)を使用し、フェッチ・サイズを適切にチューニングするのが、データをプログラムで読み取る最も効率のよい方法です。 |
また、行セットを後方にスクロールすることがなく、reset()
を呼び出してイテレータを最初の行に設定することがない場合は、データを挿入、更新、または削除するときにも、前方のみモードを使用することで行のキャッシュを避けることができます。前方のみモードは、範囲サイズが1
の場合にのみ機能します。
前述したとおり、ビュー・オブジェクトの基本的な問合せ機能はすべて、カスタムJavaコードを使用せずに実行できます。クライアントは、ビュー・オブジェクト開発者側のカスタム・コードに頼ることなく、任意のSQL問合せのデータを取得および反復処理できます。要するに、多くの読取り専用ビュー・オブジェクトでは、SQL文を定義すればそれで完了です。ただし、必要になった場合に備えて、ビュー・オブジェクトでのカスタムJava生成を有効化する方法を理解しておくことが重要です。たとえば、カスタムJavaクラスでコードを作成する理由には次のようなものがあります。
検証メソッドを追加するため(ただし、Groovyスクリプトの式でサポートされているため、それを使用すればJavaは不要)
カスタム・ロジックを追加するため
組込み動作を拡張するため
通常、カスタム・ビュー・オブジェクトおよびビュー行クラスで記述、使用およびオーバーライドする一般的なコードの詳細は、付録D「ADF Business Componentsのよく使用されるメソッド」を参照してください。
ビュー・オブジェクトのカスタムJavaクラスの生成を有効にするには、ビュー・オブジェクトの概要エディタの「Java」ページを使用します。図42-6に示すように、ビュー・オブジェクトに関連付けることができるオプションのJavaクラスが3つあります。リストの初めの2つは、非常によく使用されます。
問合せを実行するコンポーネントを表すビュー・オブジェクト・クラス
問合せ結果の各行を表すビュー行クラス
カスタム・ビュー・オブジェクト・クラスの生成を有効にする場合、「バインド変数アクセッサ」チェック・ボックスも選択すると、JDeveloperによりビュー・オブジェクト・クラスにgetterメソッドおよびsetterメソッドが生成されます。Users
ビュー・オブジェクトには3つのバインド変数(TheName
、LowUserId
およびHighUserId
)があるため、カスタムのPersonsImpl.java
ビュー・オブジェクト・クラスの対応するメソッドは次のようになります。
public String getTheName() {...} public void setTheName(String value){...} public Number getHighUserId(){...} public void setHighUserId(Number value) {...} public Number getLowUserId() {...} public void setLowUserId(Number 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メソッドが生成されます。Persons
ビュー・オブジェクトの場合、対応するカスタムのPersonsRowImpl.java
クラスには次のようなメソッドが生成されます。
public Number getPersonId() {...} public void setPersonId(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) {...}
これらのメソッドを使用して行データを操作すると、コンパイル時にデータ型の使用方法が正しいかどうかがチェックされます。つまり、PersonId
属性の値を取得する次のようなコードは、さらに簡略化できます。
Number personId = (Number)row.getAttribute("PersonId");
簡略化したコードを次に示します。
Number personId = row.getPersonId();
後者の方法では、PersonId
ではなく誤ってPersonIdentifier
と入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error Number personId = row.getPersonIdentifier();
ビュー行アクセッサ・メソッドが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both attribute name and type cast are wrong, but compiler cannot catch it String personId = (String)row.getAttribute("PersonIdentifier");
これには、スペルが正しくない属性名とキャストの型が誤っている戻り値getAttribute()
が両方とも含まれています。Row
インタフェース上で汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
カスタム・ビュー行クラスの生成を有効にする場合に、ビュー行属性アクセッサを生成するよう選択すると、オプションで「クライアントへのアクセッサの公開」チェック・ボックスも選択できます。これにより、生成される付加的なカスタム行インタフェース(アプリケーション・クライアントで使用可能)が、実装クラスに直接依存することなく行のカスタム・メソッドにアクセスできるようになります。
ベスト・プラクティス: ビジネス・コンポーネントのクライアント・コードを作成する場合、具体的なクラスのかわりにビジネス・サービス・インタフェースを使用する必要があります。実装クラスではなくインタフェースを使用することで、サーバー側の実装が変更されてもクライアント・コードを変更する必要が生じません。クライアント・コードの使用の詳細は、3.5.9項「クライアントがアクセス可能なコンポーネントのカスタム・インタフェースのサポート」を参照してください。 |
たとえば、Persons
ビュー・オブジェクトの場合、クライアントへのアクセッサの公開によって、PersonsRow
という名前のカスタム行インタフェースが生成されます。このインタフェースは、ビュー・オブジェクトがあるパッケージのcommon
サブパッケージに作成されます。行インタフェースを持つことにより、クライアントでは、強く型付けされた方式で問合せ結果の属性にアクセスするコードを記述できるようになります。例42-6は、getPersonId()
とgetEmail()
をコールできるように、PersonsRow
インタフェースへのnext()
メソッドの結果をキャストするTestClient3
サンプル・クライアント・プログラムを示します。
例42-6 アクセッサによるクライアント行インタフェースの使用の単純な例
package devguide.examples.readonlyvo.client; import devguide.examples.readonlyvo.queries.common.PersonsRow; import oracle.jbo.*; import oracle.jbo.client.Configuration; import oracle.jbo.domain.Number; public class TestClient3 { public static void main(String[] args) { String amDef = "devguide.examples.PersonService"; String config = "PersonServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); ViewObject vo = am.findViewObject("PersonList"); vo.executeQuery(); while (vo.hasNext()) { // Cast next() to a strongly-typed PersonsRow interface PersonsRow curPerson = (PersonsRow)vo.next(); Number personId = curPerson.getPersonId(); String email = curPerson.getEmail(); System.out.println(personId+ " " + email); } Configuration.releaseRootApplicationModule(am, true); } }
これまでの説明で、ビュー・オブジェクトの実行時動作をカスタマイズする必要がある場合や、バインド変数またはビュー行属性へ強く型付けされたアクセスのみ行う場合のビュー・オブジェクトのカスタムJavaクラスの生成方法を示しました。
JDeveloperによるJavaクラスの生成を制御するデフォルト設定を変更するには、「ツール」→「設定」を選択し、「ビジネス・コンポーネント」ページを開きます。選択した設定は、今後作成するすべてのビジネス・コンポーネントに適用されます。
ADF Business Componentsを初めて使用する開発者には、デフォルトではカスタムJavaクラスを生成しないよう設定することをお薦めします。必要になった場合、その1つのコンポーネントに必要なカスタムJavaコードのみを有効にできます。経験を積むにつれ、どのようなデフォルト設定の組合せが最適であるかがわかるようになります。
1つ以上のカスタムJavaクラスを生成する場合、指定したJavaファイルがJDeveloperにより作成されます。
たとえば、devguide.examples.Persons
という名前のビュー・オブジェクトの場合、カスタムJavaファイルのデフォルト名は、ビュー・オブジェクト・クラスについてはPersonsImpl.java
、ビュー行クラスについてはPersonsRowImpl.java
になります。これらのファイルは両方とも、コンポーネントのXMLコンポーネント定義ファイルと同じ./devguide/examples
ディレクトリに作成されます。
ビュー・オブジェクトのJava生成オプションは、ビュー・オブジェクトの概要エディタの「Java」ページに後でアクセスしてもそのまま反映されています。XML定義ファイルの場合と同様、このエディタでどのような変更を行っても、カスタムJavaクラスで生成されたコードは最新の状態に保たれます。後でなんらかの理由によりカスタムJavaファイルが必要なくなった場合は、「Java」ページで関連するオプションの選択を解除すると、カスタムJavaファイルを削除できます。
この項では、カスタム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専用モードの場合、このようなクラスは存在しません。かわりに、属性はAttributesEnum
タイプとして定義され、そのファイルの<ViewAttribute>
タグ、関連<ViewLinkAccessor>
タグおよび<ViewAccessor>
タグの順序で、ビュー・オブジェクトのXMLコンポーネント定義に基づいて属性名(および各属性のアクセッサ)を指定します。実行時には、ビュー行の属性値は、ビュー・オブジェクトの属性リスト内における属性の数値的位置による索引が付けられた状態で、ViewRowImpl
ベース・クラスによって管理される構造に格納されます。
多くの場合、このプライベート実装に関する詳細は重要ではありません。ただし、ビュー行のカスタムJavaクラスを有効にする場合、ビュー行クラスでJDeveloperにより自動的に管理される生成コードの一部にこの実装の詳細が関連し、コードの使用目的を理解できます。たとえば、Users
ビュー行のカスタムJavaクラスの場合、例42-7に示されているように、属性、ビュー・リンク・アクセッサ属性またはビュー・アクセッサ属性ごとに対応する生成済のAttributesEnum
列挙定数があります。JDeveloperは、複数の開発者が新しい属性をXMLコンポーネント定義に追加した結果発生する可能性があるマージ競合を防ぐために、定数ではなく列挙定数を定義します。
例42-7 カスタム・ビュー行Javaクラスで自動的に管理される属性定数
public class PersonsRowImpl extends ViewRowImpl implements PersonsRow { /** * AttributesEnum: generated enum for identifying attributes and accessors. Do not modify. */ public enum AttributesEnum {...} public static final int PERSONID = AttributesEnum.PersonId.index(); public static final int EMAIL = AttributesEnum.Email.index(); public static final int FIRSTNAME = AttributesEnum.FirstName.index(); public static final int LASTNAME = AttributesEnum.LastName.index(); public static final int PERSONTYPECODE = AttributesEnum.PersonTypeCode.index(); public static final int PRIMARYADDRESSID = AttributesEnum.PrimaryAddressId.index(); // etc.
また、自動的に管理される、ビュー行クラスで強く型付けされたgetterメソッドおよびsetterメソッドでは、これらの属性定数は次のように使用されます。
public String getEmail() { return (String) getAttributeInternal(EMAIL); // <-- Attribute constant } public void setEmail(String value) { setAttributeInternal(EMAIL, value);// <-- Attribute constant }
AttributesEnum
タイプで定義されたビュー行属性定数に関連する自動管理コードの最後の2つの側面は、getAttrInvokeAccessor()
メソッドおよびsetAttrInvokeAccessor()
メソッドです。これらのメソッドは数値順索引によって属性アクセスのパフォーマンスを最適化します。これは、ViewRowImpl
ベース・クラスの汎用コードが属性値にアクセスするときの通常の方法です。getAttrInvokeAccessor()
メソッドの例は、次のPersonsRowImpl.java
クラスのようになります。もう1つのsetAttrInvokeAccessor()
メソッドの場合も同様です。
protected Object getAttrInvokeAccessor(int index, AttributeDefImpl attrDef) throws Exception { if ((index >= AttributesEnum.firstIndex()) && (index < AttributesEnum.count())) { return AttributesEnum.staticValues()[index - AttributesEnum.firstIndex()].get(this); } return super.getAttrInvokeAccessor(index, attrDef); }
属性に関連して生成されたこのコードに関する経験則は、次のとおりです。
必要に応じて、強く型付けされた属性のgetterメソッドおよびsetterメソッドの内部にカスタム・コードを追加してください。
ビュー・オブジェクトの概要エディタを使用して、ビュー・オブジェクト属性の順序または型を変更してください。
getterメソッドおよびsetterメソッドのJavaシグネチャや関連するXMLコンポーネント定義は、自動的に変更されます。
生成されたAttributesEnum
列挙定数のリストは変更しないでください。
getAttrInvokeAccessor()
メソッドおよびsetAttrInvokeAccessor()
メソッドは変更しないでください。
ビュー・オブジェクトの概要エディタで複数の名前付きビュー基準を定義し、必要に応じて実行時にその任意の組合せをビュー・オブジェクトに選択的に適用できます。設計時の名前付きビュー基準の処理の詳細は、5.11.1項「名前付きビュー基準を宣言的に作成する方法」を参照してください。
注意: この項の例では、Fusion Order Demoアプリケーションの |
名前付きビュー基準を適用するには、setApplyViewCriteriaNames()
メソッドを使用します。このメソッドは、適用する基準の名前のString配列を受け取ります。複数の名前付き基準を適用する場合は、実行時に生成されるWHERE句の中でAND
により結合されます。setApplyViewCriteriaNames()
メソッドで適用した新しいビュー基準は、以前適用されたビュー基準を上書きしません。
複数の名前付きビュー基準を適用する必要があるとき、ビュー・オブジェクトのクライアント・インタフェースでカスタム・メソッドを公開し、適用する名前付きビュー基準の組合せをカプセル化できます。たとえば、例42-8で示されているカスタム・メソッドshowMaleCustomers()
、showFemaleStaff()
およびshowFemaleCustomers()
は、それぞれがsetApplyViewCriteriaNames()
メソッドを使用して、名前付きビュー基準の適切な組合せを適用しています。これらのメソッドをビュー・オブジェクトのクライアント・インタフェースで公開すると、実行時に、クライアントは必要に応じてこれらのメソッドを呼び出し、ビュー・オブジェクトによって表示される情報を変更できます。
例42-8 適切な名前付き基準を有効にするためのクライアント・メソッドの公開
// In PersonsViewImpl.java public void showMaleCustomers() { ViewCriteriaManager vcm = getViewCriteriaManager(); ViewCriteria vc_gender = vcm.getViewCriteria("GenderIsNotFCriteria"); ViewCriteria vc_type = vcm.getViewCriteria("IsCustomerCriteria"); VariableValueManager vvm_gender = vc_gender.ensureVariableManager(); VariableValueManager vvm_type = vc_type.ensureVariableManager(); vvm_gender.setVariableValue("bv_Gender","F"); vvm_type.setVariableValue("bv_PersonTypeCode", "CUST"); setApplyViewCriteriaNames(new String[]{"GenderIsNotFCriteria", "IsCustomerCriteria"}); } public void showFemaleStaff() { ViewCriteriaManager vcm = getViewCriteriaManager(); ViewCriteria vc_gender = vcm.getViewCriteria("GenderIsFCriteria"); ViewCriteria vc_type = vcm.getViewCriteria("IsStaffSupplierCriteria"); VariableValueManager vvm_gender = vc_gender.ensureVariableManager(); VariableValueManager vvm_type = vc_type.ensureVariableManager(); vvm_gender.setVariableValue("bv_Gender","F"); vvm_type.setVariableValue("bv_PersonTypeCode", "CUST"); setApplyViewCriteriaNames(new String[]{"GenderIsFCriteria", "IsStaffSupplierCriteria"}); executeQuery(); } public void showFemaleCustomers() { ViewCriteriaManager vcm = getViewCriteriaManager(); ViewCriteria vc_gender = vcm.getViewCriteria("GenderIsFCriteria"); ViewCriteria vc_type = vcm.getViewCriteria("IsCustomerCriteria"); VariableValueManager vvm_gender = vc_gender.ensureVariableManager(); VariableValueManager vvm_type = vc_type.ensureVariableManager(); vvm_gender.setVariableValue("bv_Gender","F"); vvm_type.setVariableValue("bv_PersonTypeCode", "CUST"); setApplyViewCriteriaNames(new String[]{"GenderIsFCriteria", "IsCustomerCriteria"}); executeQuery();
現在適用されている名前付きビュー基準を除去するには、setApplyViewCriteriaNames(null)
を使用します。たとえば、例42-9
のshowAll()メソッドをUsers
ビュー・オブジェクトに追加し、クライアント・インタフェースで公開できます。このようにすると、クライアントは必要に応じてフィルタ処理されないデータのビューに戻すことができます。
行レベル・バインド変数値が行セットに対してすでに適用されている可能性があるため、設計時ビュー基準は削除しないでください。確実を期すため、設計時にビュー・アクセッサに対して定義された名前付きビュー基準は、ビュー基準のライフサイクル・メソッドによって削除されないように、ビュー・オブジェクト・インスタンスに必須のビュー基準として適用されます。
例42-9 適用されたすべての名前付きビュー基準の除去
// In UsersImpl.java public void showAll() { setApplyViewCriteriaNames(null); executeQuery(); }
注意:
|
実行時に、アプリケーションでは、単一のビジネス・オブジェクト・インタフェースで異なるクライアント・メソッドを呼び出し、異なるフィルタ処理が行われたデータのセットを戻すことができます。例42-10は、前述のPersons
ビュー・オブジェクトを使用するTestClient
クラスの行を示しています。showResults()
メソッドは、ビュー・オブジェクトの行を反復処理して属性を表示するヘルパー・メソッドです。
例42-10 名前付きビュー基準を使用するテスト・クライアントのコード
// In TestClientMultipleViewCriterias.java PersonsView vo = (PersonsView)am.findViewObject("PersonsView"); vo.showMaleCustomers(); showResults(vo,"After applying view criterias for male customers"); vo.applyViewCriteria(null); vo.showFemaleStaff(); showResults(vo,"After applying view criterias for female staff"); vo.showFemaleCustomers(); showResults(vo,"After applying view criterias for female customers"); vo.showAll(); vo.showResults(vo,"After clearing all view criterias");
TestClient
プログラムを実行すると、次のような出力が生成されます。
---After applying view criterias for male customers --- Daniel Faviet [CUST, M] John Chen [CUST, M] Ismael Sciarra [CUST, M] Jose Manuel Urman [CUST, M] Luis Popp [CUST, M] Den Raphaely [CUST, M] Alexander Khoo [CUST, M] Sigal Tobias [CUST, M] Guy Himuro [CUST, M] Matthew Weiss [CUST, M] Adam Fripp [CUST, M] Payam Kaufling [CUST, M] Kevin Mourgos [CUST, M] James Landry [CUST, M] Steven Markle [CUST, M] ... ---After applying view criterias for female staff --- Neena Kochhar [STAFF, F] Valli Pataballa [STAFF, F] Diana Lorentz [STAFF, F] Terra Bralick [SUPP, F] Rachel Berman [SUPP, F] Claudia Benghiat [SUPP, F] Sharon Hemant [SUPP, F] Alison Chen [SUPP, F] Alex Duckers [SUPP, F] Katrina Han [SUPP, F] ---After applying view criterias for female customers --- Nancy Greenberg [CUST, F] Shelli Baida [CUST, F] Karen Colmenares [CUST, F] Shanta Vollman [CUST, F] Julia Nayer [CUST, F] Irene Mikkilineni [CUST, F] Laura Bissot [CUST, F] ---After clearing all view criterias --- Steven King [STAFF, M] Neena Kochhar [STAFF, F] Lex De Haan [STAFF, M] Alexander Hunold [STAFF, M] Bruce Ernst [STAFF, M] David Austin [STAFF, M] Valli Pataballa [STAFF, F] Diana Lorentz [STAFF, F] Nancy Greenberg [CUST, F] ...
デフォルトでは、ビュー・オブジェクトはデータベースに対して問合せを実行し、結果の行セットに行を取得します。ただし、ビュー・オブジェクトを使用して、メモリー内で検索とソートを実行し、データベースへの不必要なトリップを避けることもできます。
注意: この項の例では、Fusion Order Demoアプリケーションの |
ビュー・オブジェクトのSQLモードは、行を取得して行セットに移入するために使用されるソースを制御します。setQueryMode()
を使用すると、使用するモードまたはモードの組合せを制御できます。
ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES
データベースから結果を取得するデフォルトのモードです。
ViewObject.QUERY_MODE_SCAN_VIEW_ROWS
すでに行セットに存在する行をソースとして使用し、メモリー内フィルタ処理をとおして行セットの内容を段階的に調整できます。
ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS
エンティティ・ベースのビュー・オブジェクトに対してのみ有効なモードで、エンティティ・キャッシュに現在存在するエンティティ行をソースとして使用し、キャッシュの内容に基づいて結果を生成します。
モードを個別に使用することも、Javaの論理OR
演算子(X
|
Y
)を使用して組み合せることもできます。たとえば、エンティティ・キャッシュでポストされていない新しいエンティティ行を問い合せ、同時にデータベースで既存の行を問い合せるビュー・オブジェクトを作成するには、次のようなコードを使用します。
setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES | ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS)
SQLモードを組み合せると、ビュー・オブジェクトは重複する行のスキップを自動的に処理します。さらに、検出された結果には暗黙の順序があります。
スキャン・ビュー行(指定されていた場合)
スキャン・エンティティ・キャッシュ(指定されていた場合)
SQL問合せの発行によるスキャン・データベース表(指定されていた場合)
setQueryMode()
メソッドを呼び出してSQLモードを変更した場合、新しい設定は次にexecuteQuery()
メソッドを呼び出すと有効になります。
実行時にビュー・オブジェクトの行をソートするには、setSortBy()
メソッドを使用します。SQLのORDER BY句のようなソート式を渡します。ただし、表の列名を参照するかわりに、ビュー・オブジェクトの属性名を使用します。たとえば、Customer
およびDaysOpen
という名前の属性を含むビュー・オブジェクトの場合、次のメソッドを呼び出すことで、最初にCustomer
の降順に、次にDaysOpen
で、ビュー・オブジェクトをソートできます。
setSortBy("Customer desc, DaysOpen");
または、次のように、ソート処理句の中ではゼロから始まる属性インデックス位置を使用することもできます。
setSortBy("3 desc, 2");
setSortBy()
メソッドを呼び出した後、次にexecuteQuery()
メソッドを呼び出すと行がソートされます。ビュー・オブジェクトは、このソート句を適切な書式に変換し、ビュー・オブジェクトのSQLモードに基づいた行の並替えに使用します。デフォルトのSQLモードを使用すると、SortBy
句は適切なORDER BY
句に変換され、データベースに送信されるSQL文の一部として使用されます。いずれかのメモリー内SQLモードの場合は、SortBy
句は1つまたは複数のSortCriteria
オブジェクトに変換され、メモリー内ソートの実行で使用されます。
注意: SQL ORDER BY式では列名の大文字と小文字は区別されませんが、 |
setSortBy()
およびsetQueryMode()
メソッドを使用して、読取り専用のビュー・オブジェクトによって生成された行に対してメモリー内ソートを実行できます。例42-11は、読取り専用のビュー・オブジェクトClosedOrders
によって生成された行に対してメモリー内ソートを実行するために、setSortBy()
とsetQueryMode()
を使用するTestClientSetSortBy
クラスの興味深いコード行を示しています。
例42-11 メモリー内ソートでのsetSortByとsetQueryModeの組合せ
// In TestClientSetSortBy.java am.getTransaction().executeCommand("ALTER SESSION SET SQL_TRACE TRUE"); ViewObject vo = am.findViewObject("ClosedOrders"); vo.executeQuery(); showRows(vo,"Initial database results"); vo.setSortBy("Customer desc"); vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS); vo.executeQuery(); showRows(vo,"After in-memory sorting by Customer desc"); vo.setSortBy("Customer desc, DaysOpen"); vo.executeQuery(); showRows(vo,"After in-memory sorting by Customer desc, DaysOpen");
この例を実行すると、次のような結果が生成されます。
--- Initial database results --- 106,Ice machine not working,1,mhartste 103,Washing machine leaks,4,ngreenbe 105,Air in dryer not hot,4,jmurman 109,Freezer is not cold,4,jwhalen : --- After in-memory sorting by Customer desc --- 100,I have noticed that every time I do a...,9,dfaviet 101,Agitator does not work,8,sbaida 103,Washing machine leaks,4,ngreenbe 105,Air in dryer not hot,4,jmurman : --- After in-memory sorting by Customer desc, DaysOpen --- 100,I have noticed that every time I do a...,9,dfaviet 101,Agitator does not work,8,sbaida 105,Air in dryer not hot,4,jmurman 109,Freezer is not cold,4,jwhalen :
executeCommand()
の呼出しを含む例42-11の1行目では、ALTER SESSION SET SQL TRACE
コマンドを発行して、現在のデータベース・セッションでSQLトレースを有効にしています。これにより、Oracleデータベースは、実行されるすべてのSQL文をサーバー側のトレース・ファイルに記録します。データベースが文を解析した回数や、問合せ結果を取得する間に行のバッチをフェッチするためにクライアントが行ったラウンドトリップの回数など、各SQL文のテキストに関する情報が記録されます。
注意:
|
生成されたトレース・ファイルは、データベースに付属するTKPROF
ユーティリティを使用して書式設定できます。
tkprof xe_ora_3916.trc trace.prf
TKPROF
ユーティリティでの作業の詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』のSQLトレースとTKPROFについてに関する項およびSQLトレース機能とTKPROFの使用方法に関する項を参照してください。
このユーティリティは、ClosedOrders
ビュー・オブジェクトによって実行されたSQL文に関する、例42-12で示されているような興味深い情報を含むtrace.prf
ファイルを生成します。最初に1回の実行で6行のデータを問い合せてデータベースからフェッチした後、その後に行われたこれらの結果に対する2回のソートでは実行が行われていないことがわかります。コードでSQLモードがViewObject.QUERY_MODE_SCAN_VIEW_ROWS
に設定されているため、setSortBy()
の後のexecuteQuery()
ではメモリー内でソートが実行されました。
例42-12 メモリー内でソートが行われたことを示すトレースファイルのTKPROF出力
************************************************************* SELECT * FROM (select o.order_id, case when length(o.giftwrap_message) > 5 then rtrim(substr(o.giftwrap_message,1,5))||'...' else o.giftwrap_messagen end as giftwrap_message, ceil( (select trunc(max(creation_date)) from order_histories where order_id = or.order_id) - trunc(o.order_date) ) as days_open, p.email as customer from orders o, persons p where o.customer_id = p.person_id and order status code = 'COMPLETE') call count cpu elapsed disk query current rows ------- ----- ------ -------- ---- ------ -------- ------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 1 0.00 0.00 0 22 0 6 ------- ----- ------ -------- ---- ------ -------- ------- total 3 0.00 0.00 0 22 0 6 *************************************************************
メモリー内での行のソート方法をカスタマイズする必要がある場合は、次の2つの拡張ポイントがあります。
次のメソッドをオーバーライドできます。
public void sortRows(Row[] rows)
このメソッドは、行のメモリー内ソート処理を実際に行います。このメソッドをオーバーライドすることで、必要に応じてかわりのソート方法を組み込むことができます。
次のメソッドをオーバーライドできます。
public Comparator getRowComparator()
このメソッドのデフォルトの実装はoracle.jbo.RowComparator
を返します。RowComparator
はcompareTo()
メソッドを呼び出して、2つのデータ値を比較します。これらのメソッドとオブジェクトをオーバーライドし、カスタム比較ルーチンを提供できます。
ViewCriteria
を使用して行セットの内容をフィルタするために、次のメソッドを呼び出すことができます。
applyViewCriteria()
またはsetApplyViewCriteriaNames()
を呼び出してからexecuteQuery()
を呼び出すと、フィルタ処理された新しい行セットが生成されます。
findByViewCriteria()
を呼び出すと、元の行セットの内容を変更することなく、プログラムで処理するための新しい行セットが取得されます。
これらの方法はどちらも、ビュー基準モードに応じて、データベースに対して、またはメモリー内フィルタ処理を実行するために、あるいはその両方に使用できます。基準モードは、ViewCriteria
オブジェクトでsetCriteriaMode()
メソッドを使用して設定します。このメソッドには、次の整数フラグのどちらか一方、または両方の論理OR
を渡すことができます。
ViewCriteria.CRITERIA_MODE_QUERY
ViewCriteria.CRITERIA_MODE_CACHE
ビュー基準によるメモリー内フィルタ処理で使用できる演算子を表42-1に示します。カッコを使用して副次式をグループ化でき、副次式の間にはAND
とOR
の演算子を使用できます。
表42-1 ビュー基準によるメモリー内フィルタ処理でサポートされているSQL演算子
演算子 | 操作 |
---|---|
|
比較 |
|
論理否定 |
|
論理積 |
|
論理和 |
例42-13は、前述のデータベースに対するものとメモリー内の2つの機能を使用するTestClientFindByViewCriteria
クラスの興味深い行を示しています。この例は、CustomerList
ビュー・オブジェクトのインスタンスを使用し、次の基本的な手順を実行します。
データベースで姓がCで始まる顧客を問い合せて、次のような出力を生成します。
--- Initial database results with applied view criteria --- John Chen Emerson Clabe Karen Colmenares
メモリー内で、手順1の結果から、名がJで始まる顧客のみのサブセットを作成します。そのために、ビュー基準に2番目のビュー基準行を追加し、ANDを使用する論理積を設定しています。次のような出力が生成されます。
--- After augmenting view criteria and applying in-memory --- John Chen
結合をOR
に戻し、基準を再びデータベースに適用して、姓がJ%または名がC%の顧客を問い合せます。次のような出力が生成されます。
--- After changing view criteria and applying to database again --- John Chen Jose Manuel Urman Emerson Clabe Karen Colmenares Jennifer Whalen
姓または名に文字oを含む顧客をメモリー内で検索する新しい基準を定義します。
サブセットを作成するのではなく、findByViewCriteria()
を使用して新しい行セットを作成し、出力を生成します。
--- Rows returned from in-memory findByViewCriteria --- John Chen Jose Manuel Urman Emerson Clabe Karen Colmenares
findByViewCriteria()
を使用しても元の行セットが変化していないことを示し、出力を生成します。
--- Note findByViewCriteria didn't change rows in the view --- John Chen Jose Manuel Urman Emerson Clabe Karen Colmenares Jennifer Whalen
例42-13 ビュー基準によるデータベースおよびメモリー内でのフィルタ処理の実行
// In TestClientFindByViewCriteria.java ViewObject vo = am.findViewObject("CustomerList"); // 1. Show customers with a last name starting with a 'M' ViewCriteria vc = vo.createViewCriteria(); ViewCriteriaRow vcr1 = vc.createViewCriteriaRow(); vcr1.setAttribute("LastName","LIKE 'M%'"); vo.applyViewCriteria(vc); vo.executeQuery(); vc.add(vcr1); vo.executeQuery(); showRows(vo, "Initial database results with applied view criteria"); // 2. Subset results in memory to those with first name starting with 'S' vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS); ViewCriteriaRow vcr2 = vc.createViewCriteriaRow(); vcr2.setAttribute("FirstName","LIKE 'S%'"); vcr2.setConjunction(ViewCriteriaRow.VCROW_CONJ_AND); vc.setCriteriaMode(ViewCriteria.CRITERIA_MODE_CACHE); vc.add(vcr2); vo.executeQuery(); showRows(vo,"After augmenting view criteria and applying in-memory"); // 3. Set conjuction back to OR and re-apply to database query to find // customers with last name like 'H%' or first name like 'S%' vc.setCriteriaMode(ViewCriteria.CRITERIA_MODE_QUERY); vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES); vcr2.setConjunction(ViewCriteriaRow.VCROW_CONJ_OR); vo.executeQuery(); showRows(vo,"After changing view criteria and applying to database again"); // 4. Define new critera to find customers with first or last name like '%o%' ViewCriteria nameContainsO = vo.createViewCriteria(); ViewCriteriaRow lastContainsO = nameContainsO.createViewCriteriaRow(); lastContainsO.setAttribute("LastName","LIKE '%o%'"); ViewCriteriaRow firstContainsO = nameContainsO.createViewCriteriaRow(); firstContainsO.setAttribute("FirstName","LIKE '%o%'"); nameContainsO.add(firstContainsO); nameContainsO.add(lastContainsO); // 5. Use findByViewCriteria() to produce new rowset instead of subsetting nameContainsO.setCriteriaMode(ViewCriteria.CRITERIA_MODE_CACHE); RowSet rs = (RowSet)vo.findByViewCriteria(nameContainsO, -1,ViewObject.QUERY_MODE_SCAN_VIEW_ROWS); showRows(rs,"Rows returned from in-memory findByViewCriteria"); // 6. Show that original rowset hasn't changed showRows(vo,"Note findByViewCriteria didn't change rows in the view");
RowMatch
オブジェクトは、メモリー内フィルタ処理の基準を表現するためのさらに便利な方法を提供します。RowMatch
オブジェクトを作成するには、次のように、問合せ条件式をコンストラクタに渡します。
RowMatch rm = new RowMatch("LastName = 'Popp' or (FirstName like 'A%' and LastName like 'K%')");
SortBy
句と同じように、ビュー・オブジェクトの属性名に関するRowMatch
の式を、表42-2に示す、サポートされる演算子を使用して表現します。カッコを使用して副次式をグループ化でき、副次式の間にはAND
とOR
の演算子を使用できます。
表42-2 RowMatchによるメモリー内フィルタ処理でサポートされているSQL演算子
演算子 | 操作 |
---|---|
|
比較 |
|
論理否定 論理否定演算
|
|
論理積 |
|
論理和 |
表42-3に示すように、RowMatch
式ではSQL機能の一部も使用できます。
表42-3 RowMatchによるメモリー内フィルタ処理でサポートされているSQL関数
演算子 | 操作 |
---|---|
|
文字列内のすべての文字を大文字に変換する。 |
|
数または日付を文字列に変換する。 |
|
文字列を日付形式に変換する。 |
|
文字列をタイムスタンプに変換する。 |
注意: SQL問合せ条件では列名の大文字と小文字は区別されませんが、 |
ビュー・オブジェクトにRowMatch
を適用するには、setRowMatch()
メソッドを呼び出します。ViewCriteria
とは異なり、RowMatch
はメモリー内フィルタ処理にのみ使用するため、設定する一致モードはありません。サポートされるすべてのSQLモードのビュー・オブジェクトに対してRowMatch
を使用でき、適用した結果は、次にexecuteQuery()
メソッドを呼び出したときに表示されます。
ビュー・オブジェクトにRowMatch
を適用するとき、RowMatch
の式では、SQL文で使用するものと同じ:VarName
表記法を使用して、ビュー・オブジェクトの名前付きバインド変数を参照できます。たとえば、ビュー・オブジェクトにStatusCode
という名前の名前付きバインド変数がある場合は、次の式でRowMatchを適用できます。
Status = :StatusCode or :StatusCode = '%'
例42-14は、活動中のRowMatch
を示すTestClientRowMatch
クラスの興味深い行を示しています。例で使用されているCustomerListビュー・オブジェクトには、Selected
という名前の一時Boolean
属性があります。このコードが実行する基本的な手順は次のとおりです。
顧客の完全なリストを問い合せて、出力を生成します。
--- Initial database results --- Neena Kochhar [null] Lex De Haan [null] Nancy Greenberg [null] :
奇数行のSelected
属性にBoolean.TRUE
を設定し、奇数番号の行を選択済としてマークします。出力を生成します。
--- After marking odd rows selected --- Neena Kochhar [null] Lex De Haan [true] Nancy Greenberg [null] Daniel Faviet [true] John Chen [null] Ismael Sciarra [true] :
RowMatch
を使用して、選択されている行、つまりSelected = true
の行のみを含む行セットのサブセットを作成します。次のような出力が生成されます。
--- After in-memory filtering on only selected rows --- Lex De Haan [true] Daniel Faviet [true] Ismael Sciarra [true] Luis Popp [true] :
より複雑なRowMatch
式を使用して、行セットのサブセットをさらに絞り込みます。出力を生成します。
--- After in-memory filtering with more complex expression --- Lex De Haan [true] Luis Popp [true]
例42-14 RowMatchによるメモリー内フィルタ処理の実行
// In TestClientRowMatch.java // 1. Query the full customer list ViewObject vo = am.findViewObject("CustomerList"); vo.executeQuery(); showRows(vo,"Initial database results"); // 2. Mark odd-numbered rows selected by setting Selected = Boolean.TRUE markOddRowsAsSelected(vo); showRows(vo,"After marking odd rows selected"); // 3. Use a RowMatch to subset row set to only those with Selected = true RowMatch rm = new RowMatch("Selected = true"); vo.setRowMatch(rm); // Note: Only need to set SQL mode when not defined at design time vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS); vo.executeQuery(); showRows(vo, "After in-memory filtering on only selected rows"); // 5. Further subset rowset using more complicated RowMatch expression rm = new RowMatch("LastName = 'Popp' "+ "or (FirstName like 'A%' and LastName like 'K%')"); vo.setRowMatch(rm); vo.executeQuery(); showRows(vo,"After in-memory filtering with more complex expression"); // 5. Remove RowMatch, set query mode back to database, requery to see full list vo.setRowMatch(null); vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES); vo.executeQuery(); showRows(vo,"After re-querying to see a full list again");
RowMatch
を使用して行セットをフィルタするのみでなく、rowQualifies()
メソッドを使用すると、それがカプセル化している基準に個別の行が一致するかどうかをテストすることもできます。たとえば、次のように入力します。
RowMatch rowMatch = new RowMatch("CountryId = 'US'"); if (rowMatch.rowQualifies(row)) { System.out.println("Customer is from the United States "); }
RowMatch
を適用すると、ビュー・オブジェクトのSQLモードがデータベースからの行の取得に設定されている場合、executeQuery()
を呼び出すと、フェッチされた行にRowMatch
が適用されます。フェッチされた行が認定されない場合、その行は行セットに追加されません。
SQLのWHERE
句とは異なり、RowMatch
は、一時ビュー・オブジェクト属性およびまだポストされていない属性値を含む式でも評価できます。これは、Javaで値が計算される一時ビュー行属性を含むRowMatch
式に基づいて、問い合された行をフィルタするのに便利です。ただし、この興味深い機能は、アプリケーションが大きな行セットを処理する必要がある場合には注意して使用する必要があります。最初にデータベース・レベルのフィルタ処理を使用してできるかぎり小さい行セットを取得した後、RowMatchを使用してメモリーでそのリストからサブセットを作成することをお薦めします。
時にはSupplier
のような単一の型のエンティティ行と連携するビュー・オブジェクトを作成する場合があり、これにはSupplier
固有の属性が含まれていると考えられます。また、場合によっては、同じ行セットでエンティティ・オブジェクト継承階層に基づいて行を問い合せて更新することもあります。たとえば、同じ行セットで、Persons
、Supplier
およびStaff
の各エンティティ・オブジェクトの継承階層に共通する属性を処理することがあります。
注意: この項で説明する例を試すには、4.19項「ビジネス・ドメイン・レイヤーでの継承の使用」で使用したものと同じ |
多相エンティティ・オブジェクトの慣用名とは、継承階層のベース・エンティティ・オブジェクトを参照し、そのエンティティのサブタイプも処理するように構成されているものです。図42-8は、多相エンティティ・オブジェクトの慣用名でビュー・オブジェクトを使用した結果を示しています。エンティティ・ベースのPersonList
ビュー・オブジェクトには、1次エンティティ・オブジェクトの慣用名としてPerson
エンティティ・オブジェクトがあります。ビュー・オブジェクトは、データベースから取得された各行を、適切なエンティティ・オブジェクト・サブタイプであるPerson
のエンティティ行部分に分割します。識別子属性の値の検査に基づいて、適切なエンティティ行サブタイプを作成します。たとえば、PersonList
問合せが、個人ngreenbe
の1行、スタッフsking
の1行、サプライヤahunold
の1行を取得した場合、基礎になっているエンティティ行部分は図で示されているようになります。
多相エンティティ・オブジェクトの慣用名で作成するビュー・オブジェクトは、ベース・エンティティ・オブジェクトおよびサブタイプ・エンティティの1つ以上の属性を継承できます。エンティティ・オブジェクトから選択する属性は、ビュー・オブジェクト属性定義によってオーバーライドされます。エンティティ・ベースのビュー・オブジェクトが識別子属性を持つエンティティ・オブジェクトを参照していると、JDeveloperはその識別子属性も(主キー属性に加えて)問合せに含まれるようにします。
作業を始める前に、次のようにします。
4.2.2項「エンティティの作成ウィザードで単一のエンティティ・オブジェクトを作成する方法」の説明に従って、多相エンティティ・オブジェクトの慣用名の継承元ベース・エンティティ・オブジェクトを作成します。
4.10.14項「エンティティ・オブジェクト継承階層の識別子属性の設定方法」の説明に従って、ベース・エンティティ・オブジェクトを拡張して、多相エンティティ・オブジェクトの慣用名を作成し、エンティティ行サブタイプの基礎となる識別子属性を指定します。
5.2.1項「ビューを基にしてエンティティ・オブジェクトを作成する方法」の説明に従って、ベース・エンティティ・オブジェクトからエンティティ・ベースのビュー・オブジェクトを作成し、多相エンティティ・オブジェクトの慣用名に対して作成するビュー・オブジェクトに共通する属性を選択します。
多相エンティティ・オブジェクトの慣用名でビュー・オブジェクトを作成する手順:
アプリケーション・ナビゲータの、ユーザー・インタフェース・プロジェクトの下の部分で、アプリケーションを右クリックして「新規」を選択します。
「新規ギャラリ」で「ビジネス層」を展開し、「ADF Business Components」、「ビュー・オブジェクト」を選択して、「OK」をクリックします。
ビュー・オブジェクト作成ウィザードでビュー・オブジェクトを指定して、「参照」をクリックします。
たとえば、Supplier
およびStaff
サブタイプ・エンティティ・オブジェクトに対して、多相エンティティ・オブジェクトの慣用名でのビュー・オブジェクトの作成をサポートするために、データ・モデル・プロジェクトでベースのPersons
エンティティ・オブジェクトを定義できます。これらの多相エンティティ・オブジェクトの慣用名のビュー・オブジェクトを作成する場合、SupplierList
またはStaffList
という名前のビュー・オブジェクトを作成できます。
「親の選択」ダイアログで、ベース・エンティティ・オブジェクトから作成したエンティティ・ベースのビュー・オブジェクトを選択して、「OK」をクリックします。
たとえば、ベース・エンティティ・オブジェクトPersons
のビュー・オブジェクトPersonList
を作成する場合、拡張するビュー・オブジェクトとしてPersonList
を選択します。
ビュー・オブジェクト作成ウィザードで「次」をクリックして、「選択済」リストにベース・エンティティ・オブジェクトがすでに表示され、Extended
とラベルが付けられていることを確認します(図42-9を参照)。
ビュー・オブジェクト作成ウィザードの「エンティティ・オブジェクト」ページの「選択済」リストでベース・エンティティ・オブジェクトを選択して、「サブタイプ」をクリックします。
「サブタイプの選択」ダイアログで、許可するエンティティ・サブタイプを「使用可能」から「選択済」リストに移動し、「OK」をクリックします。
たとえば、StaffList
ビュー・オブジェクト用にエンティティ・サブタイプStaff
を選択します(図42-10を参照)。
ビュー・オブジェクト作成ウィザードの「エンティティ・オブジェクト」ページの「使用可能」リストで、このビュー・オブジェクトを処理するエンティティ・サブタイプを表すエンティティ・オブジェクトを特定して、「選択済」リストに追加します。
たとえば、多相エンティティ・オブジェクトの慣用名でのStaffList
ビュー・オブジェクトの作成をサポートするために、Staff
エンティティ・サブタイプを選択します。
「ビジネス・コンポーネント」ダイアログで、「OK」をクリックしてビュー・オブジェクトのエンティティ・オブジェクトの慣用名をオーバーライドします。
ビジネス・コンポーネント」ダイアログで、ベース・エンティティ・オブジェクトの慣用名の属性をエンティティ・サブタイプでオーバーライドする旨の警告が示されます(図42-11を参照)。
ビュー・オブジェクト作成ウィザードの「エンティティ・オブジェクト」ページで「次」をクリックし、必要な属性をウィザードの「属性」ページでベース・エンティティ・オブジェクトから「選択済」リストに移動します。
ウィザードへの入力を完了させて、「終了」をクリックします。
概要エディタの「エンティティ・オブジェクト」ページに、選択されたエンティティ・オブジェクトとエンティティ・サブタイプ・オーバーライドが表示されます。たとえば、StaffList
ビュー・オブジェクトの概要エディタに、ThePerson (Staff): overridden
のように、オーバーライドされたエンティティ・オブジェクトとカッコで囲まれたサブタイプが表示されます(図42-12を参照)。
この手順を繰り返して、ベース・エンティティ・オブジェクトに作成した追加の多相エンティティ・オブジェクトの慣用名のビュー・オブジェクトを作成します。
多相エンティティ・オブジェクトの慣用名でエンティティ・ベースのビュー・オブジェクトを作成すると、JDeveloperは許されるエンティティ・サブタイプに関する情報をビュー・オブジェクトのXMLコンポーネント定義に追加します。たとえば、前記のPersonList
ビュー・オブジェクトを作成すると、許されるサブタイプ・エンティティ・オブジェクトの名前が次のようなAttrArrayタグに記録されます。
<ViewObject Name="PersonList" ... > <EntityUsage Name="ThePerson" Entity="devguide.advanced.inheritance.Persons" > </EntityUsage> ... <AttrArray Name="EntityImports"> <Item Value="devguide.advanced.inheritance.Staff" /> <Item Value="devguide.advanced.inheritance.Supplier" /> </AttrArray> <!-- etc. --> </ViewObject>
この項では、多相エンティティ・オブジェクトの慣用名の使用に役立つ追加情報を提供します。
ビュー・オブジェクトが階層で使用可能なエンティティ・サブタイプのサブセットのみを使用する場合は、識別子列が期待するエンティティ・タイプと一致する行のみを返すように問合せを制限する適切なWHERE
句を含める必要があります。
設計上、クライアントはエンティティ・オブジェクトを直接使用しません。かわりに、当面のタスクに関連する情報のセットを表す適切なビュー・オブジェクトのビュー行を通して、間接的にエンティティ・オブジェクトを使用します。ビュー・オブジェクトは、当面のタスクに関連する1つまたは複数のエンティティ・オブジェクトの基礎となる属性の特定のセットを公開できるのと同じように、これらのエンティティから選択されたメソッドのセットも公開できます。そのためには、カスタム・ビュー行のJavaクラスを有効にし、次のビュー行クラスでメソッドを記述します。
ビュー行で生成されるエンティティ・アクセッサを使用して基礎となる適切なエンティティ行にアクセス
そのメソッドの呼出し
たとえば、PersonsImpl
クラスにperformPersonFeature()
メソッドを含むPersons
エンティティ・オブジェクトがあるものとします。このメソッドをPersonsList
ビュー行でクライアントに公開するには、カスタム・ビュー行のJavaクラスを有効にし、例42-15で示されているメソッドを記述します。JDeveloperは、エンティティ・オブジェクトの慣用名の別名に基づいて、関係する各エンティティ・オブジェクトの慣用名に対するビュー行クラスで、エンティティ・アクセッサ・メソッドを生成します。PersonsList
ビュー・オブジェクトでのPersons
エンティティの別名はThePerson
であるため、そのエンティティ・オブジェクトの慣用名に関係するエンティティ行部分を返すためのgetThePerson()
メソッドを生成します。
例42-15 選択したエンティティ・オブジェクト・メソッドの委譲によるビュー行での公開
// In PersonListRowImpl.java public void performPersonFeature() { getThePerson().performPersonFeature(); }
ビュー行のperformPersonFeature()
メソッドのコードは、このgetThePerson()
メソッドを使用して基礎となるPersonImpl
エンティティ行クラスにアクセスし、そのperformPersonFeature()
メソッドを呼び出します。このコーディング・スタイルは委譲と呼ばれるもので、ビュー行メソッドはそのメソッドの1つの実装を、基礎となるエンティティ・オブジェクトの対応するメソッドに委譲します。多相エンティティ・オブジェクトの慣用名のあるビュー行で委譲が使用されると、委譲されたメソッド呼出しは、基礎となる適切なエンティティ行サブタイプによって処理されます。つまり、PersonsImpl
クラス、StaffImpl
クラス、およびSupplierImpl
クラスが異なる方法でperformPersonFeature()
メソッドを実装している場合は、現在行のエンティティ・サブタイプに応じて、適切な実装が使用されます。
クライアント行インタフェースでこのメソッドを公開すると、クライアント・プログラムは、カスタム行インタフェースを使用して、特定のビュー行でカスタム・ビジネス機能を呼び出すことができます。例42-16は、TestEntityPolymorphism
クラスのコード行を示しています。PersonList
ビュー・オブジェクト・インスタンスのすべての行を反復し、各行をカスタムPersonListRow
インタフェースにキャストして、performPersonFeature()
メソッドを呼び出しています。
例42-16 エンティティ・オブジェクトに委譲するビュー行メソッドの呼出し
PersonList personlist = (PersonList)am.findViewObject("PersonList"); personlist.executeQuery(); while (personlist.hasNext()) { PersonListRow person = (PersonListRow)personlist.next(); System.out.print(person.getEmail()+"->"); person.performPersonFeature(); }
例42-16のクライアント・コードを実行すると、次のような出力が生成されます。
austin->## performPersonFeature as Supplier hbaer->## performPersonFeature as Person : sking->## performPersonFeature as Staff :
Persons
エンティティに関連する行では、PersonsImpl
クラスのperformPersonFeature()
メソッドが使用されたことを示すメッセージが表示されます。Supplier
エンティティおよびStaff
エンティティに関連する行では異なるメッセージが表示され、それぞれSupplierImpl
クラスおよびStaffImplクラスが継承されたperformPersonFeature()
メソッドに対して持っている異なる実装が示されます。
多相エンティティ・オブジェクトの慣用名のあるビュー・オブジェクトでは、新しいビュー行を作成すると、それにはベース・エンティティ・オブジェクトの慣用名と一致する型を持つ新しいエンティティ行部分が含まれます。かわりにいずれかのエンティティ・サブタイプで新しいビュー行を作成するには、createAndInitRow()
メソッドを使用します。例42-17では、PersonList
ビュー・オブジェクトのJavaクラスの2つのカスタム・メソッドが示されており、これらはcreateAndInitRow()
を使用して、クライアントがStaff
またはSupplier
サブタイプのエンティティ行を持つ新しい行を作成できるようにします。createAndInitRow()
を使用するには、例で示されているように、NameValuePairs
オブジェクトのインスタンスを作成し、それに識別子属性の適切な値を設定します。次に、そのNameValuePairs
をcreateAndInitRow()
メソッドに渡し、指定した識別子属性の値に基づいて、適切なエンティティ行サブタイプを持つ新しいビュー行を作成します。
例42-17 エンティティ・サブタイプを持つ新しい行を作成するためのカスタム・メソッドの公開
// In PersonListImpl.java public PersonListRow createStaffRow() { NameValuePairs nvp = new NameValuePairs(); nvp.setAttribute("PersonTypeCode","STAFF"); return (PersonListRow)createAndInitRow(nvp); } public PersonListRow createSupplierRow() { NameValuePairs nvp = new NameValuePairs(); nvp.setAttribute("PersonTypeCode","SUPP"); return (PersonListRow)createAndInitRow(nvp); }
このようなメソッドをビュー・オブジェクトのカスタム・インタフェースで公開すると、実行時に、クライアントはそれを呼び出して、適切なエンティティ・サブタイプを持つ新しいビュー行を作成できます。例42-18は、TestEntityPolymorphism
クラスのこの機能に関連する行を示しています。最初に、createRow()
メソッド、createStaffRow()
メソッド、およびcreateSupplierRow()
メソッドを使用して、3つの新しいビュー行を作成します。次に、新しい行のそれぞれでPersonListRow
カスタム・インタフェースからperformPersonFeature()
メソッドを呼び出します。
各行は期待したとおりに関連するエンティティ行のサブタイプに固有の方法でメソッドを処理し、結果を生成します。
## performPersonFeature as Person ## performPersonFeature as Staff ## performPersonFeature as Supplier
例42-18 異なるエンティティ・サブタイプでの新しいビュー行の作成
// In TestEntityPolymorphism.java PersonListRow newPerson = (PersonListRow)Personlist.createRow(); PersonListRow newStaff = Personlist.createStaffRow(); PersonListRow newSupplier = Personlist.createSupplierRow(); newPerson.performPersonFeature(); newStaff.performPersonFeature(); newSupplier.performPersonFeature();
42.6項「ビュー・オブジェクトを使用した複数の行タイプの処理」の例では、ポリモフィズムはエンティティ・オブジェクト・レベルで目に見えずに行われていました。クライアント・コードは同じPersonListRow
インタフェースを使用してすべてのビュー行を処理するので、Staff
エンティティ・オブジェクトに基づく行と、Persons
エンティティ・オブジェクトに基づく行を区別することはできません。コードは、基礎となるエンティティ・サブタイプのすべての型に共通するビュー行属性とメソッドの同じセットを使用して、すべてのビュー行を処理します。
多相ビュー行をサポートするようにビュー・オブジェクトを構成すると、クライアントは、行の型に固有のビュー行インタフェースを使用して、異なる型のビュー行を処理できます。このようにすると、クライアントは、必要に応じて、特定のサブタイプに固有のビュー属性にアクセスしたり、ビュー行メソッドを呼び出したりできます。図42-13は、前述のPersonList
の例に対してこの機能を有効にするビュー・オブジェクトの階層を示しています。SupplierList
とStaffList
は、ベースのPersonList
ビュー・オブジェクトを拡張するビュー・オブジェクトです。それぞれが、エンティティ・オブジェクトの慣用名として保持するPerson
のサブタイプに固有の追加属性を含んでいることに注意してください。SupplierList
はContractExpires
追加属性を、StaffList
はDiscountEligible
追加属性を含んでいます。次の項で説明するように、クライアントがビュー行ポリモフィズム用に構成されていると、次のものを使用してPersonList
ビュー・オブジェクトの結果を処理できます。
個人に関係するビュー行のPersonListRow
インタフェース
サプライヤに関係するビュー行のSupplierListRow
インタフェース
スタッフに関係するビュー行のStaffListRow
インタフェース
これにより、クライアントは、特定のサブタイプのビュー行に固有の追加属性とビュー行メソッドにアクセスできます。
多相ビュー行を持つビュー・オブジェクトを作成するには、次の手順を使用します。
アプリケーション・ナビゲータで、基にするビュー・オブジェクトをダブルクリックします。
前の例では、PersonList
ビュー・オブジェクトがベースです。
概要エディタで「属性」ナビゲーション・タブをクリックし、ビュー行の識別子属性を選択して、「詳細」タブをクリックします。
「詳細」セクションで、識別子属性にデフォルト値を指定し、「多相識別子」チェック・ボックスを選択して、使用するビュー行インタフェースを識別するものとして属性をマークします。
ベース・ビュー・オブジェクトのビュー行インタフェースが使用するものと予想される属性値に一致する値を、「サブタイプ値」フィールドで指定する必要があります。たとえば、PersonList
ビュー・オブジェクトでは、PersonTypeCode
属性を識別子属性としてマークし、デフォルトのサブタイプ値をperson
にします。
ベース・ビュー・オブジェクトのカスタム・ビュー行クラスを有効にし、少なくとも1つのメソッドをクライアント行インタフェースで公開します。このメソッドとしては、ビュー行属性アクセッサ・メソッドの1つまたは全部、および任意のカスタム・ビュー行メソッドを使用できます。
ベース・ビュー・オブジェクトを拡張する新しいビュー・オブジェクトを作成します。
前の例では、SupplierList
がベース・ビュー・オブジェクトPersonList
を拡張します。
拡張されたビュー・オブジェクトのカスタム・ビュー行クラスを有効にします。
妥当な場合は、新しいカスタム・ビュー行メソッドを追加するか、または親ビュー・オブジェクトの行クラスから継承するカスタム・ビュー行メソッドをオーバーライドします。
拡張されたビュー・オブジェクトの識別子属性に個別の値を設定します。
SupplierList
ビュー・オブジェクトは、PersonTypeCode
識別子属性にSUPP
という値を設定しています。
必要に応じて、手順4から6を繰り返し、他の拡張されたビュー・オブジェクトを追加します。
たとえば、StaffList
ビュー・オブジェクトはPersonList
を拡張する2番目のものです。このオブジェクトは、PersonTypeCode
識別子属性に値STAFF
を設定します。
ビュー・オブジェクトの階層を設定した後、ビュー行ポリモフィズムに参加するビュー・オブジェクト・サブタイプのリストを定義する必要があります。これは次のようにして行います。
階層内のビュー・オブジェクトの各型のインスタンスを、アプリケーション・モジュールのデータ・モデルに追加します。
たとえば、例に示したPersonModule
アプリケーション・モジュールには、PersonList
、SupplierList
およびStaffList
ビュー・オブジェクトのインスタンスがあります。
アプリケーション・モジュールの概要エディタで「データ・モデル」ナビゲーション・タブをクリックし、「サブタイプ」ボタンをクリックします。
表示される「サブタイプ」ダイアログで、ビュー行ポリモフィズムに加えるビュー・オブジェクト・サブタイプを「使用可能」から「選択済」リストに移動し、「OK」をクリックします。
この項では、多相ビュー行の使用に役立つ追加情報を提供します。
拡張されたビュー・オブジェクトを作成すると、そのオブジェクトは親のエンティティ・オブジェクトの慣用名を継承します。親のビュー・オブジェクトのエンティティ・オブジェクトの慣用名がドメイン・レイヤーのサブタイプを持つエンティティ・オブジェクトに基づいている場合は、継承した親のエンティティ・オブジェクトの慣用名型ではなく、これらのサブタイプのいずれかを拡張されたビュー・オブジェクトが使用するようにする場合があります。このようにするには2つの理由があります。
エンティティ・サブタイプに固有の属性を選択するため
エンティティ・サブタイプに固有のメソッドに委譲するビュー行メソッドを作成できるようにするため
これを行うには、継承されたエンティティ・オブジェクトの慣用名をオーバーライドして、目的のエンティティ・サブタイプを参照する必要があります。そのためには、拡張されたビュー・オブジェクトの概要エディタで次の手順を行います。
アプリケーション・ナビゲータで、ビュー・オブジェクトをダブルクリックします。
概要エディタで「エンティティ・オブジェクト」ナビゲーション・タブをクリックして、拡張されたエンティティ・オブジェクトの慣用名を使用していることを確認します。
たとえば、PersonList
ビュー・オブジェクトを拡張するSupplierList
ビュー・オブジェクトを作成するときは、最初、ThePerson
という別名を使用するエンティティ・オブジェクトの慣用名が、「選択済」リストに、「ThePerson(Person): 拡張済」のように表示されます。エンティティ・オブジェクトの慣用名の型はカッコ内で示され、「拡張済」というラベルはエンティティ・オブジェクトの慣用名が現在は親から継承されていることを示します。
「使用可能」リストで、継承されたものをオーバーライドする目的のエンティティ・サブタイプを選択します。既存のエンティティ・オブジェクトの慣用名型のサブタイプ・エンティティである必要があります。
たとえば、Persons
エンティティ型に基づいて継承されたエンティティ・オブジェクトの慣用名をオーバーライドするには、「使用可能」リストでSupplier
エンティティ・オブジェクトを選択します。
「>」をクリックして、「選択済」リストに移動します。
表示される警告に応答し、既存の継承されたエンティティ・オブジェクトの慣用名をオーバーライドすることを確認します。
この手順を実行すると、「選択済」リストが更新されて、オーバーライドされたエンティティ・オブジェクトの慣用名を反映するようになります。たとえば、SupplierList
ビュー・オブジェクトの場合は、Persons
ベースのエンティティ・オブジェクトの慣用名をSupplier
エンティティ・サブタイプでオーバーライドすると、表示が「ThePerson(Supplier): 上書き済」に更新されます。
エンティティ・オブジェクトの慣用名をエンティティ・サブタイプに関係するようにオーバーライドした後は、エディタの「属性」タブを使用して、そのサブタイプに固有の追加属性を選択できます。たとえば、SupplierList
ビュー・オブジェクトには、Supplier
エンティティ・オブジェクトに固有の、ContractExpires
という名前の追加属性が含まれます。
拡張されたビュー・オブジェクトのエンティティ・オブジェクトの慣用名をサブタイプ・エンティティを参照するようにオーバーライドした後は、サブタイプ・エンティティ・クラスに固有のメソッドに委譲するビュー行メソッドを作成できます。例42-19は、SupplierList
ビュー・オブジェクトに対するカスタム・ビュー行クラスのperformSupplierFeature()
メソッドのコードを示しています。getThePerson()
エンティティ行アクセッサからの戻り値をサブタイプSupplierImpl
にキャストした後、Supplier
エンティティ・オブジェクトに固有のperformSupplierFeature()
メソッドを呼び出します。
例42-19 サブタイプ・エンティティのメソッドに委譲するビュー行メソッド
// In SupplierListRowImpl.java public void performSupplierFeature() { SupplierImpl supplier = (SupplierImpl)getThePerson(); supplier.performSupplierFeature(); }
注意: JDeveloperでは、 |
例42-20は、次の手順を実行するTestViewRowPolymorphism
クラスのコード行を示しています。
PersonList
ビュー・オブジェクトの行を反復処理します。
ループでは、各行について、Javaのinstanceof
演算子を使用して、現在の行がStaffListRow
またはSupplierListRow
のインスタンスかどうかを検査しています。
行がStaffListRow
の場合は、行をこのさらに具体的な型にキャストした後、次の処理を行います。
StaffListRow
インタフェースに固有のperformStaffFeature()
メソッドを呼び出します。
StaffList
ビュー・オブジェクトに固有のDiscountEligible
属性の値にアクセスします。
行がSupplierListRow
の場合は、行をこのさらに具体的な型にキャストした後、次の処理を行います。
SupplierListRow
インタフェースに固有のperformSupplierFeature()
メソッドを呼び出します。
SupplierList
ビュー・オブジェクトに固有のContractExpires
属性の値にアクセスします。
それ以外の場合は、PersonListRow
でメソッドを呼び出します。
例42-20 クライアント・コードでのビュー行ポリモフィズムの使用
// In TestViewRowPolymorphism.java ViewObject vo = am.findViewObject("PersonList"); vo.executeQuery(); // 1. Iterate over the rows in the PersonList view object while (vo.hasNext()) { PersonListRow Person = (PersonListRow)vo.next(); System.out.print(Person.getEmail()+"->"); if (Person instanceof StaffListRow) { // 2. If the row is a StaffListRow, cast it StaffListRow mgr = (StaffListRow)Person; mgr.performStaffFeature(); System.out.println("Discount Status: "+staff.getDiscountEligible()); } else if (Person instanceof SupplieristRow) { // 3. If the row is a StaffListRow, cast it SupplierListRow tech = (SupplierListRow)Person; supplier.performSupplierFeature(); System.out.println("Contract expires: "+tech.getContractExpires()); } else { // 4. Otherwise, just call a method on the PersonListRow Person.performPersonFeature(); } }
例42-20のコードを実行すると、次のような出力が生成されます。
daustin->## performSupplierFeature called Contract expires: 2006-05-09 hbaer->## performPersonFeature as Person : sking->## performStaffFeature called Discount Status: Y :
これは、ビュー行ポリモフィズムの機能を使用することで、クライアントが異なる型のビュー行を区別し、ビュー行の各サブタイプに固有のメソッドと属性にアクセスできたことを示します。
通常は一緒に使用する方が役に立ちますが、ビュー行ポリモフィズム機能と多相エンティティ・オブジェクト慣用名機能は異なるものであり、個別に使用できます。特に、ビュー行ポリモフィズムの機能は、読取り専用ビュー・オブジェクトにも、エンティティ・ベースのビュー・オブジェクトにも使用できます。両方のメカニズムを組み合せると、多相のエンティティ行部分とビュー行型の両方を持つことができます。
ビュー・オブジェクトまたはエンティティ・オブジェクトでビュー行ポリモフィズムを使用するには、それぞれに個別の識別子属性プロパティを構成する必要があることに注意してください。これが必要になるのは、読取り専用ビュー・オブジェクトには、識別子情報を推測する関連エンティティ・オブジェクトの慣用名が含まれていないためです。
つまり、ビュー行ポリモフィズムを使用するには、次のことが必要です。
継承階層のルート・ビュー・オブジェクトのビュー・オブジェクト・レベルで、識別子にする属性を構成します。
階層の継承されるビュー・オブジェクトのそれぞれで、そのビュー・オブジェクト・レベルの識別子属性のサブタイプ値プロパティに、個別の値を設定します(ビュー・オブジェクト定義ファイルの属性のDefaultValue
として識別される)。
アプリケーション・モジュールのサブタイプのリストで、この階層内にあるサブクラス化されたビュー・オブジェクトのリストを作成します。
一方、多相エンティティ・オブジェクトの慣用名を含むビュー・オブジェクトを作成するには、次のことを行います。
継承階層のルート・エンティティ・オブジェクトのエンティティ・オブジェクト・レベルで、識別子にする属性を構成します。
階層の継承されるエンティティ・オブジェクトのそれぞれで、オーバーライドし、そのエンティティ・オブジェクト・レベルの識別子属性のサブタイプ値プロパティに個別の値を設定します。
ビュー・オブジェクトのサブタイプのリストで、サブクラス化されたエンティティ・オブジェクトのリストを作成します。
Worldwide Web Consortium(W3C)が定めるExtensible Markup Language(XML)標準では、電子データ交換用の言語に依存しない方式が定義されています。厳密な一連の規則により、判読可能なテキスト・ドキュメントを使用して、データに固有の構造を簡単にエンコードし、正確に解釈できます。
ビュー・オブジェクトでは、問い合されたデータに基づいて、XMLドキュメントを記述することができます。また、XMLドキュメントを読み取って、挿入、更新、削除などの変更をデータに適用することもできます。ビュー・リンクを導入すると、このXML機能は、任意の複雑さのマスター/ディテール階層の複数レベルでネストされた情報の読取りと書込みをサポートします。ビュー・オブジェクトが生成および使用するXMLは正規書式に従いますが、ビュー・オブジェクトのXML機能とXML Stylesheet Language Transformations(XSLT)を組み合せることで、正規のXML書式と使用する必要のある任意の書式の間の変換を簡単に行うことができます。
注意: この項の例では、Fusion Order Demoアプリケーションの |
ビュー・オブジェクトからXMLを生成するために、writeXML()
メソッドを使用します。2種類の方法で生成されるXMLを制御できます。
生成されるXMLを正確に制御するには、ネストされた詳細情報に対してアクセスする必要のあるビュー・リンク・アクセッサ属性など、使用する必要のある属性を示すビュー・オブジェクト属性マップを指定できます。
Node writeXML(long options, HashMap voAttrMap)
すべての属性を含むXMLを生成するには、結果を生成するためにトラバースする必要のあるビュー・リンク・アクセッサ属性のレベル数を示す深さレベルを指定するだけで済みます。
Node writeXML(int depthCount, long options)
options
パラメータは整数のフラグ・フィールドで、次のビット・フラグのいずれかを設定できます。
XMLInterface.XML_OPT_ALL_ROWS
設定するビュー・オブジェクトのすべての行をXMLに含めます。
XMLInterface.XML_OPT_LIMIT_RANGE
現在の範囲内の行のみをXMLに含めます。
現在のトランザクションにあるポストされていない新しい行をXML出力に含める場合は、論理OR演算子を使用して、前記のフラグとXMLInterface.XML_OPT_ASSOC_CONSISTENT
フラグを組み合せることができます。
どちらのバージョンのwriteXML()
メソッドも、オプションで(指定された場合には)、返される前のXML出力を変換するために使用されるXSLTスタイルシートとして第3の引数を受け取ります。
また、両方のバージョンのwriteXML()
メソッドで、引数depthCount=ignore
を設定して、深さカウントを無視し、指定されたoptions
パラメータ・フラグに基づくデータ・モデルにあるもののみをレンダリングすることを指示できます。
writeXML()
を使用してXMLを生成すると、ビュー・オブジェクトは最初に、ラップするXML要素を作成します。そのデフォルト名は、ビュー・オブジェクト定義の名前と一致しています。たとえば、devguide.advanced.xml.queries
パッケージのPersons
ビュー・オブジェクトの場合は、生成されるXMLをラップする一番外側のタグはPersonsタグになります。
次に、ビュー・オブジェクトは、適切な行の属性データをXML要素に変換します。デフォルトでは、各行のデータは、ビュー・オブジェクトの名前に接尾辞Row
を付加した名前の行要素でラップされます。たとえば、Persons
という名前のビュー・オブジェクトの各データ行は、PersonsRow要素でラップされます。各行の属性データを表す要素は、この行要素内にネストされた子として記述されます。
いずれかの属性がビュー・リンク・アクセッサ属性であり、writeXML()
に渡されたパラメータによって有効になっている場合は、ビュー・リンク・アクセッサによって返されるディテール行セットのデータも含められます。このネストされたデータは、ビュー・リンク・アクセッサ属性の名前によって名前が決まる要素でラップされます。writeXML()
メソッドの戻り値は、W3Cの標準のNode
インタフェースを実装するオブジェクトで、生成されたXMLのルート要素を表します。
注意:
|
たとえば、Persons
ビュー・オブジェクト・インスタンスのすべての行に対するXML要素を生成し、存在する最大レベルの深さまでビュー・リンク・アクセッサをたどる場合は、例42-21に示すようなコードが必要です。
例42-21 ビュー・オブジェクトの全行と全ビュー・リンク・レベルのXMLの生成
ViewObject vo = am.findViewObject("PersonsView"); printXML(vo.writeXML(-1,XMLInterface.XML_OPT_ALL_ROWS));
Persons
ビュー・オブジェクトは、その個人が作成したオーダーを表すOrders
ビュー・オブジェクトにリンクされています。そして、Orders
ビュー・オブジェクトは、顧客がオーダーした品目の詳細を提供するOrderItems
ビュー・オブジェクトにリンクされています。例42-21のコードを実行すると、ビュー・リンクによって定義されているネストされた構造を反映した、例42-22で示されるXMLが生成されます。
例42-22 Personsビュー・オブジェクトと2レベルのビュー・リンクされたディテールから生成されるXML
... <PersonsViewRow> <PersonId>111</PersonId> <PrincipalName>ISCIARRA</PrincipalName> <FirstName>Ismael</FirstName> <LastName>Sciarra</LastName> <PersonTypeCode>CUST</PersonTypeCode> <ProvisionedFlag>N</ProvisionedFlag> <PrimaryAddressId>42</PrimaryAddressId> <MembershipId>2</MembershipId> <Email>ISCIARRA</Email> <ConfirmedEmail>ISCIARRA</ConfirmedEmail> <PhoneNumber>228.555.0126</PhoneNumber> <DateOfBirth>1971-09-30</DateOfBirth> <MaritalStatusCode>SING</MaritalStatusCode> <Gender>M</Gender> <ContactableFlag>Y</ContactableFlag> <ContactByAffilliatesFlag>Y</ContactByAffilliatesFlag> <CreatedBy>SEED_DATA</CreatedBy> <CreationDate>2008-08-15 11:26:36.0</CreationDate> <LastUpdatedBy>SEED_DATA</LastUpdatedBy> <LastUpdateDate>2008-08-15 11:26:36.0</LastUpdateDate> <ObjectVersionId>1</ObjectVersionId> <OrdersView> <OrdersViewRow> <OrderId>1017</OrderId> <OrderDate>2008-08-06 11:28:26.0</OrderDate> <OrderStatusCode>STOCK</OrderStatusCode> <OrderTotal>1649.92</OrderTotal> <CustomerId>111</CustomerId> <ShipToAddressId>8</ShipToAddressId> <ShippingOptionId>2</ShippingOptionId> <PaymentOptionId>1006</PaymentOptionId> <DiscountId>3</DiscountId> <FreeShippingFlag>Y</FreeShippingFlag> <CustomerCollectFlag>Y</CustomerCollectFlag> <CollectionWarehouseId>102</CollectionWarehouseId> <GiftwrapFlag>N</GiftwrapFlag> <CreatedBy>0</CreatedBy> <CreationDate>2008-08-15 11:28:26.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2008-08-15 11:28:26.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> <OrderItemsView> <OrderItemsViewRow> <OrderId>1017</OrderId> <LineItemId>1</LineItemId> <ProductId>22</ProductId> <Quantity>1</Quantity> <UnitPrice>199.95</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2008-08-15 11:32:26.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2008-08-15 11:32:26.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> <OrderItemsViewRow> <OrderId>1017</OrderId> <LineItemId>2</LineItemId> <ProductId>9</ProductId> <Quantity>1</Quantity> <UnitPrice>129.99</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2008-08-15 11:32:27.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2008-08-15 11:32:27.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> <OrderItemsViewRow> <OrderId>1017</OrderId> <LineItemId>3</LineItemId> <ProductId>36</ProductId> <Quantity>2</Quantity> <UnitPrice>659.99</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2008-08-15 11:32:27.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2008-08-15 11:32:27.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> </OrderItemsView> </OrdersViewRow> </OrdersView> </PersonsViewRow> ...
この項では、XMLの使用に役立つ追加情報を提供します。
ビュー・オブジェクトの正規XML書式で使用されるデフォルトのXML要素名は、プロパティ・インスペクタを使用していくつかプロパティを設定することで変更できます。これを実現するには、ビュー・オブジェクトの概要エディタを開き、次の操作を行います。
「属性」ページで属性を選択し、「プロパティ・インスペクタ」で「カスタム・プロパティ」ナビゲーション・タブを選択し、カスタム属性レベルのプロパティXml Elementに値SomeOtherName
を設定すると、その属性に使用されるXML要素の名前が<SomeOtherName>
に変わります。
たとえば、Persons
ビュー・オブジェクトのEmail
属性で定義されているこのプロパティは、例42-22ではXML要素を<Email>
から<EmailAddress>
に変更しています。
「プロパティ・インスペクタ」で「一般」ナビゲーション・タブを選択し、カスタム・ビュー・オブジェクト・レベルのプロパティXml Row Elementに値SomeOtherRowName
を設定すると、そのビュー・オブジェクトに使用されるXML要素の名前が<SomeOtherRowName>
に変わります。
たとえば、Persons
ビュー・オブジェクトで定義されているこのプロパティは、例42-22では行のXML要素名を<PersonsRow>
から<Person>
に変更しています。
ビュー・リンク・アクセッサ属性からのネストされた行セット・データをラップする要素の名前を変更するには、「ビュー・リンク・プロパティ」ダイアログを使用します。ダイアログを開くには、ビュー・リンクの概要エディタで、「関連」ページの「アクセッサ」セクションにある「アクセッサの編集」アイコンをクリックします。「アクセッサ名」フィールドに、ビュー・リンク・アクセッサ属性の適切な名前を入力します。
デフォルトでは、ビュー行属性がnull
の場合、生成されるXMLから対応する要素が省略されます。概要エディタの「属性」ページで属性を選択し、「プロパティ・インスペクタ」で「カスタム・プロパティ」ナビゲーション・タブを選択し、カスタム属性レベルのプロパティXml Explicit Nullに任意の値(たとえば、true
やyes
)を設定すると、値がnullの属性についても要素が生成されます。たとえば、AssignedDate
という名前の属性にこのプロパティを設定すると、AssignedDateがnull
の行には、対応する要素AssignedDate null="true"/が生成されます。この動作をビュー・オブジェクトのすべての属性に適用する場合は、属性ごとにプロパティを定義するかわりに、ビュー・オブジェクト・レベルでXml Explicit Nullカスタム・プロパティを定義できます。
writeXML()
から返されるXML Node
オブジェクトに関する最も一般的な処理は次の2つです。
ネットワーク経由での送信やファイルへの保存などのために、ノードをシリアライズされたテキスト表現に印刷
生成されたXMLをW3C XPath式を使用して検索
残念ながら、標準のW3C Document Object Model(DOM)APIには、これらの役に立つ操作を行うためのメソッドは含まれません。しかし大丈夫です。ADF Business ComponentsはDOMのOracle XMLパーサーの実装を使用しているので、writeXML()
からのNode
戻り値をOracle固有のクラスであるXMLNode
やXMLElement
(oracle.xml.parser.v2
パッケージ内)にキャストして、次のような便利な追加機能にアクセスできます。
print()
メソッドを使用した、シリアライズされたテキスト形式へのXML要素の印刷
selectNodes()
メソッドを使用した、メモリー内のXML要素のXPath式による検索
valueOf()
メソッドを使用した、XML要素に関連するXPath式の値の検出
例42-23は、TestClientWriteXML
のprintXML()
メソッドです。Node
パラメータをXMLNode
にキャストし、print()
メソッドを呼び出してXMLをコンソールにダンプします。
生成されるXMLに含める属性をきめ細かく制御する必要がある場合は、HashMap
を受け取るバージョンのwriteXML()
メソッドを使用します。例42-24は、この手法を使用するTestClientWriteXML
クラスの行を示しています。HashMap
を作成した後、String[]
値のエントリを設定します。このエントリは、XMLに含める属性の名前であり、キーとしてはこれらの属性が属しているビュー定義の完全修飾名を使用します。例には、Persons
ビュー・オブジェクトの属性PersonId
、Email
、PersonTypeCode
およびOrdersView
と、OrdersView
ビュー・オブジェクトの属性OrderId
、OrderStatusCode
およびOrderTotal
が含まれます。
注意: 旧バージョンのADF Business Componentsとの互換性のため、 |
特定のビュー・オブジェクト・インスタンスのビュー行に対しては、次のような処理が行われます。
そのビュー・オブジェクトの完全修飾ビュー定義名とキーが一致する項目が属性マップに存在する場合は、対応するString
配列に名前のある属性のみがXMLに含められます。
さらに、文字列配列にビュー・リンク・アクセッサ属性の名前が含まれる場合は、そのディテール行セットのネストされた内容がXMLに含められます。ビュー・リンク・アクセッサ属性名が文字列配列に出現しない場合は、ディテール行セットの内容は含められません。
キーが一致する項目がマップ内に存在しない場合は、その行のすべての属性がXMLに含められます。
例42-24 生成されるXMLをきめ細かく制御するためのビュー定義属性マップの使用
HashMap viewDefMap = new HashMap(); viewDefMap.put("devguide.advanced.xml.queries.PersonsView", new String[]{"PersonId","Email", "PersonTypeCode", "OrdersView" /* View link accessor attribute */ }); viewDefMap.put("devguide.advanced.xml.queries.OrdersView", new String[]{"OrderId","OrderStatusCode","OrderTotal"}); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap));
例を実行すると、例42-25で示されるXMLが生成されます。これには、提供された属性マップで指定されている属性とビュー・リンク・アクセッサのみが含まれます。
例42-25 属性マップを使用して生成されたUsersビュー・オブジェクトからのXML
<OrdersViewRow> <OrderId>1033</OrderId> <OrderDate>2009-01-30 13:59:39.0</OrderDate> <OrderShippedDate>2009-02-02 13:59:39.0</OrderShippedDate> <OrderStatusCode>COMPLETE</OrderStatusCode> <OrderTotal>2677.96</OrderTotal> <CustomerId>108</CustomerId> <ShipToAddressId>20</ShipToAddressId> <ShippingOptionId>1</ShippingOptionId> <PaymentOptionId>1016</PaymentOptionId> <DiscountId>3</DiscountId> <FreeShippingFlag>Y</FreeShippingFlag> <CustomerCollectFlag>Y</CustomerCollectFlag> <CollectionWarehouseId>101</CollectionWarehouseId> <GiftwrapFlag>N</GiftwrapFlag> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:39.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:39.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> <OrderItemsView> <OrderItemsViewRow> <OrderId>1033</OrderId> <LineItemId>1</LineItemId> <ProductId>10</ProductId> <Quantity>3</Quantity> <UnitPrice>225.99</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:40.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:40.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> <OrderItemsViewRow> <OrderId>1033</OrderId> <LineItemId>2</LineItemId> <ProductId>1</ProductId> <Quantity>1</Quantity> <UnitPrice>1999.99</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:40.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:40.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> </OrderItemsView> </OrdersViewRow> ...
ビュー・オブジェクトが、双方向に構成されたビュー・リンクを通して関連付けられている場合は、属性マップを使用するwriteXML()
方式を使用する必要があります。双方向ビュー・リンクの状況でwriteXML()
方式を使用し、最大深度に-1
を指定して存在するビュー・リンクの全レベルを含めるようにすると、writeXML()
メソッドは、双方向のビュー・リンクを前後に移動して無限ループに陥り、メモリーを使い果すまで重複したデータを含む深くネストしたXMLを生成します。このような場合は、かわりに属性マップを指定してwriteXML()
を使用してください。この方法を使用すること以外に、XMLに含める、または含めないビュー・リンク・アクセッサを制御し、XML生成中の無限再帰を回避する方法はありません。
writeXML()
によって生成される正規のXML書式が要件を満たさない場合は、オプションの引数として、XSLTスタイルシートを提供できます。通常と同じようにXMLが生成されますが、最終的なXMLが呼出し元に返される前に、指定したスタイルシートを使用して結果の変換が行われます。
例42-26に示すようにXSLTスタイルシートについて考えます。これは、例42-25で生成されるXMLのルート要素と一致して、新しいCustomerEmailAddresses要素を結果に作成する、テンプレートが1つのみの簡単な変換です。xsl:for-each命令を使用して、ネストされたOrdersViews要素の中で複数のOrdersViewRow子要素を含むすべてのPersonsView要素を処理します。対象であることが確認されたPersonsView要素ごとに、結果にCustomer要素を作成し、そのContact
属性には、PersonsViewのEmail子要素の値を移入します。
例42-26 生成されたXMLを別の書式に変換するXSLTスタイルシート
<?xml version="1.0" encoding="windows-1252" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <CustomerEmailAddresses> <xsl:for-each select="/PersonsView/PersonsViewRow[count(OrdersView/OrdersViewRow) > 1]"> <xsl:sort select="Email"/> <Customer Contact="{Email}"/> </xsl:for-each> </CustomerEmailAddresses> </xsl:template> </xsl:stylesheet>
例42-27は、writeXML()
を呼び出すとこのXSLTスタイルシートを実行するTestClientWriteXML
クラスの行です。
例42-27 結果のXMLを変換するためのwriteXML()へのXSLTスタイルシートの引渡し
// In TestClientWriteXML.java XSLStylesheet xsl = getXSLStylesheet(); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap,xsl));
例42-27のコードを実行すると、次のような変換されたXMLが生成されます。
<CustomerEmailAddresses> <Customer Contact="dfaviet"/> <Customer Contact="jchen"/> <Customer Contact="ngreenbe"/> </CustomerEmailAddresses>
例42-28で示されているgetXSLStylesheet()
ヘルパー・メソッドは、実行時にクラスパスからXSLTスタイルシートなどのリソースを読み込む方法を学習するためのよい例です。このコードでは、Example.xsl
スタイルシートがTestClientWriteXML
クラスと同じディレクトリにあるものと想定しています。.class
演算子を使用してTestClientWriteXML
クラスのClass
オブジェクトを参照することで、コードはgetResource()
メソッドを使用してリソースに対するURL
を取得します。その後、URLをXSLProcessor
クラスのnewXSLStylesheet()
メソッドに渡して、返すための新しいXSLStylesheet
オブジェクトを作成します。このオブジェクトは、*.xsl
ファイルから読み込まれたXSLTスタイルシートのコンパイル済バージョンを表しています。
例42-28 クラスパスからのリソースとしてのXSLTスタイルシートの読込み
private static XSLStylesheet getXSLStylesheet() throws XMLParseException, SAXException,IOException,XSLException { String xslurl = "Example.xsl"; URL xslURL = TestClientWriteXML.class.getResource(xslurl); XSLProcessor xslProc = new XSLProcessor(); return xslProc.newXSLStylesheet(xslURL); }
注意: XSLTスタイルシートのように、コンパイル済のJavaクラスやXMLメタデータとともに出力ディレクトリに格納するリソースを扱うときは、「プロジェクト・プロパティ」ダイアログの「コンパイラ」ページで、「ファイル・タイプの出力ディレクトリへのコピー」フィールドを更新し、セミコロンで区切られたリストに |
ビュー・オブジェクトがXMLドキュメントを使用して挿入、更新、および削除を行うようにするには、readXML()
メソッドを使用します。
void readXML(Element elem, int depthcount)
readXML()
が受け入れる正規の書式は、同じビュー・オブジェクトでwriteXML()
メソッドを呼び出すと生成されるものと同じです。処理するXMLドキュメントが正規の書式に対応していない場合は、readXML()
に対するオプションの第3引数としてXSLTスタイルシートを提供し、ファイルを読み込んで処理する前に、入力XMLドキュメントを正規の書式に変換できます。
ビュー・オブジェクトは、正規の書式のXMLドキュメントを使用するとき、ドキュメントを処理して、行要素、その属性要素の子、およびビュー・リンク・アクセッサ属性を表すネストされた要素を認識します。depthcount
パラメータで指定されている最大レベルまで、再帰的にドキュメントを処理します。XMLドキュメントの全レベルを処理するよう要求するには、depthcount
に-1
を渡します。
認識する行要素ごとに、readXML()
メソッドは次の処理を行います。
行を処理するために関連するビュー・オブジェクトを識別します。
子属性要素を読み取り、行の主キー属性の値を取得します。
主キー属性を使用してfindByKey()
を実行し、行がすでに存在するかどうかを判定します。
行が存在する場合は、次の処理を行います。
行要素にマーカー属性bc4j-action="remove"
が含まれる場合は、既存の行を削除します。
それ以外の場合は、XMLの現在の行要素の、属性要素の子の値を使用して、行の属性を更新します。
行が存在しない場合は、新しい行を作成し、ビュー・オブジェクトの行セットに挿入します。その属性は、XMLの現在の行要素の、属性要素の子の値を使用して移入します。
Row
オブジェクトに対しても、同じreadXML()
メソッドを使用できます。このメソッドが受け入れる正規のXMLの書式は、同じ行に対してwriteXML()
を呼び出すと生成される書式と同じです。行でreadXML()
メソッドを呼び出し、次の処理を行うことができます。
属性の値をXMLで更新します。
対応する行要素にbc4j-action="remove"
マーカー属性がある場合は、行を削除します。
ビュー・リンク・アクセッサを介して、ネストされた行を挿入、更新、または削除します。
例42-29に示すようにXMLドキュメントについて考えます。これは、PersonsView
ビュー・オブジェクトの単一行で期待される正規の書式です。ルートのPersonsViewRow要素の内部にネストされているConfirmedEmail属性は、顧客の電子メール・アドレスを表します。ネストされたOrdersView要素はOrders
ビュー・リンク・アクセッサ属性に対応し、3つのOrdersViewRow要素を含みます。それぞれが、OrdersView
行の主キーを表すOrderId要素を含みます。
例42-29 行を挿入、更新、および削除するための正規の書式のXMLドキュメント
<PersonsViewRow> <!-- This will update Person's ConfirmedEmail attribute --> <PersonId>110</PersonId> <ConfirmedEmail>NewConfirmed</ConfirmedEmail> <OrdersView> <!-- This will be an update since it does exist --> <OrdersViewRow> <OrderId>1011</OrderId> <OrderStatusCode>SHIP</OrderStatusCode> </OrdersViewRow> <!-- This will be an insert since it doesn't exist --> <OrdersViewRow> <OrderId>1070</OrderId> <OrderStatusCode>PENDING</OrderStatusCode> </OrdersViewRow> <!-- This will be deleted --> <OrdersViewRow bc4j-action="remove"> <OrderId>1026</OrderId> </OrdersViewRow> </OrdersView> </PersonsViewRow>
例42-30は、TestClientReadXML
クラスのコード行を示しています。このクラスは、PersonsView
ビュー・オブジェクトの特定の行に、このXMLデータグラムを適用します。TestClientReadXML
クラスが実行する基本的な手順は次のとおりです。
キー(顧客の場合はjchenなど)を使用して対象の行を検索します。
変更を適用する前に、行に対して生成されたXMLを表示します。
ヘルパー・メソッドを使用して適用する変更を含む解析されたXMLドキュメントを取得します。
行に変更を適用するためにXMLドキュメントを読み込みます。
適用された保留中変更を含むXMLを表示します。
TestClientReadXML
クラスは、42.7.1項「問合せ済データのXMLの生成方法」で説明されているXMLInterface.XML_OPT_ASSOC_CONSISTENT
フラグを使用して、ポストされていない新しい行をXMLに含めています。
例42-30 readXML()による既存行への変更の適用
ViewObject vo = am.findViewObject("CustomersView"); Key k = new Key(new Object[] { 110 }); // 1. Find a target row by key (e.g. for customer "jchen") Row jchen = vo.findByKey(k, 1)[0]; // 2. Show the XML produced for the row before changes are applied printXML(jchen.writeXML(-1, XMLInterface.XML_OPT_ALL_ROWS)); // 3. Obtain parsed XML document with changes to apply using helper method Element xmlToRead = getInsertUpdateDeleteXMLGram(); printXML(xmlToRead); // 4. Read the XML document to apply changes to the row jchen.readXML(getInsertUpdateDeleteXMLGram(), -1); // 5. Show the XML with the pending changes applied printXML(jchen.writeXML(-1, XMLInterface.XML_OPT_ALL_ROWS | XMLInterface.XML_OPT_ASSOC_CONSISTENT));
例42-30のコードを実行すると、最初に、John Chenの変更前の情報が表示されます。次の点に注意してください。
ConfirmedEmail
属性の値は"JCHEN"です。
オーダー1011
のステータス・コードのレベルは"CANCEL"です。
オーダー1026
にはオーダー行があり、
オーダー1070
に関連するオーダー行はありません。
<PersonsViewRow> <PersonId>110</PersonId> <PrincipalName>JCHEN</PrincipalName> <FirstName>John</FirstName> <LastName>Chen</LastName> <PersonTypeCode>CUST</PersonTypeCode> <ProvisionedFlag>N</ProvisionedFlag> <PrimaryAddressId>37</PrimaryAddressId> <MembershipId>1</MembershipId> <Email>JCHEN</Email> <ConfirmedEmail>JCHEN</ConfirmedEmail> <PhoneNumber>706.555.0103</PhoneNumber> <DateOfBirth>1967-09-28</DateOfBirth> <MaritalStatusCode>MARR</MaritalStatusCode> <Gender>M</Gender> <ContactableFlag>Y</ContactableFlag> <ContactByAffilliatesFlag>Y</ContactByAffilliatesFlag> <CreatedBy>SEED_DATA</CreatedBy> <CreationDate>2009-02-23 13:59:38.0</CreationDate> <LastUpdatedBy>SEED_DATA</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:38.0</LastUpdateDate> <ObjectVersionId>1</ObjectVersionId> <OrdersView> <OrdersViewRow> <OrderId>1011</OrderId> <OrderDate>2009-02-17 13:59:38.0</OrderDate> <OrderStatusCode>CANCEL</OrderStatusCode> <OrderTotal>99.99</OrderTotal> <CustomerId>110</CustomerId> <ShipToAddressId>9</ShipToAddressId> <ShippingOptionId>2</ShippingOptionId> <PaymentOptionId>1005</PaymentOptionId> <DiscountId>5</DiscountId> <FreeShippingFlag>N</FreeShippingFlag> <CustomerCollectFlag>N</CustomerCollectFlag> <GiftwrapFlag>N</GiftwrapFlag> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:38.0</CreationDate> <LastUpdatedBy>anonymous</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:38.0</LastUpdateDate> <ObjectVersionId>8</ObjectVersionId> <OrderItemsView> <OrderItemsViewRow> <OrderId>1011</OrderId> <LineItemId>1</LineItemId> <ProductId>18</ProductId> <Quantity>1</Quantity> <UnitPrice>99.99</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:39.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:39.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> </OrderItemsView> </OrdersViewRow> ...
readXML()
を使用してXMLドキュメントの変更を行に適用した後、writeXML()
を使用して再びXMLを表示すると、次のようになります。
ConfirmedEmail
は"NewConfirmed"です。
オーダー1070
の新規オーダー行が作成されました。
オーダー1011
のステータス・コードのレベルは"SHIP"で、
オーダー1026
のオーダー行は削除されました。
<PersonsViewRow> <PersonId>110</PersonId> <PrincipalName>JCHEN</PrincipalName> <FirstName>John</FirstName> <LastName>Chen</LastName> <PersonTypeCode>CUST</PersonTypeCode> <ProvisionedFlag>N</ProvisionedFlag> <PrimaryAddressId>37</PrimaryAddressId> <MembershipId>1</MembershipId> <Email>JCHEN</Email> <ConfirmedEmail>NewConfirmed</ConfirmedEmail> <PhoneNumber>706.555.0103</PhoneNumber> <DateOfBirth>1967-09-28</DateOfBirth> <MaritalStatusCode>MARR</MaritalStatusCode> <Gender>M</Gender> <ContactableFlag>Y</ContactableFlag> <ContactByAffilliatesFlag>Y</ContactByAffilliatesFlag> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:38.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:38.0</LastUpdateDate> <ObjectVersionId>1</ObjectVersionId> <OrdersView> <OrdersViewRow> <OrderId>1070</OrderId> <OrderDate>2009-06-22</OrderDate> <OrderStatusCode>PENDING</OrderStatusCode> ... </OrdersViewRow> <OrdersViewRow> <OrderId>1011</OrderId> <OrderDate>2009-02-17 13:59:38.0</OrderDate> <OrderStatusCode>SHIP</OrderStatusCode> <OrderTotal>99.99</OrderTotal> <CustomerId>110</CustomerId> <ShipToAddressId>9</ShipToAddressId> <ShippingOptionId>2</ShippingOptionId> <PaymentOptionId>1005</PaymentOptionId> <DiscountId>5</DiscountId> <FreeShippingFlag>N</FreeShippingFlag> <CustomerCollectFlag>N</CustomerCollectFlag> <GiftwrapFlag>N</GiftwrapFlag> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-23 13:59:38.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-23 13:59:38.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> <OrderItemsView> <OrderItemsViewRow> <OrderId>1011</OrderId> <LineItemId>1</LineItemId> <ProductId>18</ProductId> <Quantity>1</Quantity> <UnitPrice>99.99</UnitPrice> <CreatedBy>0</CreatedBy> <CreationDate>2009-02-01 13:59:39.0</CreationDate> <LastUpdatedBy>0</LastUpdatedBy> <LastUpdateDate>2009-02-01 13:59:39.0</LastUpdateDate> <ObjectVersionId>0</ObjectVersionId> </OrderItemsViewRow> </OrderItemsView> </OrdersViewRow> ...
注意: この例では、 |
デフォルトのビュー・オブジェクトは、データベースからデータを読み取り、データベースの結果セットを処理するためにJava Database Connectivity (JDBC)レイヤーを使用するタスクを自動化します。これに対し、カスタムJavaクラスで適切なメソッドをオーバーライドすることにより、REF CURSOR
、メモリー内の配列、Javaの*.properties
ファイルなどの様々な代替データソースからプログラムでデータを取得するビュー・オブジェクトを作成できます。
プログラムで読取り専用のビュー・オブジェクトを作成するには、ビュー・オブジェクト作成ウィザードを使用します。
プログラムで読取り専用のビュー・オブジェクトを作成する手順:
アプリケーション・ナビゲータで、ビュー・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で「ビジネス層」を展開し、「ADF Business Components」、「ビュー・オブジェクト」を選択して、「OK」をクリックします。
ビュー・オブジェクト作成ウィザードの「名前」ページで、ビュー・オブジェクトの名前とパッケージを入力します。データ・ソースの場合、「問合せベースではなく、プログラムによって移入された行」を選択します。
「属性」ページで、「新規」を複数回クリックし、プログラムによるビュー・オブジェクトが必要とするビュー・オブジェクト属性を定義します。
「属性の設定」ページで、定義した属性に必要な設定を調節します。
「Java」ページで「ビュー・オブジェクト・クラスの生成」を選択して、カスタム・ビュー・オブジェクト・クラス(ViewObjImpl
)にコードを含められるようにします。
「終了」をクリックして、ビュー・オブジェクトを作成します。
ビュー・オブジェクトのカスタムJavaクラスで、42.8.3項「プログラムのビュー・オブジェクト用にオーバーライドする主要なフレームワーク・メソッド」で説明されているメソッドをオーバーライドし、独自のデータ取得方法を実装します。
プログラムでデータを取得するエンティティ・ベースのビュー・オブジェクトを作成するには、通常の方法でビュー・オブジェクトを作成し、カスタムJavaクラスを有効にし、次の項で説明されているメソッドをオーバーライドして独自のデータ取得方法を実装します。
プログラムによるビュー・オブジェクトでは、通常、ViewObjectImpl
ベース・クラスの次のメソッドをすべてオーバーライドして、データ取得の独自方法を実装します。
create()
このメソッドは、ビュー・オブジェクトのインスタンスが作成されるときに呼び出され、プログラムによるビュー・オブジェクトが必要とする任意の状態を初期化するために使用できます。このオーバーライドされるメソッドには、少なくとも次の行を組み込んで、プログラムによるビュー・オブジェクトが関係するSQL問合せをトレースしないようにします。
// Wipe out all traces of a query for this VO getViewDef().setQuery(null); getViewDef().setSelectClause(null); setQuery(null);
executeQueryForCollection()
このメソッドは、ビュー・オブジェクトの問合せを実行する(または再実行する)必要があるたびに呼び出されます。
hasNextForCollection()
このメソッドは、このビュー・オブジェクトから作成される行セットの行セット・イテレータでhasNext()
メソッドをサポートするために呼び出されます。実装では、プログラムのデータ・ソースから取得する行がまだある場合はtrue
を返します。
createRowFromResultSet()
このメソッドは、フェッチされたデータの各行を移入するために呼び出されます。実装では、createNewRowForCollection()
を呼び出して新しい空白の行を作成した後、populateAttributeForRow()
を呼び出して行に対するデータの各属性を移入します。
getQueryHitCount()
このメソッドは、getEstimatedRowCount()
メソッドをサポートするために呼び出されます。実装では、プログラムによるビュー・オブジェクトの問合せで取得される行数のカウントまたは予想されるカウントを返します。
protected void releaseUserDataForCollection()
コードでは、各行セットでユーザー・データ・コンテキスト・オブジェクトを格納および取得できます。このメソッドを呼び出すと、閉じられた行セットと関連付けられているリソースを解放できます。
ビュー・オブジェクト・コンポーネントは、実行時に複数のアクティブな行セットと関連付けられる可能性があるため、前記のフレームワーク・メソッドの多くは、qc
という名前のObject
パラメータを受け取ります。このパラメータで、フレームワークは、コードが設定していると予想される行のコレクションと、特定のコレクションに移入される行に影響する可能性のあるバインド変数値の配列を渡します。
行のコレクションごとにユーザー・データ・オブジェクトを格納できるので、カスタム・データ・ソースの実装は、必要なデータ・ソース・コンテキスト情報を関連付けることができます。フレームワークでは、このコレクションごとのコンテキスト情報を取得および設定するために、setUserDataForCollection()
メソッドとgetUserDataForCollection()
メソッドが提供されています。オーバーライドされたフレームワーク・メソッドのいずれかが呼び出されるたびに、getUserDataForCollection()
メソッドを使用して、フレームワークが移入を望んでいる行のコレクションと関連付けられた正しいResultSet
オブジェクトを取得できます。
以降の項の各例では、これらのメソッドをオーバーライドして、異なる種類のプログラムによるビュー・オブジェクトを実装しています。
アプリケーションでは、ストアド・プロシージャ内にカプセル化された問合せの結果を使用することが必要になる場合があります。PL/SQLを使用すると、カーソルを開いて問合せの結果を反復処理し、このカーソルへの参照をクライアントに返すことができます。このいわゆるREF CURSOR
は、クライアントが問合せの結果を反復処理するために使用できるハンドルです。これは、クライアントが元のSQL SELECT
文を実際に発行していない場合であっても可能です。
注意: この項の例では、Fusion Order Demoアプリケーションの |
REF CURSOR
を返すファンクションを含むPL/SQLパッケージの宣言は簡単です。たとえば、パッケージは次のようなものです。
CREATE OR REPLACE PACKAGE RefCursorExample IS TYPE ref_cursor IS REF CURSOR; FUNCTION get_orders_for_customer(p_email VARCHAR2) RETURN ref_cursor; FUNCTION count_orders_for_customer(p_email VARCHAR2) RETURN NUMBER; END RefCursorExample;
Order
エンティティ・オブジェクトに対するエンティティ・オブジェクトの慣用名を使用してエンティティ・ベースのOrdersForCustomer
ビュー・オブジェクトを定義した後、そのカスタムJavaクラスOrdersForCustomerImpl.java
に移動します。ビュー・オブジェクト・クラスの最上位で、ストアド・ファンクションを呼び出すためにJDBCのCallableStatement
オブジェクトを使用して実行するの匿名ブロックを保持する定数のStringをいくつか定義します。
/* * Execute this block to retrieve the REF CURSOR */ private static final String SQL = "begin ? := RefCursorSample.get_orders_for_customer(?);end;"; /* * Execute this block to retrieve the count of orders that * would be returned if you executed the statement above. */ private static final String COUNTSQL = "begin ? := RefCursorSample.count_orders_for_customer(?);end;";
次に、以降の項で説明するように、ビュー・オブジェクトのメソッドをオーバーライドします。
create()
メソッドは、このビュー・オブジェクトに対するSQL問合せのすべてのトレースを削除します。
protected void create() { getViewDef().setQuery(null); getViewDef().setSelectClause(null); setQuery(null); }
executeQueryForCollection()
メソッドは、このビュー・オブジェクトに基づいて、フレームワークが問合せコレクションのデータベース問合せを発行する必要があるときに実行されます。1つのビュー・オブジェクトは、多数の関連結果セットを生成でき、それぞれが異なるバインド変数値の結果である可能性があります。問合せの行セットがフレームワーク調整したマスター/ディテール・ビュー・リンクと関連性がある場合、params
配列には、ソース・ビュー・オブジェクトのバインド・パラメータのフレームワークが提供した1つ以上の名前と値のペアが含まれます。ユーザーが提供したバインド・パラメータ値がある場合、これはparams
配列においてフレームワークが提供したバインド変数値よりも優先され、ユーザー・パラメータの数はnumUserParams
引数の値によって示されます。
このメソッドは、ヘルパー・メソッドretrieveRefCursor()
を呼び出して、ストアド・ファンクションを実行してREF CURSOR
戻り値を返し、JDBC ResultSet
としてキャストします。
protected void executeQueryForCollection(Object qc,Object[] params, int numUserParams) { storeNewResultSet(qc,retrieveRefCursor(qc,params)); super.executeQueryForCollection(qc, params, numUserParams); }
次に、ヘルパー・メソッドstoreNewResultSet()
を呼び出します。このヘルパー・メソッドは、setUserDataForCollection()
メソッドを使用して、フレームワークが問合せの実行を要求している行のコレクションとともにこのResultSet
を格納します。
private void storeNewResultSet(Object qc, ResultSet rs) { ResultSet existingRs = getResultSet(qc); // If this query collection is getting reused, close out any previous rowset if (existingRs != null) { try {existingRs.close();} catch (SQLException s) {} } setUserDataForCollection(qc,rs); hasNextForCollection(qc); // Prime the pump with the first row. }
retrieveRefCursor()メソッドは、12.6項「ストアド・プロシージャとストアド・ファンクションの呼出し」で説明されているヘルパー・メソッドを使用して、REF CURSOR
を返します。
private ResultSet retrieveRefCursor(Object qc, Object[] params) { ResultSet rs = (ResultSet)callStoredFunction(OracleTypes.CURSOR, "RefCursorExample.get_requests_for_customer(?)", new Object[]{getNamedBindParamValue("CustEmail",params)}); return rs ; }
フレームワークは、データ・ソースからフェッチする必要のある行ごとに、オーバーライドされたcreateRowFromResultSet()
メソッドを呼び出します。この実装では、コレクション固有のResultSet
オブジェクトがユーザー・データ・コンテキストから取得されます。これは、getResultSet()
メソッド使用して結果セット・ラッパーを問合せコレクション・ユーザー・データから取得し、createNewRowForCollection()
メソッドを使用して新しい空白の行をコレクション内に作成した後、populateAttributeForRow()
メソッドを使用して、ビュー・オブジェクトの概要エディタで設計時に定義された各属性に属性値を移入します。
protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet rs) { /* * We ignore the JDBC ResultSet passed by the framework (null anyway) and * use the resultset that we've stored in the query-collection-private * user data storage */ rs = getResultSet(qc); /* * Create a new row to populate */ ViewRowImpl r = createNewRowForCollection(qc); try { /* * Populate new row by attribute slot number for current row in Result Set */ populateAttributeForRow(r,0, rs.getLong(1)); populateAttributeForRow(r,1, rs.getString(2)); populateAttributeForRow(r,2, rs.getString(3)); } catch (SQLException s) { throw new JboException(s); } return r; }
フレームワーク・メソッドhasNextForCollection()
のオーバーライドされた実装は、フェッチする行がまだあるかどうかに基づいて、true
またはfalse
を返します。最後に達したら、setFetchCompleteForCollection()
を呼び出して、このコレクションは移入が終了したことをビュー・オブジェクトに伝えます。
protected boolean hasNextForCollection(Object qc) { ResultSet rs = getResultSet(qc); boolean nextOne = false; try { nextOne = rs.next(); /* * When were at the end of the result set, mark the query collection * as "FetchComplete". */ if (!nextOne) { setFetchCompleteForCollection(qc, true); /* * Close the result set, we're done with it */ rs.close(); } } catch (SQLException s) { throw new JboException(s); } return nextOne; }
コレクションのフェッチ処理が終了すると、オーバーライドされたreleaseUserDataForCollection()
メソッドが呼び出されて、データベース・カーソルが開いたままにならないように確実にResultSet
を閉じます。
protected void releaseUserDataForCollection(Object qc, Object rs) { ResultSet userDataRS = getResultSet(qc); if (userDataRS != null) { try { userDataRS.close(); } catch (SQLException s) { /* Ignore */ } } super.releaseUserDataForCollection(qc, rs); }
最後に、ビュー・オブジェクトのgetEstimatedRowCount()
メソッドを正しくサポートするため、オーバーライドされたgetQueryHitCount()
メソッドは、すべての行が行セットからフェッチされた場合に取得された行のカウントを返します。ここで、コードはCallableStatement
を使用して処理を行います。問合せはストアド・ファンクションAPIの背後に完全にカプセル化されているため、コードもカウント・ロジックの実装およびこの機能のサポートをPL/SQLパッケージに依存しています。
public long getQueryHitCount(ViewRowSetImpl viewRowSet) { Object[] params = viewRowSet.getParameters(true); BigDecimal id = (BigDecimal)params[0]; CallableStatement st = null; try { st = getDBTransaction().createCallableStatement(COUNTSQL, DBTransaction.DEFAULT); /* * Register the first bind parameter as our return value of type CURSOR */ st.registerOutParameter(1,Types.NUMERIC); /* * Set the value of the 2nd bind variable to pass id as argument */ if (id == null) st.setNull(2,Types.NUMERIC); else st.setBigDecimal(2,id); st.execute(); return st.getLong(1); } catch (SQLException s) { throw new JboException(s); } finally {try {st.close();} catch (SQLException s) {}} }
デフォルトでは、複数のエンティティ・オブジェクト慣用名を持つビュー・オブジェクトを作成するとき、概要エディタでビュー・オブジェクトに追加する2次エンティティ・オブジェクト慣用名それぞれが次の設定で構成されます。
「更新可能」チェック・ボックスを選択解除
「参照」チェック・ボックスを選択
ビュー・オブジェクト概要エディタの「エンティティ・オブジェクト」ページの「選択済」リストで「使用方法」を選択して、「更新可能」チェック・ボックスを選択し、デフォルトの動作を、2次エンティティ・オブジェクトの慣用名を更新可能にできます。
また、各エンティティ・オブジェクト慣用名に関して、「参照」を選択のままにして、エンティティ参照情報が変更されたときに2次エンティティの属性をリフレッシュするかどうかを決定できます。デフォルトでは、「参照」が選択されていて、各2次エンティティ・オブジェクトの属性がリフレッシュされます。複数のエンティティ・オブジェクト慣用名を使用する行の挿入を許可する場合のこの設定の詳細は、42.9.2項「実行時の処理: ビュー行の作成」を参照してください。
表42-4に、ビュー・オブジェクトに複数のエンティティ・オブジェクト慣用名を定義する際に選択できる組合せのまとめを示します。
表42-4 ビュー行作成動作を制御するビュー・オブジェクト・プロパティ
更新可能 | 参照 | ビュー行の動作 |
---|---|---|
|
true |
この組合せでは、エンティティ・オブジェクト慣用名の属性を更新し、その属性の主キーの値との同期を保ちます。この組合せは、ビュー・リンク一貫性機能での動作が良好であるため、これを使用して、ビュー・オブジェクトにある、挿入を行うエンティティ・オブジェクト慣用名を1つにできます。 |
|
false |
この組合せでは、エンティティ・オブジェクト慣用名の属性を更新しますが、その属性は主キー参照によって変更されません。これはかなり稀な組合せで、ビュー・オブジェクトを既存のデータの更新または削除のみに使用する状況に最適です。この組合せでは、ユーザーは非参照の更新可能なエンティティ・オブジェクト慣用名に関連する属性を更新でき、ビュー行は適切な基礎のエンティティ行に変更を委譲します。 注意: ビュー・オブジェクトの一部の2次エンティティ・オブジェクト慣用名がUpdatable=true、Reference=falseに設定されているビュー・リンク一貫性機能の組合せでは、アプリケーションで不要で余分な新しいエンティティを作成してしまう場合があります。 |
false |
true |
これは、5.5.1項「エンティティ・ベースのビュー・オブジェクトに対する結合の作成方法」に記載されているデフォルトの動作です。この組合せは、エンティティ・オブジェクト慣用名を更新しないことを想定しています。 |
新しい行の作成をサポートするための複数の更新可能エンティティ(Updatable=true
、Reference=false
)と、コンポジットでないエンティティ・オブジェクト間のアソシエーションを持つビュー・オブジェクトが必要な場合は、42.9.1項「複数の更新可能エンティティ・オブジェクト慣用名を持つ新規行をプログラムで作成する方法」の説明に従って、コードを少し記述する必要があります。
新しい行の作成をサポートするための複数の更新可能エンティティ(Updatable=true
、Reference=false
)と、コンポジットでないエンティティ・オブジェクト間のアソシエーションを持つビュー・オブジェクトが必要な場合は、ビュー・オブジェクトのカスタム・ビュー行クラスのcreate()
メソッドをオーバーライドしてこれを有効にし、正しく動作するようにする必要があります。
注意: 作成する必要があるのは、更新可能なエンティティ間のアソシエーションがコンポジットでない場合に新しい行の作成を処理するコードのみです。アソシエーションがコンポジットである場合は、ADF Business Componentsにより自動的に処理されます。 |
複数の更新可能エンティティを持つビュー・オブジェクトでcreateRow()
を呼び出すと、更新可能なエンティティ・オブジェクトの慣用名ごとに新しいエンティティ行の部分が作成されます。このシナリオの複数エンティティはアソシエーションによって関連付けられているので、新しい関連のあるエンティティ行を正常に保存できるようにするには、3つのコードを実装する必要があります。
正しいポスト順序を制御するために含まれるエンティティ・オブジェクトで、postChanges()
メソッドをオーバーライドすることが必要な場合があります。
関連付けられたエンティティの主キーがDBSequence
を使用してデータベース順序によって移入され、複数のエンティティ・オブジェクトが関連付けられてはいても、コンポジットではない場合は、postChanges()
メソッドとrefreshFKInNewContainees()
メソッドをオーバーライドし、更新された主キー値を、一時的な値を参照していた関連行にカスケードする必要があります。
ビュー・オブジェクトのカスタム・ビュー行クラスのcreate()
メソッドをオーバーライドし、親エンティティ・オブジェクトのコンテキストを新しく作成される子エンティティに渡すよう、デフォルトの行作成動作を変更する必要があります。
手順1と2のコードを理解するために、4.14.7項「制約違反を防ぐためのエンティティ・ポスト順序を制御する方法」に記載されている、関連Suppliers
とProducts
のエンティティ・オブジェクトの例を参照してください。最後に理解する必要があることは、ビュー行でcreate()
メソッドをオーバーライドする方法です。1次エンティティ・オブジェクト慣用名がProduct
で、2次エンティティ・オブジェクト慣用名がSupplier
である、ProductAndSupplier
ビュー・オブジェクトについて考えます。Product
エンティティ・オブジェクトの慣用名は更新可能で非参照としてマークされ、Supplier
エンティティ・オブジェクトの慣用名は参照エンティティ・オブジェクトの慣用名であるものとします。
例42-31は、ビュー行作成操作の間に、複数の更新可能エンティティ行部分を正しい順序で作成するために必要なコメント付きのコードを示しています。
例42-31 複数の更新可能エンティティの場合のビュー行create()メソッドのオーバーライド
/** * By default, the framework will automatically create the new * underlying entity object instances that are related to this * view object row being created. * * We override this default view object row creation to explicitly * pre-populate the new (detail) ProductsImpl instance using * the new (master) SuppliersImpl instance. Since all entity objects * implement the AttributeList interface, we can directly pass the * new SuppliersImpl instance to the ProductsImpl create() * method that accepts an AttributeList. */ protected void create(AttributeList attributeList) { // The view row will already have created "blank" entity instances SuppliersImpl newSupplier = getSupplier(); ProductsImpl newProduct = getProduct(); try { // Let product "blank" entity instance to do programmatic defaulting newSupplier.create(attributeList); // Let product "blank" entity instance to do programmatic // defaulting passing in new SuppliersImpl instance so its attributes // are available to the EmployeeImpl's create method. newProduct.create(newSupplier); } catch (JboException ex) { newSupplier.revert(); newProduct.revert(); throw ex; } catch (Exception otherEx) { newSupplier.revert(); newProduct.revert(); throw new RowCreateException(true /* EO Row? */, "Product" /* EO Name */, otherEx /* Details */); } }
このProductAndSupplier
ビュー・オブジェクトのビュー行クラスProductAndSupplierRowImpl
で、Suppliers
エンティティ・オブジェクトおよびProducts
エンティティ・オブジェクトの保護されたcreate()
メソッドを呼び出せるようにするには、エンティティ・オブジェクト・クラスでcreate()
メソッドをオーバーライドする必要があります。
/** * Overridding this method in this class allows friendly access * to the create() method by other classes in this same package, like the * ProductsAndSuppliers view object implementation class, whose overridden * create() method needs to call this. * @param nameValuePair */ protected void create(AttributeList nameValuePair) { super.create(nameValuePair); }
create()
メソッドをオーバーライドするとき、メソッドの宣言は次の条件によって異なります
ビュー・オブジェクトとエンティティ・オブジェクトが同じパッケージ内にある場合、オーバーライドされたcreate()
メソッドは保護されたアクセスが可能で、ProductAndSupplierRowImpl
クラスがこれらにアクセスします。
いずれかのエンティティ・オブジェクトが異なるパッケージにある場合、SupplierImpl.create()
とProductImpl.create()
(どちらが別のパッケージにある場合でも)は、SupplierAndProductViewRowImpl
クラスがこれらを呼び出すことができるように、public
宣言される必要があります。
新しい行の作成をサポートするための複数の更新可能エンティティを持つビュー・オブジェクトが必要な場合、Referenceフラグがビュー行作成に関する動作および情報の自動アソシエーション・ドリブン参照を制御することを理解する必要があります。指定のエンティティ・オブジェクト慣用名のReferenceフラグを無効にすると、次のようになります。
新しいビュー行が作成されるたびに、そのエンティティ・オブジェクト慣用名に対して新しいエンティティ・インスタンスが作成されます。
そのエンティティ・オブジェクト慣用名に関するビュー行属性のストレージに対してビュー行が指すエンティティ行は、フレームワークによって自動的に変更されなくなります。
逆に、エンティティ・オブジェクト慣用名のReferenceフラグを有効のまま(デフォルト)にすると、次のようになります。
新しいビュー行が作成されても、そのエンティティ・オブジェクト慣用名に対して新しいエンティティ・インスタンスは作成されません。
そのエンティティ・オブジェクト慣用名に関するビュー行属性のストレージに対してビュー行が指すエンティティ行は、前記のエンティティのアソシエーションを行ったビュー行の属性に対して実行された変更との同期が自動的に保たれます。
DEPT表とEMP表からの情報を結合するEmployeeView
ビュー・オブジェクトを考えてみます。これは、Empno
、Ename
、EmpDeptno
(ビュー・オブジェクト・エディタで選択され、EmployeeView
の属性として識別する名前)、DeptDeptno
(ビュー・オブジェクト・エディタで選択され、DepartmentView
の属性として識別する名前)およびDname
の各属性を表示します。
ここで、updatableとreferenceの両方のマークを付けられた2次エンティティ・オブジェクトを設定するデフォルトのケースの実行時の処理について考えてみます。
従業員エンティティ・オブジェクトは1次エンティティ・オブジェクト慣用名です。
部門エンティティ・オブジェクトは2次エンティティ・オブジェクト慣用名でReferenceのマークを付けられます。
ユーザーが新しい行をEmployeeView
に作成するとき、ADF Business Componentsは新しい従業員エンティティ・オブジェクト・インスタンスのみを作成します(部門エンティティ・オブジェクトのReferenceフラグが有効であるため)。ユーザーが従業員のDeptDeptno
属性を10
に変更すると、ADF Business Componentsは、エンティティ・キャッシュで主キー10
の部門エンティティを自動的に検索し(まだキャッシュされていない場合にはデータベースから読み込む)、この新しいビュー行の部門エンティティ・オブジェクト慣用名をこの部門10
エンティティを指すようにします。これは、部門10
に正しいDname
値を自動的に反映した結果です。
デフォルトのシナリオでは、部門のエンティティ・オブジェクト慣用名がreferenceとしてマークされ、従業員エンティティと部門エンティティの間にアソシエーションが存在するため、両方で参照検索が発生します。アソシエーション定義を介して、ADF Business Componentsは、どの属性がこのアソシエーションのどちら側と関連性があるかを認識します。EmpToDept
アソシエーションの従業員側の任意の属性が変更されるときに、部門エンティティ・オブジェクト慣用名がReferenceとマークされている場合、ADF Business Componentsは自動参照検索を実行します。ユーザーがこの新しいビュー行でDname
をNewDept
に設定した場合、変更をコミットした後に、データベースは新しい従業員を部門10
に格納し、部門10
の名前をNewDept
に更新します。
ここで、updatableとreferenceのマークを付けられた2次エンティティ・オブジェクトを無効に設定した場合、実行時の処理について考えてみます。
従業員エンティティ・オブジェクトは1次エンティティ・オブジェクト慣用名です。
部門エンティティ・オブジェクトは2次エンティティ・オブジェクト慣用名ですが、この慣用名はReferenceのマークを付けられていません。
このシナリオでは、ユーザーが新しい行をEmployeeView
に作成するとき、ADF Business Componentsは新しい従業員エンティティ・オブジェクト・インスタンスと新しい部門エンティティ・オブジェクト・インスタンスの両方を作成します(部門エンティティ・オブジェクト・慣用名のReferenceフラグが無効になっているため)。ユーザーが従業員のDeptno
属性を10
に変更しても、行に表示されるDname
属性の値には影響しません。また、ユーザーがこの新しいビュー行でDeptDeptno
を99
に、Dname
をNewDept
に設定すると、変更をコミットした後、データベースは新しい従業員を部門10
に格納し、新しい部門番号を99
にします。
oracle.jbo.server.ViewDefImpl
クラスでは、ビュー・オブジェクト・インスタンスのビュー定義メタオブジェクトを動的に定義できます。ビュー定義は、ビュー・オブジェクトの構造を示します。
一般に、アプリケーションでは、JDeveloperの概要エディタを使用して作成したXMLファイルをロードして、ビュー定義オブジェクトを作成します。アプリケーションでビュー・オブジェクト・インスタンスを作成する必要がある場合、同じ名前のビュー定義についてMetaObjectManager
に問い合せます。その後、XMLファイルを検索し、開いて解析して、ビュー定義を作成します。
ViewDefImpl
クラスのメソッドを使用して、プログラムでビュー定義を作成することもできます。ViewDefImpl
メソッドを使用すると、ビュー・オブジェクトの動的な作成が簡易化され、実行時にApplicationModule
インタフェースでcreateViewObjectFromQueryStmt()
APIを呼び出す場合に発生する、実行時オーバーヘッドは発生しません。
プログラムでビュー定義を作成する場合、アプリケーション・コードは次のコードで始まる必要があります。
ViewDefImpl viewDef = new ViewDefImpl("MyViewDef");
MyViewDef
は、ビュー定義オブジェクトがMetaObjectManager
によって識別される名前です。このインスタンス名は、APIからビュー定義名が要求されたときにアプリケーションが渡す名前です。たとえば、ApplicationModule.createViewObject(String, String)
を呼び出すときにアプリケーションはdefName
パラメータをリクエストできます。
例42-32に、ビュー定義のインスタンス化およびビュー・オブジェクトSQL文の定義(1つの文または部分での)を示します。ビュー定義コードはresolveDefObject()
を使用して定義オブジェクトを解決し、RowDef.registerDefObject()
を使用してMetaObjectManager
に登録する必要があります。
ビュー定義を作成した後、この定義に基づいてビュー・オブジェクト・インスタンスを作成するには、次の基本手順に従います(例42-32を参照)。
ビュー定義オブジェクトを作成します。
ビュー・オブジェクトSQL文を定義します。
ビュー定義を解決して登録します。
ビュー定義を使用して、ビュー定義に基づくビュー・オブジェクトのインスタンスを作成します。
例42-32 ViewDefImpl APIを使用したビュー定義の作成
/* * 1. Create the view definition object. */ ViewDefImpl v = new ViewDefImpl("some.unique.DefNameForTheObject"); /* * 2. Then, define the view object's SQL statement by either using a fully- * specified "expert-mode" SQL query. */ v.setQuery("select e.empno,e.ename,e.sal,e.deptno,d.dname,d.loc,"+ "d.deptno,trunc(sysdate)+1 tomorrow_date, "+ "e.sal + nvl(e.comm,0) total_compensation, "+ "to_char(e.hiredate,'dd-mon-yyyy') formated_hiredate"+ " from emp e, dept d "+ " where e.deptno = d.deptno (+)"+ " order by e.ename"); v.setFullSql(true); /* * Or, you can construct the SQL statement in parts like this. */ v.setSelectClause("e.empno,e.ename,e.sal,e.deptno,d.dname,d.loc,"+ "d.deptno,trunc(sysdate)+1 tomorrows_date,"+ "e.sal + nvl(e.comm,0) total_compensation, "+ "to_char(e.hiredate,'dd-mon-yyyy') formated_hiredate"); v.setFromClause("emp e, dept d"); v.setWhereClause("e.deptno = d.deptno (+)"); v.setOrderByClause("e.ename"); /* * 3. Then resolve and register the view definition. */ v.resolveDefObject(); v.registerDefObject(); /* * 4. Finally, use the dynamically-created view definition to construct * instances of view objects based on it. */ ViewObject vo = createViewObject("SomeInstanceName",v);
Oracle Formsなどの一部の4GLツールでは、特定のデータ・コレクションが挿入、更新または削除を許可するかどうかを制御する宣言的プロパティが提供されています。現在のリリースでは、まだビュー・オブジェクトはこれを組込み機能としてサポートしていませんが、ビュー・オブジェクトでの挿入、更新または削除を制御するための開発者提供のフラグとしてカスタム・メタデータ・プロパティを利用するフレームワーク拡張クラスを使用すれば、この機能を簡単に追加できます。
注意: この項の例では、Fusion Order Demoアプリケーションの |
開発者が個別のビュー・オブジェクト・インスタンスを制御できるようにするには、ビュー・オブジェクト・インスタンスと同じ名前でアプリケーション・モジュール・カスタム・プロパティを使用するという規則を導入します。たとえば、アプリケーション・モジュールにProductsInsertOnly
、ProductsUpdateOnly
、ProductsNoDelete
およびProducts
という名前のビュー・オブジェクト・インスタンスがある場合、汎用コードでは、これらと同じ名前でアプリケーション・モジュール・カスタム・プロパティを検索します。プロパティ値にInsert
が含まれている場合は、そのビュー・オブジェクト・インスタンスでは挿入が有効です。プロパティにUpdate
が含まれる場合は、更新が許可されています。同様に、プロパティ値にDelete
が含まれる場合は、削除が許可されています。次のようなヘルパー・メソッドを使用してこれらのアプリケーション・モジュール・プロパティを検査し、挿入、更新および削除が特定のビュー・オブジェクトに対して許可されているかどうかを判定できます。
private boolean isInsertAllowed() { return isStringInAppModulePropertyNamedAfterVOInstance("Insert"); } private boolean isUpdateAllowed() { return isStringInAppModulePropertyNamedAfterVOInstance("Update"); } private boolean isDeleteAllowed() { return isStringInAppModulePropertyNamedAfterVOInstance("Delete"); } private boolean isStringInAppModulePropertyNamedAfterVOInstance(String s) { String voInstName = getViewObject().getName(); String propVal = (String)getApplicationModule().getProperty(voInstName); return propVal != null ? propVal.indexOf(s) >= 0 : true; }
例42-33は、ビュー行が実装を完了するために、カスタム・フレームワーク拡張クラスで必要な他のコードを示しています。これは、次のメソッドをオーバーライドします。
isAttributeUpdateable()
挿入が許可されていない場合に新しい行のフィールドを無効にするため、または更新が許可されていない場合に既存の行のフィールドを無効にするために、ユーザー・インタフェースを有効化します。
setAttributeInternal()
挿入が許可されていない場合に新しい行の属性値が設定されないようにします。また、更新が許可されていない場合に既存の行の属性が設定されないようにします。
remove()
削除が許可されない場合に削除を禁止します。
create()
挿入が許可されない場合に作成を禁止します。
例42-33 カスタム・プロパティに基づく挿入、更新、または削除の禁止
public class CustomViewRowImpl extends ViewRowImpl { public boolean isAttributeUpdateable(int index) { if (hasEntities() && ((isNewOrInitialized() && !isInsertAllowed()) || (isModifiedOrUnmodified() && !isUpdateAllowed()))) { return false; } return super.isAttributeUpdateable(index); } protected void setAttributeInternal(int index, Object val) { if (hasEntities()) { if (isNewOrInitialized() && !isInsertAllowed()) throw new JboException("No inserts allowed in this view"); else if (isModifiedOrUnmodified() && !isUpdateAllowed()) throw new JboException("No updates allowed in this view"); } super.setAttributeInternal(index, val); } public void remove() { if (!hasEntities() || isDeleteAllowed() || isNewOrInitialized()) super.remove(); else throw new JboException("Delete not allowed in this view"); } protected void create(AttributeList nvp) { if (isInsertAllowed()) { super.create(nvp); } else { throw new JboException("Insert not allowed in this view"); } } // private helper methods omitted from this example }