この章では、エンティティ・オブジェクトと自動的に連携して完全に更新可能なデータ・モデルを有効にする更新可能なビュー・オブジェクトを作成する方法について説明します。
この章の内容は次のとおりです。
エンティティ・ベースのビュー・オブジェクトは、更新可能な行をサポートしています。ビュー・オブジェクトは、クライアントが直面している当面のタスクに必要なデータのみを問い合せ、ビジネス・ドメイン・レイヤー内の1つ以上のエンティティ・オブジェクトと連携し、ビュー行の変更を自動的に検証および保存します。読取り専用ビュー・オブジェクトと同様に、エンティティ・ベースのビュー・オブジェクトは、SQL問合せをカプセル化します。また、このビュー・オブジェクトは、マスター/ディテール階層にリンクしたり、アプリケーション・モジュールのデータ・モデルで使用できます。
この章の終わりには、図7-1に示す概念を理解できるようになります。
更新可能なビュー・オブジェクトは、1つ以上のエンティティ・オブジェクトの属性を参照して定義します。
関連付けられた複数のエンティティ・オブジェクトを使用することにより、参照情報の操作を簡略化できます。
基礎となるエンティティ・アソシエーションに基づいてビュー・リンクを定義できます。
エンティティ・ベースのビュー・オブジェクトは、トランザクションを提供するアプリケーション・モジュールとの関連で使用します。
実行時に、ビュー行は、属性の格納および検証を基礎となるエンティティ・オブジェクトに委譲します。
この章では、アプリケーション・モジュールのデータ・モデル内のエンティティ・ベースのビュー・オブジェクトのインスタンスを使用して、再使用可能なビジネス・ドメイン・オブジェクトが持つ明確なオブジェクト指向のカプセル化能力にSQLの完全なデータ形成能力を組み合せることにより、クライアントがビジネス・ドメイン・レイヤー情報を検索、更新、挿入および削除できるようにする方法について説明します。また、これらすべての作業についてコード行は必要ありません。
注意: この章の例では、第6章「エンティティ・オブジェクトを使用したビジネス・ドメイン・レイヤーの作成」と同じServiceRequest 、ServiceHistory 、Product 、User およびExpertiseArea エンティティ・オブジェクトの基本的なSRDemoアプリケーションのビジネス・ドメイン・レイヤーを使用します。この例の動作バージョンを体験するには、サンプルのダウンロード・ページ(http://otn.oracle.com/documentation/jdev/b25947_01 )からDevGuideExamples ワークスペースをダウンロードし、EntityBasedViewObjects プロジェクトを参照してください。 |
エンティティ・ベースでビュー・オブジェクトを作成する場合、SQL文を自分で入力する必要がないため、読取り専用ビュー・オブジェクトより簡単に作成できます。また、エンティティ・ベースのビュー・オブジェクトの方が、読取り専用ビュー・オブジェクトより非常に多くの実行時機能が用意されています。
エンティティ・ベースのビュー・オブジェクトを作成するには、ビュー・オブジェクトの作成ウィザードを使用します。このウィザードは、「新規ギャラリ」の「Business Tier」→「ADF Business Components」カテゴリから起動できます。ここでは、スタッフ・メンバーの更新可能リストを取得するために、devguide.model.queries
パッケージにStaffList
ビュー・オブジェクトを作成するとします。
図7-2に示すように、ステップ1の「名前」ページで、ビュー・オブジェクトの名前とパッケージを指定します。データの管理方法は、デフォルト設定の「エンティティ・オブジェクトを介した更新可能アクセス」のままにします。
ステップ2の「エンティティ・オブジェクト」ページで、ビュー・オブジェクトでデータを使用するエンティティ・オブジェクトを選択します。図7-3に、User
エンティティ・オブジェクトを選択して「選択済」リストに移動した結果を示します。このリストのエントリは、エンティティ・オブジェクトの慣用名と呼ばれます。これは、このエンティティには、ビュー・オブジェクトが使用するエンティティ・オブジェクトが記録されるためです。また、このエンティティは、エンティティ参照とも呼ばれます。これは、ビュー・オブジェクトがこのエンティティの属性を参照するためです。
ステップ3の「属性」ページで、エンティティ・オブジェクトの慣用名から組み込む属性を「使用可能」リストから選択し、「選択済」リストに移動します。図7-4では、UserId
、Email
、FirstName
およびLastName
属性が選択されています。
ステップ4の「属性の設定」ページでは、「属性の選択」ドロップダウンを使用してビュー・オブジェクト属性を切り替え、名前や初期設定を変更できます。この例では、デフォルトのままでかまいません。
ステップ5の「SQL文」ページでは、図7-5のように、選択したエンティティ属性に基づいてSELECT文が自動的に生成されます。必要に応じて、データのフィルタリングや順序付けを行うためにWHEREおよびORDER BY句を問合せに追加できます。このStaffList
ビュー・オブジェクトでは、USERS表でUSER_ROLE列の値としてtechnicianまたはmanagerを持つ行のみを表示するために、「WHERE」フィールドに適切なWHERE句条件を含めることができます。データを姓および名前別に順序付けるには、「ORDER BY」フィールドに適切なORDER BY句を含めます。「WHERE」および「ORDER BY」フィールドの値には、WHEREまたはORDER BYキーワードは表示されません。これらのキーワードは、実行時に問合せを実行する際に追加されます。
ここで「終了」をクリックし、ビュー・オブジェクトを作成します。
基礎となるエンティティ・オブジェクトのすべての属性をクライアントが操作できるようにするには、7.2.1項「エンティティ・ベースのビュー・オブジェクトの作成方法」で説明するように、ビュー・オブジェクトの作成ウィザードを使用します。この場合、エンティティ・オブジェクトを選択した後、「属性」ページですべての属性を選択します。ただし、これは頻繁に行う操作であるため、同じ作業をアプリケーション・ナビゲータでより簡単に実行する方法があります。
新しいエンティティ・ベースのビュー・オブジェクトの作成方法:
アプリケーション・ナビゲータで目的のエンティティ・オブジェクトを選択します。
ポップアップ・メニューから「新規デフォルト・ビュー・オブジェクト」を選択します。
図7-6に示すように、「デフォルト・ビュー・オブジェクトを作成」ダイアログで新しいビュー・オブジェクトのパッケージおよびコンポーネント名を指定します。
作成される新しいエンティティ・ベースのビュー・オブジェクトは、ビュー・オブジェクトの作成ウィザードで作成できるものと同一のものです。デフォルトでは、このビュー・オブジェクトには、アプリケーション・ナビゲータで選択したエンティティ・オブジェクトを参照する単一のエンティティ・オブジェクトの慣用名があり、そのすべての属性が含まれます。最初は、このビュー・オブジェクトにはWHERE句もORDER BY句もないため、次の操作にビュー・オブジェクト・エディタを使用します。
不要な属性の削除
WHERE句を使用した選択内容の限定
ORDER BY句による結果の順序付け
ビュー・オブジェクトのプロパティのカスタマイズ
エンティティ・ベースのビュー・オブジェクトを作成する際、JDeveloperでビュー・オブジェクトの宣言的設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリに保存されます。図7-2のように、ビュー・オブジェクトにはdevguide.model.queries
パッケージ内でStaffList
という名前が付けられたため、作成されたXMLファイルは、プロジェクトのソース・パスの下に./devguide/model/queries/StaffList.xml
として作成されます。このXMLファイルには、SQL問合せに関する情報、エンティティ・オブジェクトの慣用名、および各属性のプロパティが含まれます。この内容を確認する場合、アプリケーション・ナビゲータでビュー・オブジェクトを選択し、構造ウィンドウで対応するソース・フォルダ内を参照し、ビュー・オブジェクトのXMLファイルを確認します。StaffList.xml
ノードをダブルクリックすると、このXMLがエディタで開かれ、その内容を確認できます。
注意: IDEレベルのビジネス・コンポーネントJava生成設定で指定されている場合、オプションのカスタム・ビュー・オブジェクト・クラスStaffListImpl.java またはカスタム・ビュー行クラスStaffListRowImpl.java (あるいはその両方)も生成される場合があります。 |
エンティティ・ベースのビュー・オブジェクトを作成した後、ビュー・オブジェクト・エディタを使用してその設定を編集できます。アプリケーション・ナビゲータのポップアップ・メニューで「編集」メニュー・オプションを選択するか、ビュー・オブジェクトをダブルクリックし、ビュー・オブジェクト・エディタを起動します。エディタの様々なパネルを開き、問合せのWHERE句およびORDER BY句の調整、属性名の変更、名前付きバインド変数の追加、UIコントロール・ヒントの追加、Java生成オプションの制御、およびその他の設定の構成を行うことができます。
各ビュー・オブジェクト属性は、対応するエンティティ・オブジェクト属性のプロパティを継承します。
エンティティ・ベースのビュー・オブジェクトに関して興味深い特徴の1つは、基礎となるエンティティ・オブジェクト属性に関連する各属性がその属性のプロパティを継承することです。図7-7に、UserId
属性が選択された状態のビュー・オブジェクト・エディタを示します。ここでは、Javaの「属性」の「型」および「問合せ列」の「型」などのプロパティは無効になっており、これらの値は、このビュー・オブジェクトが関連付けられているUser
エンティティ・オブジェクトの関連UserId属性から継承されています。属性のデータ型などの一部のプロパティは継承され、ビュー・オブジェクト・レベルでは変更できません。
Queryable
およびUpdatable
などの他のプロパティは継承されますが、オーバーライド後の設定の方が継承された設定より制限が大きい場合、これらのプロパティをオーバーライドできます。たとえば、User
エンティティ・オブジェクトのUserId
属性の「更新可能」設定は、「常に」です。図7-7のように、ビュー・オブジェクト・エディタでは、対応するビュー・オブジェクト属性を、制限がより大きい「新規の間」または「なし」などに設定できます。しかし、User
エンティティ・オブジェクトのUserId
属性の「更新可能」設定が「なし」である場合、StaffList
の関連ビュー・オブジェクト属性を、制限がより小さい「常に」などに設定できません。
ビジネス・アプリケーションでは、プライマリ・ビジネス・ドメイン・オブジェクトとともにセカンダリ参照情報から情報を補足し、外部キー属性が示す内容をエンド・ユーザーが理解しやすいようにすることは非常に一般的です。ServiceRequest
エンティティ・オブジェクトを例として考えてみます。このオブジェクトには、次のようなNumber
型の外部キー属性が含まれます。
CreatedBy
: リクエストを作成したユーザーを示します。
AssignedTo
: リクエストが割り当てられるユーザーを示します。
ProdId
: リクエストが関係する製品を示します。
経験上、このような未処理の数値のみをエンド・ユーザーに示しても、あまり役立ちません。アプリケーションの使いやすさを向上させるには、関連するUser
およびProduct
エンティティ・オブジェクトからの参照情報の表示が理想的です。一般的な解決方法の1つとして、プライマリ情報と参照情報の組合せを取得する結合問合せを実行する方法があります。また、参照表に対する特別な問合せに基づいて、問合せ対象の行ごとにダミー・フィールドに参照情報を移入できます。ただし、エンド・ユーザーがデータの編集時に外部キー値を変更できる場合、この方法には別の課題が生じます。
たとえば、ある技術者から別の技術者にサービス・リクエストを再度割り当てる場合、エンド・ユーザーは、参照情報の同期が維持されているとみなします。この場合、エンティティ・ベースのビュー・オブジェクトは、常に最新の状態の参照情報を簡単に組み込む機能をサポートしています。この機能を活用するには、ビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名として機能するエンティティ・オブジェクトと、参照情報を提供するエンティティ・オブジェクト間にアソシエーションが確立されていることが必要です。
この項では、前に作成したデフォルトのServiceRequests
ビュー・オブジェクトを変更し、User
およびProduct
エンティティ・オブジェクトからの参照情報を組み込む方法について説明します。
ビュー・オブジェクトの参照エンティティを組み込むには、単一のエンティティ・オブジェクトの慣用名をすでに持つエンティティ・ベースのビュー・オブジェクトに対してビュー・オブジェクト・エディタを開き、「エンティティ・オブジェクト」ページを開きます。このページの「選択済」リストの最初のエンティティ・オブジェクトの慣用名は、ビュー・オブジェクトのプライマリ・エンティティ・オブジェクトの慣用名と呼ばれます。ただし、このリストは単一のエンティティ・オブジェクトの慣用名には限定されません。ビュー・オブジェクトで別のエンティティ・オブジェクトを使用するには、これらを「使用可能」リストから選択し、「選択済」リストに移動します。
図7-8に、既存のServiceRequests
ビュー・オブジェクトに3つの参照エンティティ・オブジェクトの慣用名を追加した結果を示します。このうち、1つはProduct
エンティティ用で、2つはUser
エンティティ用の個別の慣用名です。「選択済」リストでエンティティ・オブジェクトの慣用名をクリックすると、「参照」チェック・ボックスの状態により、ビュー・オブジェクトに追加された2番目以降のエンティティ・オブジェクトの慣用名が参照情報としてデフォルトでマークされることが示されます。同様に、「更新可能」チェック・ボックスが選択されていない状態であることからわかるように、セカンダリ・エンティティ・オブジェクトの慣用名は更新不可能としてデフォルト設定されます。
注意: セカンダリ・エンティティ・オブジェクトの慣用名を追加すると、これらの「更新可能」フラグはfalse、「参照」フラグはtrueにデフォルト設定されます。これは最も一般的な慣用名のパターンです。27.9項「複数の更新可能エンティティによるビュー・オブジェクトの作成」では、あまり一般的ではないが役に立つ方法として、更新可能エンティティ・オブジェクトの慣用名を複数持つ結合ビュー・オブジェクトを作成する方法について説明します。 |
「アソシエーション」ドロップダウン・リストには、選択したエンティティ・オブジェクトの慣用名をプライマリ・エンティティ・オブジェクトの慣用名に関連付けるアソシエーションの名前が表示されます。デフォルト名が明確でない場合は、「別名」フィールドを使用して、より意味のある名前をエンティティ・オブジェクトの慣用名として付けることができます。たとえば、User
エンティティ・オブジェクトの2つのエンティティ・オブジェクトの慣用名を「選択済」リストに移動した後、これらのオブジェクトの慣用名の当初の別名はUser1
とUser2
でした。図からわかるように、これらの名前をTechnician
とCustomer
に変更すると、ビュー・オブジェクトが参照している情報が非常に明確になります。この図からわかるもう1つ重要な点は、同じエンティティに対して複数のエンティティ・オブジェクトの慣用名を追加する場合、「アソシエーション」ドロップダウン・リストを使用して、このオブジェクトの慣用名とプライマリ・エンティティ・オブジェクトの慣用名との関連を示すアソシエーションを選択する必要があることです。Technician
エンティティ・オブジェクトの慣用名の場合はServiceRequestsAssignedToUser
アソシエーションを選択し、Customer
エンティティ・オブジェクトの慣用名の場合はServiceRequestsCreatedByUser
アソシエーションを選択します。
セカンダリ・エンティティ・オブジェクトの慣用名を追加した後、ビュー・オブジェクト・エディタの「属性」ページに切り替え、ビュー・オブジェクトに組み込むこれらの新しいオブジェクトの慣用名から特定の追加属性を選択します。図7-9に、次の追加属性を「選択済」リストに移動した結果を示します。
Product
エンティティ・オブジェクトの慣用名のName
属性
Technician
エンティティ・オブジェクトの慣用名のEmail
属性
Customer
エンティティ・オブジェクトの慣用名のEmail
属性
これらを組み込む意図がなかったとしても、各エンティティ・オブジェクトの慣用名の主キー属性が「選択済」リストの一部であるかどうかが自動的に検証されます。これらがリストに含まれていない場合は自動的に追加されます。
「SQL文」ページを選択すると、SELECT文に新しい列が含まれるとともに、「WHERE」フィールドが更新され、次のような適切な結合句が含まれていることがわかります。
((ServiceRequest.PROD_ID = Product.PROD_ID) AND (ServiceRequest.ASSIGNED_TO = Technician.USER_ID)) AND (ServiceRequest.CREATED_BY = Customer.USER_ID)
ビュー・オブジェクト・エディタの左側のツリーで「属性」ノードを開くと、リストの最後に属性が追加されていることがわかります。デフォルトの属性名はわかりにくいため、1つずつ順に選択し、名前を次のように変更できます。
Name
→ProductName
ProdId1
→PKProdId
(Product
エンティティ・オブジェクトの慣用名の主キー)
Email
→TechnicianEmail
UserId
→PKTechnicianUserId
(Technician
エンティティ・オブジェクトの慣用名の主キー)
Email1
→CustomerEmail
UserId1
→PKCustomerUserId
(Customer
エンティティ・オブジェクトの慣用名の主キー)
プライマリ・エンティティ・オブジェクトの慣用名の主キー属性に対応するビュー・オブジェクト属性は、ビュー行を識別するための主キーとして機能します。セカンダリ・エンティティ・オブジェクトの慣用名を追加すると、これらの主キー属性に対応するビュー・オブジェクト属性もビュー行キーの一部としてマークされます。ビュー・オブジェクトが単一の更新可能なプライマリ・エンティティ・オブジェクトの慣用名と多くの参照エンティティ・オブジェクトの慣用名で構成されている場合、ビュー行を一意に識別するにはプライマリ・エンティティ・オブジェクトの慣用名の主キー属性で十分です。この場合、これらの追加キー属性は不要であるため、これらの「キー属性」設定をfalse
に切り替える必要があります。前に作成したビュー・オブジェクトの場合、PKProdId
、PKTechnicianUserId
およびPKCustomerUserId
の各属性についてこの設定をfalse
に切り替え、「キー属性」の選択を解除します。
通常、ビュー・オブジェクトに自動的に追加された主キー属性は表示する必要がないため、図7-10のように、UIの「コントロール・ヒント」ページで「ヒントの表示」プロパティを「非表示」に設定できます。
「OK」をクリックし、ビュー・オブジェクトの変更を保存します。
参照別のセカンダリ・エンティティ・オブジェクトの慣用名をビュー・オブジェクトに組み込むと、ビュー・オブジェクトのXMLコンポーネント定義が更新され、追加のエンティティ・オブジェクトの慣用名に関する情報が組み込まれます。たとえば、3つの参照エンティティ・オブジェクトの慣用名を組み込むために拡張した前述のビュー・オブジェクトのServiceRequests.xml
ファイルを参照すると、この情報がこのファイルの複数の<EntityUsage>
要素に記録されていることがわかります。たとえば、プライマリ・エンティティ・オブジェクトの慣用名のエントリは次のようになります。
<EntityUsage Name="ServiceRequest" Entity="devguide.model.entities.ServiceRequest"/>
セカンダリの参照エンティティ・オブジェクトの慣用名のエントリはこれとは若干異なり、このエンティティ・オブジェクトの慣用名をプライマリ・エンティティ・オブジェクトの慣用名に関連付けるアソシエーションに関する情報が含まれます。
<EntityUsage Name="Product" Entity="devguide.model.entities.Product" Association= "devguide.model.entities.associations.ServiceRequestsForProduct" AssociationEnd= "devguide.model.entities.associations.ServiceRequestsForProduct.Product" SourceUsage="devguide.model.queries.ServiceRequests.ServiceRequest" ReadOnly="true" Reference="true"/>
XML内の各属性エントリは、参照先のエンティティ・オブジェクトの慣用名を示します。ProblemDescription
属性のこのエントリは、この属性がServiceRequest
エンティティ・オブジェクトの慣用名に関連付けられていることを示します。
<ViewAttribute Name="ProblemDescription" IsNotNull="true" EntityAttrName="ProblemDescription" EntityUsage="ServiceRequest" AliasName="PROBLEM_DESCRIPTION" > </ViewAttribute>
一方、CustomerEmail
属性は、Customer
エンティティ・オブジェクトの慣用名に関連付けられています。
<ViewAttribute Name="CustomerEmail" IsUpdatable="false" IsNotNull="true" EntityAttrName="Email" EntityUsage="Customer" AliasName="EMAIL1" > </ViewAttribute>
ビュー・オブジェクト・エディタでは、このアソシエーション情報が設計時に使用され、ビュー・オブジェクトの結合WHERE句が自動的に作成されます。この情報が実行時に使用されることにより、エンド・ユーザーによって外部キー属性値が変更されたときに参照情報を最新の状態に保つことが可能になります。
ビュー・オブジェクトが複数のエンティティ・オブジェクトを参照している場合、これらのエンティティ・オブジェクトはビジネス・コンポーネント・ダイアグラム上で個別のエンティティ・オブジェクトの慣用名として表示されます。必要であれば、デフォルトの内部結合句を外部結合として変更もできます。
6.4項「ビジネス・レイヤーのエンティティ・ダイアグラムの作成」では、ビジネス・コンポーネント・ダイアグラムを作成してビジネス・ドメイン・レイヤーを視覚化する方法について説明しました。JDeveloperのUMLダイアグラムでは、エンティティ・オブジェクトのサポートのみならず、ビュー・オブジェクトをダイアグラムにドロップして、その構造とエンティティ・オブジェクトの慣用名を視覚化できます。devguide.model.design
パッケージにSRService Data Model
という名前の新しいビジネス・コンポーネント・ダイアグラムを作成し、アプリケーション・ナビゲータからServiceRequests
ビュー・オブジェクトをダイアグラムにドラッグすると、図7-11のようになります。ノードを拡張して表示すると、ダイアグラムには、ビュー・オブジェクトのエンティティ・オブジェクトの慣用名を含む部分が表示されます。
プライマリ・エンティティ・オブジェクトの慣用名の表とそれに関連付けられているセカンダリ・エンティティ・オブジェクトの慣用名の表の間に結合用のWHERE句が作成される場合、デフォルトでは常に内部結合が作成されます。ここでは、ServiceRequests
ビュー・オブジェクトのWHERE句について詳しく検討します。
((ServiceRequest.PROD_ID = Product.PROD_ID) AND (ServiceRequest.ASSIGNED_TO = Technician.USER_ID)) AND (ServiceRequest.CREATED_BY = Customer.USER_ID)
サービス・リクエストがまだ技術者に割り当てられていない場合、AssignedTo
属性はnull
になります。前述のとおり生成されたデフォルトの内部結合条件では、これらの未割当てのサービス・リクエストは取得されません。ServiceRequests
ビュー・オブジェクトを介して未割当てのサービス・リクエストを表示可能および更新可能にする必要がある場合、ビュー・オブジェクト・エディタの「SQL文」ページに再度アクセスし、ASSIGNED_TO列のnullになる可能性のある値についてのUSER表に対する外部結合に問合せを変更する必要があります。次に示す更新されたWHERE句では、結合でデータがなくても許可される関連表に対する追加の(+)
演算子が等号の右側にあります。
((ServiceRequest.PROD_ID = Product.PROD_ID) AND (ServiceRequest.ASSIGNED_TO = Technician.USER_ID (+) )) AND (ServiceRequest.CREATED_BY = Customer.USER_ID)
読取り専用ビュー・オブジェクトの場合と同様に、エンティティ・ベースのビュー・オブジェクトを他のビュー・オブジェクトにリンクし、任意の複雑さでマスター/ディテール階層を形成できます。作成手順で唯一異なるのは、マスター・ビュー・オブジェクトとディテール・ビュー・オブジェクトの両方がエンティティ・ベースのビュー・オブジェクトで、これらの各エンティティ・オブジェクトの慣用名がアソシエーションによって関連付けられている場合です。この場合、アソシエーションにより、これらのオブジェクトを関連付ける一連のソース属性と関連先属性のペアが取得されるため、ベースとなるアソシエーションを指定するのみでビュー・リンクを作成できます。
アソシエーション・ベースのビュー・リンクを作成するには、ビュー・リンクの作成ウィザードを使用します。
アソシエーション・ベースのビュー・リンクの作成方法:
「新規ギャラリ」の「Business Tier」→「ADF Business Components」カテゴリから、ビュー・リンクの作成ウィザードを選択します。
ステップ1の「名前」ページで、パッケージおよびコンポーネント名を指定します。ここでは、devguide.model.queries.viewlinks
パッケージにビュー・リンクを作成し、これをRequestsAssignedToTechnician
と呼ぶとします。
ステップ2の「ビュー・オブジェクト」ページの「ソース属性の選択」ツリーで、devguide.model.queries
パッケージ内のソースとなるStaffList
ビュー・オブジェクトを開きます。「関連先属性の選択」ツリーで、ServiceRequests
ビュー・オブジェクトを開きます。ビュー・オブジェクト属性の他にも、エンティティ・ベースのビュー・オブジェクトの場合、リストには関連するアソシエーションも表示されます。図7-12のように、「ソース属性の選択」ツリーと「関連先属性の選択」ツリーの両方で同じServiceRequestsAssignedToUser
アソシエ追加追加」をクリックしてアソシエーションを下の表に追加します。「次へ」および「終了」をクリックし、新しいビュー・リンクの作成を完了します。
次に、サービス・リクエストの履歴エントリに関する詳細情報を表示するビュー・オブジェクトとServiceRequests
ビュー・オブジェクトの間に別のアソシエーション・ベースのビュー・リンクを作成します。すでにマスターのServiceRequests
ビュー・オブジェクトはありますが、最初にディテール用のビュー・オブジェクトを作成してから、これらをリンクさせる必要があります。前述の簡単な作成方法を使用して、アプリケーション・ナビゲータでdevguide.model.entities
パッケージ内のServiceHistory
エンティティ・オブジェクトを選択し、ポップアップ・メニューから新規デフォルト・ビュー・オブジェクト」を選択し、このエンティティに基づいてdevguide.model.queries
内にServiceHistories
という名前のビュー・オブジェクトを作成します。
注意: 「デフォルト・ビュー・オブジェクトを作成」では、コンボボックスを使用して、既存のパッケージのリストからdevguide.model.queries を選択できます。 |
最後に、前述のステップを繰り返し、ServiceRequests
ビュー・オブジェクトと新しいServiceHistories
ビュー・オブジェクト間に、これらのプライマリ・エンティティ・オブジェクトの慣用名をそれぞれ関連付けるアソシエーションに基づいてアソシエーション・ベースのビュー・リンクを作成します。devguide.model.queries.viewlinks
パッケージでこのビュー・リンクにHistoryLinesForRequest
という名前を付けます。パッケージ名を入力する必要のない、もう1つの簡単な方法として、図7-13に示すとおり、アプリケーション・ナビゲータでviewlinks
パッケージ・ノードのポップアップ・メニューの「新規ビュー・リンク」を使用できます。
アソシエーション・ベースのビュー・リンクを作成する際、JDeveloperでその宣言的設定を表すXMLコンポーネント定義ファイルが作成され、そのパッケージの名前に対応するディレクトリ内に保存されます。前述の例では、ビュー・リンクにdevguide.model.queries.viewlinks
パッケージでRequestsAssignedToTechnician
およびHistoryLinesForRequest
という名前が付けられたため、これらのXMLファイルは、プロジェクトのソース・パスの下の./devguide/model/queries/viewlinks
ディレクトリで/RequestsAssignedToTechnician.xml
および/HistoryLinesForRequest.xml
として作成されます。このXMLファイルには、指定したソース・ビュー・オブジェクトとターゲット・ビュー・オブジェクトを関連付けるアソシエーションに関する宣言情報が含まれています。また、ビュー・リンク・コンポーネント定義自体が保存されるのみならず、ビュー・リンク関連内のソース・ビュー・オブジェクトのXML定義も更新され、ビュー・リンクのアクセッサ属性に関する情報が追加されます。
エンティティ・ベースのビュー・オブジェクトの対話的テストは、読取り専用ビュー・オブジェクトと同じ方法で行います。目的のビュー・オブジェクトのインスタンスをアプリケーション・モジュールのデータ・モデルに追加してから、Business Component Browserを使用してこのアプリケーション・モジュールをテストします。
Business Component Browserは、アプリケーション・モジュールを簡単にテストおよびデバッグできる非常に便利なツールです。図7-14に、Business Component Browserのすべてのツールバー・ボタンで実行する操作の概要を示します。
5.10.4.3項「データ・モデルでアクティブなマスター/ディテール調整を有効化する方法」で説明した内容と同じ手順に従い、次のビュー・オブジェクト・インスタンスをSRService
アプリケーション・モジュールのデータ・モデルに追加し、図7-15に示すマスター/ディテール・ビュー・オブジェクトの階層を作成します。
最初に「データ・モデル」ツリーで既存のServiceRequests
ビュー・オブジェクト・インスタンスを選択してから、「選択可能なビュー・オブジェクト」リストでServiceRequests
の子として表示されているServiceHistories
ビュー・オブジェクトをServiceHistories
という名前のディテール・インスタンスとして追加します。
最初に「データ・モデル」ツリーで既存のStaffList
ビュー・オブジェクト・インスタンスを選択してから、「選択可能なビュー・オブジェクト」リストでStaffList
の子として表示されているServiceRequests
ビュー・オブジェクトをAssignedServiceRequests
という名前のディテール・インスタンスとして追加します。
最初に「データ・モデル」ツリーで新しいAssignedServiceRequests
ビュー・オブジェクト・インスタンスを選択してから、「選択可能なビュー・オブジェクト」リストでServiceRequests
の子として表示されているServiceHistories
ビュー・オブジェクトをAssignedServiceHistories
という名前のディテール・インスタンスとして追加します。
図7-15に示すように、SRService
アプリケーション・モジュールのデータ・モデルを設定した場合、そのテスト方法は次のとおりです。
エンティティ・ベースのビュー・オブジェクトのテスト方法:
アプリケーション・ナビゲータでアプリケーション・モジュールを選択し、ポップアップ・メニューから「テスト」を選択します。
Business Component Browserの「接続」ダイアログで「接続」をクリックし、テスト用としてデフォルトのSRServiceLocal
構成を使用します。
注意: デフォルトでは、アプリケーション・モジュールには、AppModuleName Local という名前のデフォルトのローカル構成のみがあります。アプリケーション・モジュール用として別の追加構成を作成してある場合、かわりにこれらの1つを使用してアプリケーション・モジュールをテストするには、「接続」をクリックする前に、「接続」ダイアログのビジネス・コンポーネント構成ドロップダウン・リストから目的の構成を選択します。 |
Business Component Browserを起動する場合、JDeveloperでは別のプロセスでテスター・ツールが起動され、Business Component Browserが表示されます。図7-16に示すとおり、左側に表示されるツリーには、データ・モデルのビュー・オブジェクト・インスタンスの階層が表示されます。また、このツリーには、マスターで現在の行が変更されたときにマスター/ディテールをアクティブに調整するビュー・リンク・インスタンスを示す、マスター・ビュー・オブジェクト・インスタンスとディテール・ビュー・オブジェクト・インスタンスの間の追加ノードが含まれます。
ツリーでHistoryLinesForRequest2
ビュー・リンク・インスタンスをダブルクリックすると、これまでのテスト・セッションで実行されていなかった場合はマスター・オブジェクトが実行され、図7-17に示すように、マスター/ディテール・テスター・パネルが表示されます。ビュー・オブジェクト・ノード上に表示される追加ポップアップ・メニュー項目では、テスター・パネルの削除、およびその他タスクの実行のため、必要に応じて問合せを再実行できます。5.5項「Business Component Browserを使用したビュー・オブジェクトのテスト」でBusiness Component Browserを使用したときも、類似するマスター/ディテール・パネルを使用しました。問合せ結果は表示およびスクロールできます。ここでの重要な違いの1つは、エンティティ・ベースのビュー・オブジェクトの使用による直接的な結果です。読取り専用データを示す無効なUIコントロールが表示されるかわりに、編集可能なフィールドが表示され、作成、挿入、更新、検証、コミットおよびロールバックを自由に実行できます。
マスター/ディテール階層の様々なレベルを参照する、同時に複数のテスター・パネルを開く、また「ウィンドウに結果を表示」ツールバー・ボタンでフレームからタブを独立した別ウィンドウに表示させるなど、複数のビュー・オブジェクトのデータを同時に表示する操作を試してみてください。
Business Component Browserを使用すると、独自のカスタム・ユーザー・インタフェースを作成する前に、エンド・ユーザーによるアプリケーション・モジュールのデータ・モデルとの対話をシミュレートできます。UIページを作成した後でも、Business Component Browserは問題が発生した場合の診断に役立ちます。Business Component Browserで問題を再現して、問題がアプリケーションのビューまたはコントローラ・レイヤーにあるのか、あるいはビジネス・サービス・レイヤーのアプリケーション・モジュール自体にあるのかを確認できます。
図7-17に示すマスター/ディテール・テスター・ページを使用して、アプリケーションの様々な機能領域をテストできます。
テスト・パネルに表示されるプロンプトにより、属性ごとにわかりやすい「ラベル・テキスト」コントロール・ヒントが定義されているかどうかを確認できます。たとえば、ServiceRequests
ビュー・オブジェクト・インスタンスのRequestDate
属性のプロンプトは、「リクエスト日」です。RequestDate
フィールドの編集フィールドの上にマウスを置くと、定義されていればツールチップのコントロール・ヒント・テキストが表示されます。表示されるツールチップの内容は「The date on which the service request was created」です。これは、6.5.1項「属性のコントロール・ヒントの追加方法」でエンティティ・オブジェクトに設定したヒントです。このことは、ServiceRequests
ビュー・オブジェクトには特に新しいコントロール・ヒントを指定していないため、エンティティ・ベースのビュー・オブジェクト属性には基礎となるエンティティ・オブジェクト属性からコントロール・ヒントが継承されたことを示しています。
データをスクロールするか、「ビュー基準の指定」ツールバー・ボタンを使用して、まだ割り当てられていないサービス・リクエストが正しく表示されるかどうかを検証できます。外部結合を使用するよう問合せのWHERE句を正しく変更すると、これらの行は想定どおり表示されます。
AssignedTo
属性を別の技術者のユーザーIDに変更して、Business Component BrowserでStaffList
ビュー・オブジェクト・インスタンスをダブルクリックして有効なスタッフのユーザーIDを参照すると、対応する参照情報が自動的に更新され、新しい技術者が反映されているかどうかを検証できます。
ServiceRequests
のサンプル行のProductName
フィールドおよび2つの「電子メール・アドレス」フィールドの値を調べて、対応するわかりやすい製品名と、リクエストを作成した顧客およびリクエストが割り当てられる技術者双方の電子メール・アドレスを確認できます。また、顧客の電子メール・アドレスと技術者の電子メール・アドレスの両方に「電子メール・アドレス」のUser
エンティティ・オブジェクトから「ラベル・テキスト」ヒントが継承されているという問題にも気付きます。これでは、エンド・ユーザーには電子メール・アドレスがどちらのものであるかがわかりません。
このように、技術者の電子メール・アドレスと顧客の電子メール・アドレスの両方に同じ「電子メール・アドレス」ラベルが継承されて表示されるという問題に対処するには、ServiceRequests
ビュー・オブジェクトを編集し、これらの両方についてビュー・オブジェクト・レベルで「ラベル・テキスト」コントロール・ヒントを定義します。この場合、「ラベル・テキスト」ヒントを、TechnicianEmail
属性は「Technician Email Address」、CustomerEmail
属性は「Customer Email Address」に設定します。ビュー・オブジェクト・レベルで定義したこれらのコントロール・ヒントにより、基礎となるエンティティ・オブジェクトから一般的に継承されるコントロール・ヒントがオーバーライドされたかどうかは、Business Component Browserを使用して検証します。
Closed
サービス・リクエストのStatus
属性値をReopened
に変更してみてください。フィールド値以外のものを指定しようとすると、次の例外が発生します。
(oracle.jbo.AttrSetValException) The status must be Open, Pending, or Closed
6.7項「宣言的な検証規則の使用」で定義した他の簡単な宣言的検証規則に基づいて、ProblemDescription
値をスペースで囲まれた単語urgent
を含めて更新し、エラーの表示を試行できます。
(oracle.jbo.AttrSetValException) Problem Description cannot contain the word urgent
最後に、ProdId
の値として1234
を入力してレンジ検証規則に違反し、次のメッセージの表示を試行できます。
(oracle.jbo.AttrSetValException) Valid product codes are between 100 and 999
ツールバーで「ロールバック」ボタンをクリックし、データを以前の状態に戻してください。
Business Component Browserの起動時、「接続」ダイアログの「プロパティ」タブを開いて、デフォルトのロケール設定をオーバーライドして次のように変更できます。
jbo.default.country
= IT
jbo.default.language
= it
このようにプロパティを設定して、ServiceRequest
エンティティ・オブジェクトのコントロール・ヒントのイタリア語の翻訳が正しく配置されているかどうかを確認できます。特に、Status、Requested OnおよびProblemラベルがそれぞれStato、Aperto IlおよびProblemaラベルになっています。また、RequestDate
のフォーマットが03/12/2006 16:55
などの値から12/03/2006 16:55
に変更されています。
ServiceRequests
ビュー・オブジェクトに対してツールバーの「行の作成」ボタンをクリックし、新しい空白行を作成します。宣言的なデフォルト値があるフィールドの場合、その値が空白行に表示されます。DBSequence
値を持つSvrId
属性の場合、新しい行に一時的な負の数で読取り専用として表示されます。すべての必須フィールドに入力した後(ProdIdフィールドには100
、Requested Byフィールドには300
を入力)、「コミット」ボタンをクリックしてトランザクションをコミットします。コミットが成功した後、トリガーによって割り当てられた実際の主キーがSvrId
フィールドに表示されます。
ビュー・オブジェクトには、基礎となるエンティティ・オブジェクトにマップされた属性以外にも、エンティティ・オブジェクトの属性値にマップされない計算属性を組み込むこともできます。計算属性としては、次の2種類があります。
この項では、この両方の種類の追加方法について説明します。最初にLastCommaFirst
という名前のSQL計算属性、次にFirstDotLast
という名前の一時計算属性をStaffList
ビュー・オブジェクトに追加する方法について説明します。最後に、ビュー・オブジェクトには、エンティティ・オブジェクト・レベルではそれ自体が一時属性である、エンティティにマップされた属性を組み込むことができることを示し、サポートされているすべての組合せが明確であることを確認します。
エンティティ・ベースのビュー・オブジェクトへのSQL計算属性の追加方法:
ビュー・オブジェクト・エディタで「属性」ページを開き、「新規」をクリックします。
LastCommaFirst
などの属性名を入力します。
Javaの「属性」の「型」をString
などの適切な値に設定します。
「列またはSQLにマップ済」チェック・ボックスを選択します。
「式」フィールドにLAST_NAME||', '||FIRST_NAME
などのSQL式を入力します。
属性名と一致するようにSQL列の別名を変更するかどうかを検討します。
データベースの「問合せ列」の「型」を確認し、必要に応じて長さ(または精度/スケール)を調整します。
「OK」をクリックし、属性を作成します。
SQL計算属性を追加し、ビュー・オブジェクト・エディタを終了すると、ビュー・オブジェクトのXMLコンポーネント定義が更新され、新しい属性が反映されます。XMLでは、LastName
のようなエンティティにマップされた属性は次のようになりますが、ほとんどのプロパティはこの属性がマップされた、基礎となるエンティティ属性から継承されます。
<ViewAttribute Name="LastName" IsNotNull="true" EntityAttrName="LastName" EntityUsage="User1" AliasName="LAST_NAME" > </ViewAttribute>
一方、SQL計算属性のViewAttributeタグは、次のようになります。想定どおり、このタグにはEntityUsage
またはEntityAttrName
プロパティはなく、次のようにデータ型情報とともにSQL式が含まれます。
<ViewAttribute Name="LastCommaFirst" IsUpdatable="false" IsPersistent="false" Precision="62" Type="java.lang.String" ColumnType="VARCHAR2" AliasName="FULL_NAME" Expression="LAST_NAME||', '||FIRST_NAME" SQLType="VARCHAR" > </ViewAttribute>
注意: &39; は、アポストロフィを示すXML文字参照で、数値ASCIIコードの39(10進)によって参照します。XMLで同じように構成する必要があるその他のリテラル・テキスト文字には、小なり、大なりおよびアンパサンド文字があります。 |
エンティティ・ベースのビュー・オブジェクトへの一時属性の追加方法:
ビュー・オブジェクト・エディタで「属性」ページを開き、「新規」をクリックします。
FirstDotLast
などの属性名を入力します。
Java「属性」の「型」をString
に設定します。
「列またはSQLにマップ済」チェック・ボックスは未チェックのままにします。
「OK」をクリックし、属性を作成します。
エンティティ・ベースのビュー・オブジェクトに一時エンティティ・オブジェクト属性を追加するには、ビュー・オブジェクト・エディタの「エンティティ・オブジェクト」ページでエンティティにエンティティ・オブジェクトの慣用名があることを最初に確認します。次に、「属性」ページに移動し、目的の属性を「使用可能」リストから「選択済」リストに移動します。これらの手順を使用して、FullName
計算属性をUser
エンティティ・オブジェクトからStaffList
ビュー・オブジェクトに追加できます。
これら3つの属性をStaffList
ビュー・オブジェクト・リストに追加した後、Business Component Browserを使用してSRService
データ・モデルをテストする場合、図7-20のように、その結果を確認できます。
一時属性を追加し、ビュー・オブジェクト・エディタを終了すると、ビュー・オブジェクトのXMLコンポーネント定義が更新され、新しい属性が反映されます。XMLでは、一時属性のViewAttributeタグは、SQL計算属性の場合と類似していますが、Expression
プロパティがありません。
一時属性は、データ値のプレースホルダです。一時属性の「更新可能」プロパティを「新規の間」または「常に」に変更すると、エンド・ユーザーは属性値を入力できるようになります。一時属性に計算値を表示する場合は通常、「更新可能」プロパティを「なし」に設定し、値を計算するカスタムJavaコードを記述します。
ビュー・オブジェクトに一時属性を追加した後、この属性を計算済一時属性にするには、次のようにする必要があります。
ビュー・オブジェクト・エディタの「Java」ページでカスタム・ビュー行クラスを有効にし、アクセッサ・メソッドの生成を選択します。
一時属性のアクセッサ・メソッドの内部で、計算済値を戻すJavaコードを記述します。
たとえば、StaffListRowImpl.java
ビュー行クラスの生成を有効化した後、計算値を戻すJavaコードは、次のようにgetLastCommaFirst()
メソッド内にあります。
// In StaffListRowImpl.java public String getFirstDotLast() { // Commented out this original line since we're not storing the value // return (String) getAttributeInternal(FIRSTDOTLAST); return getFirstName().substring(0,1)+". "+getLastName(); }
注意: 26.8項「属性の自動再計算の実装」では、計算属性が依存している属性値の1つが変更されたときに、計算属性をエンティティ行レベルで再計算するためのコーディング技術について説明します。これと非常に類似した方法をビュー行レベルでも使用すると、計算済ビュー・オブジェクト属性を自動的に再計算できます。 |
ビュー・オブジェクトとエンティティ・オブジェクトにより、すべてのエンタープライズ・アプリケーション開発者が実行する必要のある次の2つの重要なジョブが単独で簡略化されます。
SQL問合せ結果の操作
データベース表内の行の変更および検証
エンティティ・ベースのビュー・オブジェクトは、エンド・ユーザーが表示または変更する必要のあるデータを任意に選択して問い合せることができます。エンド・ユーザーによる変更が可能なデータは、再使用可能なビジネス・ドメイン・レイヤーによって検証および保存されます。開発者として指定する次のような主要な構成要素は、それらを指定するユーザーのみが把握しています。
ビジネス・ドメイン・レイヤーにどのビジネス・ロジックを適用するかの決定
画面に表示する必要のあるデータを記述する問合せの決定
これらの内容により、アプリケーションが一意のものになります。実装の残りの詳細部分は、エンティティ・ベースのビュー・オブジェクトに組み込まれた機能によって処理されます。これまで、Business Component Browserでエンティティ・ベースのビュー・オブジェクトを試用し、その利点を目にしてきましたが、次に必要なのは、その正確な仕組みを理解することです。この項では、エンティティ・ベースのビュー・オブジェクトを介してデータを取得および変更するシナリオについて段階的に説明し、その背景で行われている処理のうち、注目に値するものに焦点を当てます。ただし、詳しい内容に入る前に、行キーの背景や、トランザクションでエンティティ・キャッシュが果す役割について多少理解する必要があります。その後、エンティティ・ベースのビュー・オブジェクトについて詳しく理解する準備が整います。
図7-21に示すとおり、ビュー行を操作する場合、oracle.jbo
パッケージ内のRow
インタフェースを使用します。このインタフェースには、getKey()
と呼ばれるメソッドが含まれます。このメソッドを使用して、任意の行を識別するKey
オブジェクトにアクセスできます。ここで、Row
インタフェースはoracle.jbo.server
パッケージ内のEntity
インタフェースによって拡張されます。このような関係は、エンティティ行という用語が最適であることの具体的な理由となります。エンティティ行がビジネス・ロジックのカプセル化やデータベース・アクセスの処理のための追加機能をサポートしている場合でも、エンティティ行はそのままRow
として扱うことができます。
前述のとおり、ビュー行とエンティティ行はどちらも単一属性または複数属性のキーをサポートしているため、特定のRow
に関連付けられたKey
オブジェクトにより、キーを構成するすべての属性がカプセル化されます。Key
オブジェクトがある場合は、任意の行セットに対してfindByKey()
メソッドを使用して、Key
オブジェクトに基づいて行を検索できます。
注意: エンティティ・ベースのビュー・オブジェクトを定義する場合、デフォルトでは、すべてのエンティティ・オブジェクトの慣用名の主キー属性は、「キー属性」プロパティの設定がtrue としてマークされます。参照エンティティ・オブジェクトの慣用名のキー属性では、「キー属性」プロパティを後で無効にすることをお薦めします。更新可能なエンティティ・オブジェクトの慣用名の主キーに関連するビュー・オブジェクト属性は、複合ビュー行キーの一部である必要があるため、「キー属性」プロパティは無効にできません。 |
アプリケーション・モジュールは、論理的な作業ユニットのトランザクション・コンテナです。アプリケーション・モジュールは実行時に、自分で指定した名前付き構成の情報を使用してデータベース接続を確立し、トランザクション管理を別のTransaction
オブジェクトに委譲します。論理的な作業ユニットには、様々なタイプの複数のエンティティ行の検索や変更が含まれる場合があるため、Transaction
オブジェクトには、現在のユーザーのトランザクションに関連するエンティティ行を保持するための作業領域として、エンティティ・キャッシュが用意されています。各エンティティ・キャッシュには単一のエンティティ・タイプの行が含まれるため、User
エンティティ・オブジェクトとServiceHistory
エンティティ・オブジェクトの両方が関連するトランザクションでは、これらのエンティティ行の作業用コピーが2つの個別キャッシュに保持されます。
エンティティ・オブジェクトの関連エンティティ定義を使用して、アプリケーション・モジュールで既存のエンティティ行を検索し、変更するためのコードを記述できます。図7-22に示すとおり、ServiceRequest
エンティティ・オブジェクトのエンティティ定義に対してfindByPrimaryKey()
をコールすると、そのキーを持つ行を取得できます。この行がまだエンティティ・キャッシュ内にない場合、エンティティ・オブジェクトによってこの行をデータベースから取得するための問合せが実行されます。この問合せにより、基礎となる表からエンティティ・オブジェクトのすべての持続属性が選択され、エンティティ・オブジェクトの主キー属性に対応する列に適したWHERE
句を使用して行が検索されます。同一トランザクション中に同じエンティティ行をキーによって引き続き検索する場合、この行をキャッシュ内で検索することにより、データベースへのトリップを回避します。特定のエンティティ・キャッシュでは、主キーによってエンティティ行が索引付けされます。これにより、キャッシュ内でのエンティティ行の検索処理が速くなります。
アソシエーション・アクセッサ・メソッドを使用して関連エンティティ行にアクセスする場合、これらの行はエンティティ・キャッシュからも取得されます。また、キャッシュ内にない場合はデータベースから取得されます。最後に、エンティティ・キャッシュは、新しいエンティティ行が保存されるまで保持される場所でもあります。つまり、createInstance2()
メソッドを使用してエンティティ定義に対して新しいエンティティ行を作成する場合、このエンティティ行はエンティティ・キャッシュに追加されます。
エンティティ行を作成、変更または削除すると、このエンティティ行はトランザクションの保留中の変更リストに自動的に登録されます。Transaction
オブジェクトに対してcommit()
をコールすると、保留中の変更リストが処理され、まだ無効の新規または変更済エンティティ行があるかどうかが検証されます。保留リスト内のエンティティ行がすべて有効である場合、Transaction
により、データベースのSAVEPOINT
が発行され、データベースへのエンティティ行の保存が調整されます。すべて正常に完了すると、最終的にデータベースのCOMMIT
文が発行されます。問題が発生した場合、Transaction
によってROLLBACK TO SAVEPOINT
が実行され、ユーザーはエラーを修正するか、または再試行できるようになります。
アプリケーション・モジュールで使用されるTransaction
オブジェクトは、単一のエンド・ユーザー・トランザクション用のエンティティ行の作業セットです。設計上、このオブジェクトは共有のグローバル・キャッシュではありません。データベース・エンジン自体は、複数の同時ユーザーを対象とした非常に効率的な共有グローバル・キャッシュです。ADF Business Componentsでは、30年間も行われてきたデータベースの共有グローバル・キャッシュ機能の微調整を繰り返すのではなく、その結果が意識的に採用されています。refresh()
メソッドをコールすると、データベースから単一のエンティティ・オブジェクトのデータをいつでもリフレッシュできます。Transaction
オブジェクトに対してsetClearCacheOnCommit()
またはsetClearCacheOnRollback()
を使用すると、コミットまたはロールバック時にエンティティ・キャッシュを消去するかどうかを制御できます。デフォルトはそれぞれfalse
とtrue
です。また、Transaction
オブジェクトにはclearEntityCache()
メソッドも用意されており、このメソッドを使用して、特定のエンティティ・タイプ(またはすべてのタイプ)のエンティティ行をプログラムで消去できます。エンティティ・キャッシュを消去すると、そのタイプのエンティティ行が主キーによって次に検出されるとき、データベースから最新の状態で取得されるか、次の各項で説明するように、エンティティ・ベースのビュー・オブジェクトによって取得されるようになります。
主キーによるエンティティ行の検索、およびアソシエーション・アクセッサを介した関連エンティティのナビゲーションの次は、エンティティ・ベースのビュー・オブジェクトを使用してこのような処理を行う方法について説明します。エンティティ・ベースのビュー・オブジェクトでは、ビュー・オブジェクトとエンティティ・オブジェクトが果す役割が明確に区別されています。
ビュー・オブジェクトは、データソースです。SQLを使用してデータを取得します。
エンティティ・オブジェクトは、データ・シンクです。データの変更を検証および保存します。
ビュー・オブジェクトとエンティティ・オブジェクトの果す役割は明確に区別されているため、再使用可能なエンティティ・オブジェクトを変更せずに、大量の異なるビュー・オブジェクトを作成できます。つまり、ユーザー・インタフェースで求められる、アプリケーションごとに異なる方法でデータを計画、フィルタ、結合およびソートできます。実際、一部の大規模な開発組織では、エンティティ・オブジェクトのコア・ビジネス・ドメイン・レイヤーを担当するチームは、エンド・ユーザー要件に対応するための特定のアプリケーション・モジュールおよびビュー・オブジェクトを作成したチームとは完全に区別されている場合があります。この非常に柔軟な共生関係は、エンティティ・ベースのビュー・オブジェクトにより、SELECT
リストの列を1つ以上の基礎となるエンティティ・オブジェクトに関連付ける方法をカプセル化するメタデータによって実現されます。
ここで、未処理および保留中のサービス・リクエストを簡単に確認できるページをエンド・ユーザーから要求されるという新しい要件が発生したとします。エンド・ユーザーは、サービス・リクエストID、ステータス、問題の説明、リクエストの解決を担当する技術者、リクエストが未処理であった日数のみの表示を求めています。また、ステータスと割り当てられた技術者を更新可能にする必要があります。図7-23に、この要件に対応したOpenProblemsAndAssignees
という名前の新しいエンティティ・ベースのビュー・オブジェクトを示します。
図内の点線は、問合せのSELECT
リストの列を、ビュー・オブジェクトで使用されるエンティティ・オブジェクトの属性にマップする、ビュー・オブジェクトのXMLコンポーネント定義で取得されるメタデータを示します。
ビュー・オブジェクトとその問合せについては、次の点に注意してください。
前述の例にある、割当て済技術者に関連するアソシエーションに基づいて、プライマリ・エンティティ・オブジェクトの慣用名(ServiceRequest
)のデータとセカンダリの参照エンティティ・オブジェクトの慣用名(User
)のデータが結合されます。
外部結合のServiceRequest.ASSIGNED_TO = Technician.USER_ID (+)
が使用されます。
SQL式のCEIL(SYSDATE - TRUNC(REQUEST_DATE))
に基づいてSQL計算属性DaysOpen
が含まれます。
OpenProblemsAndAssignees
のインスタンスを同じ名前でSRService
のデータ・モデルに追加した後、問合せの実行時に行われる処理を確認できます。読取り専用ビュー・オブジェクトの場合と同様、エンティティ・ベースのビュー・オブジェクトが標準のJDBC(Java Database Connectivity)APIを使用してデータベースにSQL問合せを直接送信し、データベースが結果セットを生成します。ただし、読取り専用ビュー・オブジェクトの場合とは異なり、エンティティ・ベースのビュー・オブジェクトは、データベースの結果セットの各行を取得するときに、関連付けられているエンティティ・オブジェクトの慣用名に基づいて行属性を分割します。この分割の際、各ビュー・オブジェクトのエンティティ・オブジェクトの慣用名に適切なタイプのエンティティ・オブジェクト行が作成され、問合せによって取得された関連属性がこれらのエンティティ行に移入され、このようなエンティティ行がそれぞれ個別のエンティティ・キャッシュに格納されます。これにより、データの複製コピーを格納するかわりに、ビュー行が構成元のエンティティ行部分を指し示すだけですみます。図7-24に示すとおり、結果セットで強調表示された行は、主キー306
のUser
エンティティ行と、主キー112
のServiceRequest
エンティティ行に分割されています。SQL計算属性DaysOpen
はエンティティ・オブジェクトに関連付けられていないため、その値はビュー行に直接格納されます。
前にfindByPrimaryKey()
を使用してキャッシュに取り込んだServiceRequest
エンティティ行には、ServiceRequest
エンティティ・オブジェクトのすべての属性が含まれていました。一方、OpenProblemsAndAssignees
の問合せ結果の行を分割して作成されたServiceRequest
エンティティ行には、問合せ結果に表れる属性の値のみが含まれます。このエンティティ行には、属性の完全セットは含まれません。このようにエンティティ行が部分的に移入されることにより、実行時のパフォーマンスが大幅に最適化されます。
一般的なエンタープライズ・アプリケーションで変更される行と比較して、取得される行の比率が非常に高くなるため、表示する必要がある属性のみをメモリーに読み込むことにより、常にすべての属性をメモリーに読み込む場合よりもメモリーを大幅に節約できます。
最後に、サービス・リクエストについて問い合せた結果の行114
には、技術者が割り当てられていません。このため、ビュー行では、User
エンティティ・オブジェクトについてのエンティティ行部分がnull
になります。
このような方法で問合せ結果のデータを基礎となるエンティティ行の構成要素部分に分割すると、現在のトランザクションが変更された場合、ユーザーに関してUserId
= 306
を使用して問い合せた結果のデータを含むすべての行で整合している結果が表示されるという、重要な利点が得られます。つまり、あるビュー・オブジェクトでユーザー306
のEmail
属性を変更できる場合、ユーザー306
のEmail
属性を表示する任意のエンティティ・ベースのビュー・オブジェクトのすべての行が即座に更新され、変更が反映されます。ユーザー306
に関連付けられたデータがUser
エンティティ・キャッシュで主キー306
を持つエンティティ行に格納されるのは1回のみであるため、ユーザーのEmail
属性を問い合せたビュー行はどれも、この単一のエンティティ行を指し示すことになります。
このような実装の詳細は、ビュー・オブジェクトの行セット内の行を操作するクライアントに対しては完全に非表示にされます。Business Component Browserの場合と同様、クライアントはビュー行を操作して属性を取得し、設定する際、これらの属性がその背後でどのようにエンティティ行に関連付けられているかには気付きません。
たとえば、前述のOpenProblemsAndAssignees
結果セットには、サービス・リクエスト112
に関連付けられた行が含まれます。クライアントがサービス・リクエスト112
のステータスの値をClosed
に更新しようとすると、最終的にはビュー行でsetStatus("Closed")
メソッドがコールされます。図7-25に、基礎となるエンティティ行に応じてこのビュー行属性の変更を自動的に調整する手順を示します。
クライアントがStatus
属性の値をClosed
に設定しようとします。
Status
はServiceRequest
エンティティ・オブジェクトの慣用名にマップされた属性であるため、ビュー行は、ServiceRequest
エンティティ・キャッシュ内で主キー112
を持つ、基礎となるエンティティ行に設定されている属性を委譲します。
ServiceRequest
エンティティ・オブジェクトのStatus
属性に対する属性レベルの検証規則が評価され、検証が失敗した場合は操作に失敗します。
Status
属性の検証規則によってRequestDate
属性がプログラム的に参照されるとします(たとえば、ServiceRequest
は開始日と同日に処理済にはできないというビジネス・ルールを適用する必要がある場合など)。RequestDate
は、問合せによって取得されたServiceRequest
属性ではないため、ServiceRequest
エンティティ・キャッシュ内の部分的に移入されたエンティティ行にはありません。
ビジネス・ルールでエンティティ・オブジェクトのすべての属性を常に参照可能にするため、エンティティ・オブジェクトはこの状況を検出し、主キーによって変更されるエンティティ行についてServiceRequest
エンティティ・オブジェクト属性のセット全体を失敗にします(主キーはビュー・オブジェクトに適用される各エンティティ・オブジェクトの慣用名に表示されます)。
属性レベルの検証がすべて成功した後、エンティティ・オブジェクトでは、最初の属性の変更を許可する前にSERVICE_REQUESTS
表でこの行にロックをかけます。
この行をロックできた場合、行内のStatus
属性の設定が成功し、エンティティ行でこの値が変更されます。
ユーザーがサービス・リクエスト112
に割り当てられている技術者も更新すると、また別の処理が行われます。リクエストは現在、ユーザーID 306
を持つvpatabal
に割り当てられています。ここで、エンド・ユーザーがAssignedTo
属性を300
に設定し、リクエストをsking
に再度割り当てるとします。図7-26に示すように、背後では次の処理が行われます。
クライアントがAssignedTo
属性の値を300
に設定しようとします。
AssignedTo
はServiceRequest
エンティティ・オブジェクトの慣用名にマップされた属性であるため、ビュー行は、ServiceRequest
エンティティ・キャッシュ内で主キー112
を持つ、基礎となるエンティティ行に設定されている属性を委譲します。
ServiceRequest
エンティティ・オブジェクトのAssignedTo
属性に対する属性レベルの検証規則が評価され、検証が失敗した場合は操作に失敗します。
この行はすでにロックされているため、行内のAssignedTo
属性の設定が成功し、エンティティ行でこの値が変更されます。
ServiceRequest
エンティティ・オブジェクトの慣用名のAssignedTo
属性はUser
エンティティ・オブジェクトに対するTechnician
という名前の参照エンティティ・オブジェクトの慣用名に関連付けられているため、外部キー値をこのように変更すると、ビュー行で、ユーザー306の現在のエンティティ行部分が、新しいUserId = 300
に対応するエンティティ行に置き換えられます。これにより、サービス・リクエスト112
のビュー行がsking
のエンティティ行を指し示すよう効率的に変更できるため、ビュー行のEmail
の値が更新され、この新しく割り当てられた技術者の参照情報が正しく反映されます。
ビュー・オブジェクトの問合せを再実行する場合、デフォルトでは、最新の結果セットを読み込むための準備中、現在の行セット内のビュー行は無視されます。ただし、このビュー・オブジェクト操作はエンティティ・キャッシュには直接影響しません。この場合、ビュー・オブジェクトがデータベースに対してSQLを送信することによってプロセスが再開され、データベースの結果セット行が取得され、エンティティ行部分に分割されます。
注意: 通常、問合せを再実行する場合、この操作は最新のデータベース情報を確認するためのものです。かわりにビュー・オブジェクトの問合せ対象をキャッシュ内の既存のエンティティ行のみに制限する場合や、すでにビュー・オブジェクトの行セット内にある既存の行に制限して、データベースのラウンドトリップを回避する場合、その方法は、27.5項「行セットのメモリー内でのソート処理とフィルタ処理の実行」を参照してください。 |
問合せの再実行時におけるこのエンティティ行分割プロセスの一環として、エンティティ行の属性が変更されていない場合、エンティティ・キャッシュ内の値が更新され、新しい問合せ結果の値が反映されます。
現在のトランザクションでエンティティ行属性の値が変更されている場合、問合せ再実行時のエンティティ行の分割プロセスでは、値はリフレッシュされません。現在のトランザクションでコミットされない変更はそのまま残されるため、エンド・ユーザーの論理作業ユニットは保持されます。任意のエンティティ属性値の場合と同様、これらの保留中の変更は、変更済エンティティ行を参照する任意のエンティティ・ベースのビュー・オブジェクト行に継続して表示されます。
図7-27に、このシナリオを示します。現在のトランザクションで保留中の変更について、ユーザーがServiceRequests
ビュー・オブジェクト・インスタンスを使用する別のページにドリルダウンし、サービス・リクエスト112
に関する詳細を取得するとします。このビュー・オブジェクトには4つのエンティティ・オブジェクトの慣用名、つまり、プライマリServiceRequest
の慣用名と、Product
、User
(技術者)およびUser
(顧客)の3つの参照の慣用名があります。問合せ結果がエンティティ行に分割されると、前のOpenProblemsAndAssignees
ビュー行で変更されたものと同じServiceRequest
エンティティ行を指し示すことになります。つまり、エンド・ユーザーには保留中の変更が正しく表示され、サービス・リクエストはSteven King
に割り当てられています。
図7-27はまた、ServiceRequests
ビュー・オブジェクトの問合せにより、OpenProblemsAndAssignees
の場合とは異なる、ユーザーに関する参照情報のサブセットを取得する状況も示しています。OpenProblemsAndAssignees
ビュー・オブジェクトはユーザーのEmail
を問い合せていましたが、ServiceRequests
はユーザーのFirstName
およびLastName
を問い合せています。この図は、このシナリオで実行時に行われる処理を示しています。取得した行の分割時に、すでにキャッシュ内にある、部分的に移入されたエンティティ行とは異なる属性セットがエンティティ行部分に含まれる場合、属性はマージされます。その結果、キャッシュ内で部分的に移入されたエンティティ行に、オーバーラップするユーザー属性のサブセットが統合されます。一方、キャッシュにはなかったJohn Chen(ユーザー308)の場合、新しいエンティティ行にはFirstName
およびLastName
属性のみが含まれ、Email
は含まれません。
変更に問題がなく、トランザクションをコミットするとします。図7-28に示すとおり、次の2つの基本手順があります。
Transaction
オブジェクトにより、保留中の変更リスト内の無効なエンティティ行が検証されます。
保留中の変更リスト内のエンティティ行がデータベースに保存されます。
この図は、1つの変更済エンティティ・オブジェクトの検証処理が他のエンティティ・オブジェクトに対する変更にプログラム的に影響する前に行う、手順1のループを示します。保留中の変更リスト上で無効なエンティティのリストが処理された後、リストにまだエンティティが残されている場合、無効なエンティティのリストのパススルーが行われます。リストのパススルーは最大10回行われます。この時点でまだ無効なエンティティ行がある場合は通常、ビジネス・ロジックにエラーがあり、このビジネス・ロジックを調査する必要があるため、例外がスローされます。
ビュー・オブジェクトとエンティティ・オブジェクトの連携について理解する必要がある最後の側面は、マルチユーザー環境で操作するときに発生する2つの例外です。これらの例外はテスト用に簡単にシミュレートできます。これを行うには、SRService
アプリケーション・モジュールに対してBusiness Component Browserを2回起動するだけですみます(2つ目を起動するとき、最初のインスタンスは終了しません)。次の2つのテストを実行し、これらのマルチユーザー環境で例外がどのように発生するかを確認してください。
1つのBusiness Component Browserのテスターで、既存のサービス・リクエストのステータスを変更し、このフィールドから次に進みます。次に、もう1つのBusiness Component Browserウィンドウで、同じサービス・リクエストを同じ方法で変更してみます。この場合、2人目のユーザーにはoracle.jbo.AlreadyLockedException
が発生します。
テストを繰り返しますが、今度は、Business Component Browserの「接続」ダイアログの「プロパティ」ページでjbo.locking.mode
の値をoptimistic
にオーバーライドしてみます。この場合、エラーは即座には発生せず、2人目のユーザーのコミット時に発生します。
1つのBusiness Component Browserのテスターで、既存のサービス・リクエストのステータスを変更し、このフィールドから次に進みます。次に、もう1つのBusiness Component Browserウィンドウで、同じステータス・リクエストを変更せずに取得してみます。最初のウィンドウに戻り、変更をコミットします。2人目のユーザーが同じサービス・リクエストを変更しようとすると、oracle.jbo.RowInconsistentException
が発生します。これは、2人目のユーザーが行をエンティティ・キャッシュに取得した後に、行が別のユーザーによって変更されてコミットされたためです。
アプリケーション・モジュールのデータ・モデルにアクセスするクライアントの視点からは、読取り専用ビュー・オブジェクトを操作するAPIと、エンティティ・ベースのビュー・オブジェクトを操作するAPIは同一のものです。機能的な主な相違点は、エンティティ・ベースのビュー・オブジェクトでは、ビュー・オブジェクトのデータを完全に更新できる点です。エンティティ・ベースのビュー・オブジェクトを含むアプリケーション・モジュールは、作業ユニットを定義し、トランザクションを管理します。この項では、SRService
アプリケーション・モジュールを操作する簡単な4つのテスト・クライアント・プログラムを使用して次について説明します。
マスター/ディテール/ディテール階層の反復
行の検索および外部キー値の更新
新規サービス・リクエストの作成
行を識別する行キーの取得
例7-1では、次の基本手順を実行しています。
StaffList
ビュー・オブジェクト・インスタンスを検索します。
問合せを実行します。
結果のStaffList
行を反復します。
計算されたFullName
属性の値を取得し、スタッフ・メンバーの氏名を出力します。
ビュー・リンクのアクセッサ属性を使用してServiceRequests
の関連行セットを取得します。
ServiceRequests
行を反復します。
サービス・リクエストの属性値を出力します。
ステータスがClosed
ではない場合、ビュー・リンクのアクセッサ属性を使用してServiceHistories
の関連行セットを取得します。
ServiceHistories
行を反復します。
サービス・リクエストの履歴属性を出力します。
注意: この例では、もう1つ別のネスト・レベルを使用せずに、5.10.4.2項「ビュー・リンク・アクセッサを使用したディテール・コレクションのアクセス方法」で説明した、マスター/ディテール読取り専用ビュー・オブジェクトを反復するTestClient2 プログラムと同じAPIを使用しています。 |
例7-1 マスター/ディテール/ディテール階層の反復
package devguide.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestClient { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); // 1. Find the StaffList view object instance. ViewObject staffList = am.findViewObject("StaffList"); // 2. Execute the query staffList.executeQuery(); // 3. Iterate over the resulting rows while (staffList.hasNext()) { Row staffMember = staffList.next(); // 4. Print the staff member's full name System.out.println("Staff Member: "+staffMember.getAttribute("FullName")); // 5. Get related rowset of ServiceRequests using view link accessor RowSet reqs = (RowSet)staffMember.getAttribute("ServiceRequests"); // 6. Iterate over the ServiceRequests rows while (reqs.hasNext()) { Row svcreq = reqs.next(); // 7. Print out some service request attribute values System.out.println(" ["+svcreq.getAttribute("Status")+"] "+ svcreq.getAttribute("SvrId")+": "+ svcreq.getAttribute("ProblemDescription")); if(!svcreq.getAttribute("Status").equals("Closed")) { // 8. Get related rowset of ServiceHistories RowSet hists = (RowSet)svcreq.getAttribute("ServiceHistories"); // 9. Iterate over the ServiceHistories rows while (hists.hasNext()) { Row hist = hists.next(); // 10. Print out some service request history attributes System.out.println(" "+hist.getAttribute("LineNo")+": "+ hist.getAttribute("Notes")); } } } } Configuration.releaseRootApplicationModule(am,true); } }
このプログラムを実行すると、次の結果が生成されます。
Staff Member: David Austin [Open] 104: Spin cycle not draining 1: Researching issue Staff Member: Bruce Ernst [Closed] 101: Agitator does not work [Pending] 102: Washing Machine does not turn on 1: Called customer to make sure washer was plugged in... 2: We should modify the setup instructions to include... [Open] 108: Freezer full of frost 1: Researching issue Staff Member: Alexander Hunold [Closed] 100: I have noticed that every time I do a... [Closed] 105: Air in dryer not hot :
例7-2では、次の基本手順を実行しています。
ServiceRequests
ビュー・オブジェクト・インスタンスを検索します。
サービス・リクエスト番号101
の行を検索するためのKey
オブジェクトを作成します。
findByKey()
を使用して行を検索します。
サービス・リクエストの属性値を出力します。
無効な値であるReopened
のStatus
属性への割当てを試行します。
ビュー・オブジェクト行はエンティティ・オブジェクトと連携するため、Status
属性に対する検証規則によって例外がスローされ、この不正な変更が阻止されます。
Status
を有効な値のOpen
に設定します。
Status
属性の値を出力し、正常に更新されたことを示します。
割り当てられている技術者の電子メールの現在の値を出力します。
AssignedTo
属性を設定し、サービス・リクエストを技術者番号303
(Alexander Hunold)に再度割り当てます。
新しい技術者が反映された参照情報(TechnicianEmail
)の値を表示します。
ロールバックを発行してトランザクションを取り消します。
例7-2 外部キー値の検索および更新
package devguide.client; import oracle.jbo.ApplicationModule; import oracle.jbo.JboException; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestFindAndUpdate { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); // 1. Find the ServiceRequests view object instance ViewObject vo = am.findViewObject("ServiceRequests"); // 2. Construct a new Key to find ServiceRequest# 101 Key svcReqKey = new Key(new Object[]{101}); // 3. Find the row matching this key Row[] reqsFound = vo.findByKey(svcReqKey,1); if (reqsFound != null && reqsFound.length > 0) { Row svcReq = reqsFound[0]; // 4. Print some service request information String curStatus = (String)svcReq.getAttribute("Status"); System.out.println("Current status is: "+curStatus); try { // 5. Try setting the status to an illegal value svcReq.setAttribute("Status","Reopened"); } catch (JboException ex) { System.out.println("ERROR: "+ex.getMessage()); } // 6. Set the status to a legal value svcReq.setAttribute("Status","Open"); // 7. Show the value of the status was updated successfully System.out.println("Current status is: "+svcReq.getAttribute("Status")); // 8. Show the current value of the assigned technician System.out.println("Assigned: "+svcReq.getAttribute("TechnicianEmail")); // 9. Reassign the service request to technician # 303 svcReq.setAttribute("AssignedTo",303); // Alexander Hunold (technician) // 10. Show the value of the reference info reflects new technician System.out.println("Assigned: "+svcReq.getAttribute("TechnicianEmail")); // 11. Rollback the transaction am.getTransaction().rollback(); System.out.println("Transaction cancelled"); } Configuration.releaseRootApplicationModule(am,true); } }
この例を実行すると、次の結果が生成されます。
Current status is: Closed ERROR: The status must be Open, Pending, or Closed Current status is: Open Assigned: bernst Assigned: ahunold Transaction cancelled
例7-3では、次の基本手順を実行しています。
ServiceRequests
ビュー・オブジェクト・インスタンスを検索します。
新しい行を作成し、行セットに挿入します。
関連するエンティティ・オブジェクトのStatus
属性がデフォルト設定されることを示します。
新しい行の必須属性の値を設定します。
トランザクションをコミットします。
トリガーによって割り当てられたサービス・リクエストIDを取得して表示します。
例7-3 新規サービス・リクエストの作成
package devguide.client; import java.sql.Timestamp; import oracle.jbo.ApplicationModule; import oracle.jbo.Row; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; import oracle.jbo.domain.DBSequence; import oracle.jbo.domain.Date; public class TestCreatingServiceRequest { public static void main(String[] args) throws Throwable { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the ServiceRequests view object instance. ViewObject svcReqs = am.findViewObject("ServiceRequests"); // 2. Create a new row and insert it into the row set Row newSvcReq = svcReqs.createRow(); svcReqs.insertRow(newSvcReq); // 3. Show effect of entity object defaulting for Status attribute System.out.println("Status defaults to: "+newSvcReq.getAttribute("Status")); // 4. Set values for some of the required attributes newSvcReq.setAttribute("CreatedBy",308); // Nancy Greenberg (user) Date now = new Date(new Timestamp(System.currentTimeMillis())); newSvcReq.setAttribute("RequestDate",now); newSvcReq.setAttribute("ProdId",119); // Ice Maker newSvcReq.setAttribute("ProblemDescription","Cubes melt immediately"); // 5. Commit the transaction am.getTransaction().commit(); // 6. Retrieve and display the trigger-assigned service request id DBSequence id = (DBSequence)newSvcReq.getAttribute("SvrId"); System.out.println("Thanks, reference number is "+id.getSequenceNumber()); Configuration.releaseRootApplicationModule(am, true); } }
この例を実行すると、次の結果が生成されます。
Status defaults to: Open Thanks, reference number is 200
例7-4では、次の基本手順を実行しています。
ServiceRequests
ビュー・オブジェクトを検索します。
サービス・リクエスト番号101を検索するためのキーを作成します。
このキーを使用してServiceRequests
行を検索します。
ServiceRequests
行のキーを表示します。
ビュー・リンクのアクセッサ属性を使用してServiceHistories
の行セットにアクセスします。
ServiceHistories
行を反復します。
ServiceHistories
の各行のキーを表示します。
例7-4 行を識別する行キーの取得
package devguide.client; import oracle.jbo.ApplicationModule; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSet; import oracle.jbo.ViewObject; import oracle.jbo.client.Configuration; public class TestFindAndShowKeys { public static void main(String[] args) { String amDef = "devguide.model.SRService"; String config = "SRServiceLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef, config); // 1. Find the ServiceRequests view object ViewObject vo = am.findViewObject("ServiceRequests"); // 2. Construct a key to find service request number 101 Key svcReqKey = new Key(new Object[] { 101 }); // 3. Find the ServiceRequests row with this key Row[] reqsFound = vo.findByKey(svcReqKey, 1); if (reqsFound != null && reqsFound.length > 0) { Row svcReq = reqsFound[0]; // 4. Display the key of the ServiceRequests row showKeyFor(svcReq); // 5. Access row set of ServiceHistories using view link accessor RowSet histories = (RowSet)svcReq.getAttribute("ServiceHistories"); // 6. Iterate over the ServiceHistories row while (histories.hasNext()) { Row historyRow = histories.next(); // 7. Display the key of the current ServiceHistories row showKeyFor(historyRow); } } Configuration.releaseRootApplicationModule(am, true); } private static void showKeyFor(Row r) { // get the key for the row passed in Key k = r.getKey(); // format the key as "(val1,val2)" String keyAttrs=formatKeyAttributeNamesAndValues(k); // get the serialized string format of the key, too String keyStringFmt = r.getKey().toStringFormat(false); System.out.println("Key "+keyAttrs+" has string format "+keyStringFmt); } // Build up "(val1,val2)" string for key attributes private static String formatKeyAttributeNamesAndValues(Key k) { StringBuffer sb = new StringBuffer("("); int attrsInKey = k.getAttributeCount(); for (int i = 0; i < attrsInKey;i++) { if (i > 0 ) sb.append(","); sb.append(k.getAttributeValues()[i]); } sb.append(")"); return sb.toString(); } }
この例を実行すると、次の結果が生成されます。シリアライズされたキーの文字列フォーマットは16進数で、キー内のすべての属性を示す単一文字列に情報が含まれています。
Key (101) has string format 000100000003313031 Key (101,1) has string format 000200000003C2020200000002C102 Key (101,2) has string format 000200000003C2020200000002C103
これまでの説明で、ビュー・オブジェクトには基礎となるエンティティ・オブジェクトに関連付けられるものと関連付けられないものがあることを示しました。この項では、これら2つの基本的なビュー・オブジェクトの実行時の動作の相違点についての概要を示します。
ビュー・オブジェクトに基礎となるエンティティ・オブジェクトの慣用名が1つ以上ある場合、新しい行の作成や、問合せ結果の行の変更または削除が可能です。エンティティ・ベースのビュー・オブジェクトは基礎となるエンティティ・オブジェクトと連携して、ビジネス・ルールを適用し、変更を永続的に保存します。また、エンティティ・ベースのビュー・オブジェクトには次のような特徴があることを説明しました。
同じトランザクションの他のビュー・オブジェクトを介して行われた、関連するエンティティ・オブジェクト属性に対する保留中の変更を即時に反映します。
新しく作成された行の属性値を、基礎となるエンティティ・オブジェクト属性の値で初期化します。
外部キー属性値の変更時に更新された参照情報を反映します。
エンティティ・オブジェクトの慣用名のないビュー・オブジェクトは読取り専用であり、エンティティから導出されたデフォルト値は使用されず、保留中の変更、および更新された参照情報は反映されません。アプリケーションに必要な機能の種類を判断し、それに応じてビュー・オブジェクトを設計する必要があります。通常、SQLベースの検証を行うためのビュー・オブジェクトや、ドロップダウン・リストに有効な選択肢のリストを表示するためのビュー・オブジェクトは読取り専用です。ビュー・オブジェクト行とエンティティ・オブジェクト行の間の調整には、実行時に若干のオーバーヘッドが生じるため、エンティティにマップされたビュー・オブジェクトに用意された機能を必要としない場合、関連するエンティティ・オブジェクトのない読取り専用ビュー・オブジェクトを使用すると、パフォーマンスが多少向上します。
エンティティ・ベースのビュー・オブジェクトは、キーによる行の検索タスクを、基礎となるエンティティ行部分に委譲します。findByKey()
メソッドを使用してキーによってビュー行を検索すると、ビュー行では逆にエンティティ定義のfindByPrimaryKey()
により、ビュー行キーに属性を提供する各エンティティ行が検索されます。
読取り専用ビュー・オブジェクトの場合、ジョブを委譲する基礎となるエンティティ行がないため、このようなスキームは実現できません。読取り専用ビュー・オブジェクトを使用すると、エンティティ・ベースのビュー・オブジェクトに用意されている追加機能を必要とせず、問合せ結果を簡単に反復できます。このため、読取り専用ビュー・オブジェクトでは、ビュー・オブジェクトの行セット・レベルでキーを使用して行を管理することによる若干のオーバーヘッドが発生することは前提としていません。
読取り専用ビュー・オブジェクトでfindByKey()
メソッドを正常に使用するには、次の2つの手順を実行する必要があります。
ビュー・オブジェクトの少なくとも1つの属性に「キー属性」プロパティが設定されていることを確認します。
ビュー・オブジェクトのカスタムJavaクラスを有効にし、次のようにcreate()
メソッドをオーバーライドし、super.create()
のコール後にsetManageRowsByKey(true)
をコールします。
// In custom Java class for read-only view object public void create() { super.create(); setManageRowsByKey(true); }
注意: 読取り専用ビュー・オブジェクトへのデータ・バインディングにADFモデル・レイヤーを使用するアプリケーションの場合、ADF Faces表(またはエンド・ユーザーがページ内をクリックして現在の行を設定する他のコントロール)を正常に操作するには、この追加手順が必要です。また、このことは、読取り専用ビュー・オブジェクトでsetCurrentRowWithKey やsetCurrentRowWithKeyValue などの組込み型のバインディング・レイヤー・アクションを正常に使用する場合にも同様です。これらをすべて要約すると、背後にあるfindByKey() をコールするということになります。 |
25.3.2項「ランタイム・メタデータを使用した汎用機能の実装」では、findByKey()
を想定どおりに機能させる必要があるすべての読取り専用ビュー・オブジェクトに対して、この作業を省略できるようにするための一般的な技術について説明します。