11 ビュー・オブジェクトのプログラムによる操作
この章の内容は次のとおりです。
ビュー・オブジェクトのカスタムJavaクラスの生成
ADFビジネス・コンポーネントのビュー・オブジェクトに関連付けられているカスタムJavaクラスでカスタムJavaコードを使用して、カスタム・ロジックおよび検証メソッドを追加します。
前述したとおり、ビュー・オブジェクトの基本的な問合せ機能はすべて、カスタムJavaコードを使用せずに実行できます。クライアントは、ビュー・オブジェクト開発者の役割でカスタム・コードを実行しなくても、SQL問合せのデータを取得して繰り返すことができます。要するに、多くのビュー・オブジェクトでは、SQL文を定義すればそれで完了です。ただし、必要になった場合に備えて、ビュー・オブジェクトでのカスタムJava生成を有効化する方法を理解しておくことが重要です。たとえば、カスタムJavaクラスでコードを作成する理由には次のようなものがあります。
-
検証メソッドを追加するため(ただし、Groovyスクリプトの式でサポートされているため、それを使用すればJavaは不要)
-
カスタム・ロジックを追加するため
-
組込み動作を拡張するため
通常、カスタム・ビュー・オブジェクトおよびビュー行クラスで記述、使用およびオーバーライドする一般的なコードのクイック・リファレンスは、「ADFビジネス・コンポーネントのよく使用されるメソッド」を参照してください。
カスタム・クラスの生成方法
ビュー・オブジェクトのカスタムJavaクラスの生成を有効にするには、ビュー・オブジェクトの概要エディタの「Java」ページを使用します。次の図に示すように、ビュー・オブジェクトに関連付けることができるオプションのJavaクラスが3つあります。リストの初めの2つは、非常によく使用されます。
-
問合せを実行し、実行のライフサイクルを制御するコンポーネントを表すビュー・オブジェクト・クラス
-
問合せ結果の各行を表すビュー行クラス
ビュー行属性アクセッサの生成
カスタム・ビュー行クラスの生成を有効にするときに、「Javaオプションの選択」ダイアログに示されている「アクセサを含める」チェック・ボックスも選択すると、JDeveloperによりビュー行の各属性に対してgetterメソッドおよびsetterメソッドが生成されます。たとえば、ProductView
ビュー・オブジェクトの場合、対応するカスタムのProductViewRowImpl.java
クラスには次のようなメソッドが生成されます。
public String getName() {...} public void setName(String value) {...} public String getShortDesc() {...} public void setShortDesc(String value) {...} public String getImageId() {...} public void setImageId(Integer value) {...} public String getSuggestedWhlslPrice() {...} public void setSuggestedWhlslPrice(BigDecimal value) {...}
これらのメソッドを使用して行データを操作すると、コンパイル時にデータ型の使用方法が正しいかどうかがチェックされます。つまり、Name
属性の値を取得する次のようなコードは、さらに簡略化できます。
String name = row.getAttribute("Name");
次のようなコードを記述できます。
String name = row.getName();
後者の方法では、Name
ではなく誤ってFullName
と入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error String name = row.getFullName();
ビュー行アクセッサ・メソッドが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both attribute name and type cast are wrong, but compiler cannot catch it Integer name = (Integer)row.getAttribute("FullName");
これには、スペルが正しくない属性名とキャストの型が誤っている戻り値getAttribute()
が両方とも含まれています。Row
インタフェース上で汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
クライアントへのビュー行アクセッサの公開
カスタム・ビュー行クラスの生成を有効にする場合に、ビュー行属性アクセッサを生成する場合は、「クライアントへのアクセッサの公開」チェック・ボックスも選択できます。これにより、生成される付加的なカスタム行インタフェース(アプリケーション・クライアントで使用可能)が、実装クラスに直接依存することなく行のカスタム・メソッドにアクセスできるようになります。
ベスト・プラクティス:
ビジネス・コンポーネントのクライアント・コードを作成する場合、具体的なクラスのかわりにビジネス・サービス・インタフェースを使用する必要があります。実装クラスではなくインタフェースを使用することで、サーバー側の実装が変更されてもクライアント・コードを変更する必要が生じません。クライアント・コードの使用の詳細は、「カスタム・インタフェースのサポートに関する必知事項」を参照してください。
たとえば、ProductView
ビュー・オブジェクトの場合、クライアントへのアクセッサの公開によって、ProductViewRow
という名前のカスタム行インタフェースが生成されます。このインタフェースは、ビュー・オブジェクトがあるパッケージのcommon
サブパッケージに作成されます。行インタフェースを持つことにより、クライアントでは、強く型付けされた方式で問合せ結果の属性にアクセスするコードを記述できるようになります。次の例は、printAllAttributes()
やtestSomethingOnProductRows()
のようなメソッドをコールできるようにProductViewRow
インタフェースへのproductByName.first()
メソッドの結果をキャストする、TestClient
サンプル・クライアント・プログラムを示しています。
package oracle.summit.model.extend; import oracle.jbo.*; import oracle.jbo.client.Configuration; import oracle.summit.model.extend.common.ProductView; import oracle.summit.model.extend.common.ProductViewEx; import oracle.summit.model.extend.common.ProductViewExRow; import oracle.summit.model.extend.common.ProductViewRow; public class TestClient { public static void main(String[] args) { String amDef = "oracle.summit.model.extend.AppModule"; String config = "AppModuleLocal"; ApplicationModule am = Configuration.createRootApplicationModule(amDef,config); ProductView products = (ProductView)am.findViewObject("ProductView1"); products.executeQuery(); ProductViewRow product = (ProductViewRow)products.first(); printAllAttributes(products,product); testSomethingOnProductsRow(product); products = (ProductView)am.findViewObject("ProductViewEx1"); ProductViewEx productsByName = (ProductViewEx)products; productsByName.setbv_ProductName("bunny"); productsByName.executeQuery(); product = (ProductViewRow)productsByName.first(); printAllAttributes(productsByName,product); testSomethingOnProductsRow(product); am.getTransaction().rollback(); Configuration.releaseRootApplicationModule(am,true); } private static void testSomethingOnProductsRow(ProductViewRow product) { try { if (product instanceof ProductViewExRow) { ProductViewExRow productByName = (ProductViewExRow)product; productByName.someExtraFeature("Test"); } product.setName("Q"); System.out.println("Setting the Name attribute to 'Q' succeeded."); } catch (ValidationException v) { System.out.println(v.getLocalizedMessage()); } } private static void printAllAttributes(ViewObject vo, Row r) { String viewObjName = vo.getName(); System.out.println("Printing attribute for a row in VO '"+ viewObjName+"'"); StructureDef def = r.getStructureDef(); StringBuilder sb = new StringBuilder(); int numAttrs = def.getAttributeCount(); AttributeDef[] attrDefs = def.getAttributeDefs(); for (int z = 0; z < numAttrs; z++) { Object value = r.getAttribute(z); sb.append(z > 0 ? " " : "") .append(attrDefs[z].getName()) .append("=") .append(value == null ? "<null>" : value) .append(z < numAttrs - 1 ? "\n" : ""); } System.out.println(sb.toString()); } }
デフォルトのJava生成設定の構成
これまでの説明で、ビュー・オブジェクトの実行時動作をカスタマイズする必要がある場合や、バインド変数またはビュー行属性へ強く型付けされたアクセスのみ行う場合のビュー・オブジェクトのカスタムJavaクラスの生成方法を示しました。
JDeveloperによるJavaクラスの生成を制御するデフォルト設定を変更するには、「ツール」→「プリファレンス」を選択し、「ADFビジネス・コンポーネント」ページを開きます。選択した設定は、今後作成するすべてのビジネス・コンポーネントに適用されます。
ADFビジネス・コンポーネントを初めて使用する開発者には、デフォルトではカスタムJavaクラスを生成しないよう設定することをお薦めします。必要になった場合、その1つのコンポーネントに必要なカスタムJavaコードのみを有効にできます。経験を積むにつれ、どのようなデフォルト設定の組合せが最適であるかがわかるようになります。
カスタム・クラス生成時の処理
1つ以上のカスタムJavaクラスを生成する場合、指定したJavaファイルがJDeveloperにより作成されます。
たとえば、oracle.summit.model.extend.ProductView
という名前のビュー・オブジェクトの場合、カスタムJavaファイルのデフォルト名は、ビュー・オブジェクト・クラスについてはProductViewImpl.java
、ビュー行クラスについてはProductViewRowImpl.java
になります。どちらのファイルも、同じ./model/extend
ディレクトリに、コンポーネントのXMLドキュメント・ファイルとして生成されます。
ビュー・オブジェクトのJava生成オプションは、ビュー・オブジェクトの概要エディタの「Java」ページに後でアクセスしてもそのまま反映されています。XML定義ファイルの場合と同様、このエディタでどのような変更を行っても、カスタムJavaクラスで生成されたコードは最新の状態に保たれます。後でなんらかの理由によりカスタムJavaファイルが必要なくなった場合は、「Java」ページで関連するオプションの選択を解除すると、カスタムJavaファイルを削除できます。
カスタム・クラスに関する必知事項
この項では、カスタムJavaクラスの使用に役立つ追加情報を提供します。
ビュー・オブジェクトのフレームワーク・ベース・クラスについて
XML専用ビュー・オブジェクトを使用する場合、実行時には、その機能はデフォルトのADFビジネス・コンポーネント実装クラスによって提供されます。生成される各カスタムJavaクラスによって適切なADFビジネス・コンポーネントのベース・クラスが自動的に拡張されるため、コードでは、デフォルトの動作を継承し、このクラスを簡単に追加またはカスタマイズできます。ビュー・オブジェクト・クラスはViewObjectImpl
を拡張し、ビュー行クラスはViewRowImpl
を拡張します(これらは両方ともoracle.jbo.server
パッケージ内にあります)。
カスタム・コンポーネント・ファイルに安全に追加できるコード
開発者によっては、以前に大変な経験をしたために、生成されたJavaソース・ファイルに独自のコードを追加することを躊躇する場合があります。JDeveloperによって作成および管理される各カスタムJavaソース・コード・ファイルには、独自のカスタム・コードをこのファイルに追加しても安全であることを示す、次のようなコメントがファイルの上部に記載されています。
// --------------------------------------------------------------------- // --- File generated by ADF Business Components Design Time. // --- Custom code may be added to this class. // --- Warning: Do not modify method signatures of generated methods. // ---------------------------------------------------------------------
コンポーネント・ダイアログで「OK」または「適用」ボタンをクリックしても、ファイルが知らぬ間に再生成されることはありません。かわりに、管理が必要なメソッドはスマートに更新され、独自のカスタム・コードはそのまま残されます。
属性索引およびInvokeAccessor生成コード
ビュー・オブジェクトは、XML専用モードで機能するか、XMLドキュメントとカスタムJavaクラスの組合せを使用して機能するように設計されています。属性値はビュー行クラスのプライベート・メンバー・フィールドには格納されないため、XML専用モードの場合、このようなクラスは存在しません。かわりに、属性はAttributesEnum
タイプとして定義されます。このタイプでは、ビュー・オブジェクトのXMLドキュメントに基づいて、そのファイルの<ViewAttribute>
タグ、関連<ViewLinkAccessor>
タグおよび<ViewAccessor>
タグの順序で、属性名(および各属性のアクセッサ)が指定されます。実行時には、ビュー行の属性値は、ビュー・オブジェクトの属性リスト内における属性の数値的位置による索引が付けられた状態で、ViewRowImpl
ベース・クラスによって管理される構造に格納されます。
多くの場合、このプライベート実装に関する詳細は重要ではありません。ただし、ビュー行のカスタムJavaクラスを有効にする場合、ビュー行クラスでJDeveloperにより自動的に管理される生成コードの一部にこの実装の詳細が関連し、コードの使用目的を理解できます。たとえば、Users
ビュー行のカスタムJavaクラスの場合、次の例に示されているように、属性、ビュー・リンク・アクセッサ属性またはビュー・アクセッサ属性ごとに対応する生成済のAttributesEnum
列挙があります。JDeveloperは、複数の開発者が新しい属性をXMLドキュメントに追加した結果発生する可能性があるマージ競合を防ぐために、定数ではなく列挙定数を定義します。
public class RowImpl extends SummitViewRowImpl implements ProductViewRow { /** * AttributesEnum: generated enum for identifying attributes and accessors. * Do not modify. */ public enum AttributesEnum {...} public static final int ID = AttributesEnum.Id.index(); public static final int NAME = AttributesEnum.Name.index(); public static final int SHORTDESC = AttributesEnum.ShortDesc.index(); public static final int LONGTEXTID = AttributesEnum.LongtextId.index(); public static final int IMAGEID = AttributesEnum.ImageId.index(); public static final int SUGGESTEDWHLSLPRICE = AttributesEnum.SuggestedWhlslPrice.index(); public static final int WHLSLUNITS = AttributesEnum.WhlslUnits.index(); public static final int SOMEVALUE = AttributesEnum.SomeValue.index(); public static final int SFADSF = AttributesEnum.sfadsf.index(); ...
また、自動的に管理される、ビュー行クラスで強く型付けされたgetterメソッドおよびsetterメソッドでは、これらの属性定数は次のように使用されます。
public Integer getId() { return (Integer) getAttributeInternal(ID); // <-- Attribute constant } public void setId(Integer value) { setAttributeInternal(ID, value);// <-- Attribute constant }
AttributesEnum
タイプで定義されたビュー行属性定数に関連する自動管理コードの最後の2つの側面は、getAttrInvokeAccessor()
メソッドおよびsetAttrInvokeAccessor()
メソッドです。これらのメソッドは数値順索引によって属性アクセスのパフォーマンスを最適化します。これは、ViewRowImpl
ベース・クラスの汎用コードが属性値にアクセスするときの通常の方法です。getAttrInvokeAccessor()
メソッドの例は、次のProductViewRowImpl.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); }
属性に関連して生成されたこのコードに関する経験則は、次のとおりです。
親アプリケーション・モジュール・タイプにおける依存性の作成の回避
ビュー・オブジェクト・クラスまたはビュー・オブジェクト行クラスで実装するカスタム・メソッドは、アプリケーション・モジュールの戻り型に依存できません。実行時には、ある場合、そのような依存性を持って実行されるメソッドがClassCastException
をスローすることがあります。戻されたアプリケーション・モジュールが、期待された型と一致しないからです。そのため、実装するカスタム・メソッドには、次に示すように、特定のアプリケーション・モジュールやビュー・オブジェクトの実装を取得するコードを記述しないことをお薦めします。
((MyAM)getRootApplicationModule()).getMyVO
特に、次のシナリオにおいて、上のコードがClassCastException
で失敗します。
-
ADFビジネス・コンポーネント・フレームワークによって、ビュー・オブジェクトが定義されているコンテナとは別のコンテナ・アプリケーション・モジュールでインスタンス化される場合。通常、ビュー・オブジェクトは、その使用方法を宣言するコンテナ・アプリケーション・モジュール(XML定義)でインスタンス化されますが、ADFビジネス・コンポーネント・ランタイムでは、各アプリケーション・モジュールに関連付けられたコンテナを固定することが保証されていません。つまり、親アプリケーション・モジュール・タイプで依存性を持つメソッドを実装しても、それらのメソッドが一貫性を持って実行されない場合があります。
-
ルート・アプリケーション・モジュールの下に、手動でアプリケーション・モジュールをネストする場合。この場合、ネストされたアプリケーション・モジュールが同じ
Transaction
オブジェクトを共有するため、前述のコードによって、期待されるアプリケーション・モジュールの型が戻される保証はありません。 -
ADFビジネス・コンポーネント・フレームワークの実装がリリースを追うごとに変化する場合。たとえば、以前のリリースでは、アプリケーションがADFタスク・フローを使用して定義した宣言トランザクションを制御するために、フレームワークで内部的なルート・アプリケーション・モジュールが作成されていました。
複数の名前付きビュー基準のプログラム的処理
ADFビジネス・コンポーネントを使用すると、複数のビュー基準を定義し、それらの組合せを実行時にビュー・オブジェクトに適用できます。
ビュー・オブジェクトの概要エディタで複数の名前付きビュー基準を定義し、必要に応じて実行時にその任意の組合せをビュー・オブジェクトに選択的に適用できます。設計時の名前付きビュー基準の処理の詳細は、「名前付きビュー基準を宣言的に作成する方法」を参照してください。
ノート:
この項の例では、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.multinamedviewcriteria
パッケージを参照します。
名前付きビュー基準の適用
名前付きビュー基準を適用するには、setApplyViewCriteriaNames()
メソッドを使用します。このメソッドは、適用する基準の名前のString配列を受け取ります。複数の名前付き基準を適用する場合は、実行時に生成されるWHERE句の中でAND
により結合されます。setApplyViewCriteriaNames()
メソッドで適用した新しいビュー基準は、以前適用されたすべてのビュー基準を上書きします。以前適用されたビュー基準に単一のビュー基準を追加する場合、setApplyViewCriteriaName()
メソッドを使用することもできます。
複数の名前付きビュー基準を適用する必要があるとき、ビュー・オブジェクトのクライアント・インタフェースでカスタム・メソッドを公開し、適用する名前付きビュー基準の組合せをカプセル化できます。たとえば、次の例で示されているカスタム・メソッドshowCustomersForSalesRep()
、showCustomersForCreditRating()
およびshowCustomersForSalesRepForCreditRating()
は、それぞれがsetApplyViewCriteriaNames()
メソッドを使用して、名前付きビュー基準の適切な組合せを適用しています。これらのメソッドをビュー・オブジェクトのクライアント・インタフェースで公開すると、実行時に、クライアントは必要に応じてこれらのメソッドを呼び出し、ビュー・オブジェクトによって表示される情報を変更できます。
// In CustomerViewImpl.java public void showCustomersForSalesRep() { // reset the view criteria setApplyViewCriteriaNames(null); // set the view criteria to show all the customers for the given sales rep setApplyViewCriteriaName("SalesRepIsCriteria"); executeQuery(); } public void showCustomersForCreditRating() { setApplyViewCriteriaNames(null); setApplyViewCriteriaName("CreditRatingIsCriteria"); executeQuery(); } public void showCustomersForSalesRepForCreditRating() { // reset the view criteria setApplyViewCriteriaNames(null); // apply both view criteria to show all the customers that match both setApplyViewCriteriaNames(new String[]{"SalesRepIsCriteria", "CreditRatingIsCriteria"}); executeQuery(); }
1つ以上の名前付きビュー基準の適用に関する必知事項
setApplyViewCriteriaNames()
のかわりにappendViewCriteriaName()
メソッドを使用して、1つ以上の名前付きビュー基準を適用することもできます。このメソッドは、適用する基準の名前のString配列を受け入れるsetApplyViewCriteriaNames()
と同様に機能します。複数の名前付き基準を適用する場合は、実行時に生成されるWHERE句の中でAND
により結合されます。appendViewCriteriaName()
メソッドで適用した新しいビュー基準により、ビュー・オブジェクトのすでに適用されているビュー基準リストに新規のビュー基準が追加されます。以前適用されたビュー基準に単一のビュー基準を追加する場合、appendViewCriteriaName()
メソッドを使用することもできます。
次のコード例は、ビュー・オブジェクトに追加できる様々な新規APIを示しています。
-
適用されるビュー基準の既存のリストに、指定されたビュー基準名を追加する場合。
public void appendViewCriteriaName(String criteria);
-
ビュー基準の既存のリストに、指定されたビュー基準名のリストを追加する場合。
public void appendViewCriteriaName(String[] criterias);
-
基準の既存のリストに、指定されたビュー基準オブジェクトを追加する場合。
public void appendViewCriteriaObject(ViewCriteria criteria);
-
適用されるビュー基準の既存のリストに、指定されたビュー基準オブジェクトのリストを追加する場合。
public void appendViewCriteriaObject(ViewCriteria[] criterias);
-
適用されるビュー基準の既存のリストに、指定されたビュー基準式を追加する場合。
public void appendViewCriteriaExpression(String expr);
-
すべてのビュー基準オブジェクトを定義状態にリセットする場合。
public void resetAllViewCriteria();
実行時に作成されたビュー基準も削除されます。
-
指定されたビュー基準オブジェクトをその定義状態にリセットする場合。
public void resetViewCriteriaName(String viewcriteria);
適用されたすべての名前付きビュー基準の除去
現在適用されている名前付きビュー基準を除去するには、setApplyViewCriteriaNames(null)
を使用します。たとえば、次の例のshowAllCustomers()
メソッドをCustomersView
ビュー・オブジェクトに追加し、クライアント・インタフェースで公開できます。このようにすると、クライアントは必要に応じてフィルタ処理されないデータのビューに戻すことができます。
行レベル・バインド変数値が行セットに対してすでに適用されている可能性があるため、設計時ビュー基準は削除しないでください。確実を期すため、設計時にビュー・アクセッサに対して定義された名前付きビュー基準は、ビュー基準のライフサイクル・メソッドによって削除されないように、ビュー・オブジェクト・インスタンスに必須のビュー基準として適用されます。
// In CustomerViewImpl.java public void showAllCustomers() { setApplyViewCriteriaNames(null); executeQuery(); }
ノート:
setApplyViewCriterias(null)
は適用されているすべてのビュー基準を除去しますが、後でビュー基準の任意の組合せを再度適用できます。これに対し、clearViewCriterias()
メソッドはすべての名前付きビュー基準を削除します。clearViewCriterias()
を呼び出した後、ビュー基準を適用するには、まず再びputViewCriteria()
を使用して新しい名前付き基準を定義する必要があります。
実行時の名前付き基準の使用
実行時に、アプリケーションでは、単一のビジネス・オブジェクト・インタフェースで異なるクライアント・メソッドを呼び出し、異なるフィルタ処理が行われたデータのセットを戻すことができます。次の例は、前述のCustomerView
ビュー・オブジェクトを使用するテスト・クライアント・クラスの興味深い行を示しています。showResults()
メソッドは、ビュー・オブジェクトの行を反復処理して属性を表示するヘルパー・メソッドです。
// In MultiNamedViewCriteriaTestClient.java CustomerView vo = (CustomerView) am.findViewObject("CustomerView"); // Show list of all rows in the CustomerView, without applying any view criteria. showResults(vo,"All Customers"); // Use type safe set method to set the value of the bind variable used by the // view criteria vo.setbv_SalesRepId(12); vo.showCustomersForSalesRep(); showResults(vo, "All Customers with SalesRepId = 12"); // Set the sales rep id to 11 and show the results. vo.setbv_SalesRepId(11); vo.showCustomersForSalesRep(); showResults(vo, "All Customers with SalesRepId = 11"); // use type safe method to set value of bind variable used by the view criteria vo.setbv_CreditRatingId(2); // This method applies both view criteria to the VO so the results match // both criteria, but we do not set the bind variable for sales rep as it // was already set. The results will show all the customers with a sales // rep of 11 and a credit rating of 2. vo.showCustomersForSalesRepForCreditRating(); showResults(vo, "Customers with SalesRepId = 11 and CreditRating = 2"); // Now show all the customers again. The showAllCustomers method resets // the view criteria. vo.showAllCustomers(); showResults(vo, "All Customers");
MultiNamedViewCriteriaTestClient.java
を実行すると、次のような出力が生成されます。
---All Customers --- Unisports Sao Paulo [12, 1] Simms Athletics Osaka [14, 4] Delhi Sports New Delhi [14, 2] ... ---All Customers with SalesRepId = 12 --- Unisports Sao Paulo [12, 1] Futbol Sonora Nogales [12, 1] Stuffz Sporting Goods Madison [12, 3] ... ---All Customers with SalesRepId = 11 --- Womansport Seattle [11, 3] Beisbol Si! San Pedro de Macon's [11, 1] Big John's Sports Emporium San Francisco [11, 1] Ojibway Retail Buffalo [11, 3] ... ---Customers with SalesRepId = 11 and CreditRating = 2 --- Max Gear New York [11, 2] MoreAndMoreStuffz Dallas [11, 2] Schindler's Sports St Louis [11, 2] ... ---All Customers --- Unisports Sao Paulo [12, 1] Simms Athletics Osaka [14, 4] Delhi Sports New Delhi [14, 2] ...
行セットのメモリー内でのソート処理とフィルタ処理の実行
ADFビジネス・コンポーネントを使用すると、データベースからではなく、メモリー内のデータを検索するように、ビュー・オブジェクトを構成できます。インメモリー検索およびソートは、トランザクション効率がよく、費用効果が高いです。
ビュー・オブジェクト行は、次のAPIを使用してソートできます。
-
setSortBy
-
setOrderByClause
-
setOrderByorSortBy
SortBy()
メソッドはメモリー内ソート処理で使用し、OrderBy()
メソッドはデータベース・レイヤーでの行のソートで使用します。
デフォルトでは、ビュー・オブジェクトはデータベースに対して問合せを実行し、結果の行セットに行を取得します。ただし、ビュー・オブジェクトを使用して、メモリー内で検索とソートを実行し、データベースへの不必要なトリップを避けることもできます。一時属性がsetSortBy
APIに渡された場合、行はメモリー内でのみソートされます。この種の操作は、ビュー・オブジェクト行セットのまだポストされていない新しい行をソートおよびフィルタ処理する場合に最適です。この操作が行われないと、ポストされていない行は行セットの先頭に追加されます。永続属性がsetSortBy
APIに渡された場合、その永続属性に対してのみデータベースのORDER BY句が生成されるので、行はまずデータベース・レイヤーでソートされます。一時属性と永続属性の両方が渡された場合は、永続属性にデータベースのORDER BY句が生成され、データベース・ソートされた行がメモリー内にフェッチされて、そのフェッチされた行をADF BCが一時属性と永続属性の両方を使用して指定の順序でソートします。
ノート:
この項の例では、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.inMemoryOperations
パッケージを参照します。
ビュー・オブジェクトのSQLモード
ビュー・オブジェクトの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()
メソッドを使用してソート順序を制御します。または、ビュー・オブジェクトのプロパティPROP_ALWAYS_USE_SORT
をtrue
に設定して、システムがデフォルトでメモリー内ソートを処理できるようにすることもできます。
ソート順序を制御する場合は、SQLのORDER BY句のようなソート式を渡します。ただし、表の列名を参照するかわりに、ビュー・オブジェクトの属性名を使用します。たとえば、CreditRatingId
およびZipCode
という名前の属性を含むビュー・オブジェクトの場合、次のメソッドを呼び出すことで、最初にCustomer
の降順で、次にDaysOpen
で、ビュー・オブジェクトをソートできます。
setSortBy("CreditRatingId desc, ZipCode");
または、次のように、ソート処理句の中ではゼロから始まる属性インデックス位置を使用することもできます。
setSortBy("3 desc, 2");
setSortBy()
メソッドを呼び出した後、次にexecuteQuery()
メソッドを呼び出すと行がソートされます。ビュー・オブジェクトは、このソート句を適切な書式に変換し、ビュー・オブジェクトのSQLモードに基づいた行の並替えに使用します。デフォルトのSQLモードを使用すると、SortBy
句は適切なORDER BY
句に変換され、データベースに送信されるSQL文の一部として使用されます。いずれかのメモリー内SQLモードの場合は、SortBy
句は1つまたは複数のSortCriteria
オブジェクトに変換され、メモリー内ソートの実行で使用されます。
ノート:
SQL ORDER BY式では列名の大文字と小文字は区別されませんが、SortBy
式の属性名では大文字と小文字が区別されます。
メモリー内ソートでのsetSortByとsetQueryModeの組合せ
setSortBy()
およびsetQueryMode()
メソッドを使用して、読取り専用のビュー・オブジェクトによって生成された行に対してメモリー内ソートを実行できます。次の例は、読取り専用のビュー・オブジェクトCustomersInTx
によって生成された行に対してメモリー内ソートを実行するために、setSortBy()
とsetQueryMode()
を使用する、TestClientSetSortBy
クラスの興味深いコード行を示しています。
// In TestClientSetSortBy.java am.getTransaction().executeCommand("ALTER SESSION SET SQL_TRACE TRUE"); ViewObject vo = am.findViewObject("CustomersInTx"); vo.executeQuery(); showRows(vo,"Initial database results"); vo.setSortBy("Name"); vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS); vo.executeQuery(); showRows(vo,"After in-memory sorting by Customer Name "); vo.setSortBy("CreditRatingId desc, ZipCode"); vo.executeQuery(); showRows(vo,"After in-memory sorting by CreditRating desc, and ZipCode");
この例を実行すると、次のような結果が生成されます。
--- Initial database results --- 36,Great Gear,1500 Barton Springs Rd,78704, 2, TX 37,Acme Outfitters,3500 Guadalupe St,78705, 3, TX 38,Athena's Closet,1209 Red River St,78701, 1, TX 39,BuyMyJunk,5610 E Mockingbird Ln,75206, 3, TX ... --- After in-memory sorting by Customer Name --- 37,Acme Outfitters,3500 Guadalupe St,78705, 3, TX 38,Athena's Closet,1209 Red River St,78701, 1, TX 43,Big Bad Sports,2920 Hillcroft St,77057, 1, TX 39,BuyMyJunk,5610 E Mockingbird Ln,75206, 3, TX ... --- After in-memory sorting by CreditRating desc, and ZipCode --- 42,Field Importers,2111 Norfolk St,77098, 4, TX 39,BuyMyJunk,5610 E Mockingbird Ln,75206, 3, TX 37,Acme Outfitters,3500 Guadalupe St,78705, 3, TX 41,MoreAndMoreStuffz,3501 McKinney Ave,75204, 2, TX ...
executeCommand()
のコールを含む前述のテスト・クライアントの1行目では、ALTER SESSION SET SQL TRACE
コマンドを発行して、現在のデータベース・セッションでSQLトレースを有効にしています。これにより、Oracleデータベースは、実行されるすべてのSQL文をサーバー側のトレース・ファイルに記録します。データベースが文を解析した回数や、問合せ結果を取得する間に行のバッチをフェッチするためにクライアントが行ったラウンドトリップの回数など、各SQL文のテキストに関する情報が記録されます。
ノート:
ALTER SESSION
コマンドを実行してSQL出力のトレースを行うには、DBAによるSummit
アカウントへの権限付与が必要になる場合があります。
生成されたトレース・ファイルは、データベースに付属するTKPROF
ユーティリティを使用して書式設定できます。
tkprof xe_ora_3916.trc trace.prf
TKPROF
ユーティリティでの作業の詳細は、『Oracle Database SQLチューニング・ガイド』の「アプリケーション・トレースの実行」の章の、SQLトレースとTKPROFの理解に関する項およびSQLトレース機能とTKPROFの使用に関する項を参照してください。
これにより、trace.prf
ファイルが生成されます。このファイルには、CustomersInTx
ビュー・オブジェクトによって実行されたSQL文に関する、次の例のような興味深い情報が含まれます。最初に1回の実行で6行のデータを問い合せてデータベースからフェッチした後、その後に行われたこれらの結果に対する2回のソートでは実行が行われていないことがわかります。コードでSQLモードがViewObject.QUERY_MODE_SCAN_VIEW_ROWS
に設定されているため、setSortBy()
の後のexecuteQuery()
ではメモリー内でソートが実行されました。
SELECT S_CUSTOMER.ID ID, S_CUSTOMER.NAME NAME, S_CUSTOMER.PHONE PHONE, S_CUSTOMER.ADDRESS ADDRESS, S_CUSTOMER.CITY CITY, S_CUSTOMER.STATE STATE, S_CUSTOMER.ZIP_CODE ZIP_CODE, S_CUSTOMER.CREDIT_RATING_ID CREDIT_RATING_ID FROM S_CUSTOMER WHERE S_CUSTOMER.STATE = 'TX' call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.02 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 9 0.00 0.00 0 14 0 8 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 11 0.00 0.02 0 14 0 8
簡素化されたメモリー内ソート処理
メモリー内の行ソート方法を制御する必要がない場合は、ビュー・オブジェクト・インタフェースに用意されたPROP_ALWAYS_USE_SORT
プロパティを使用して、メモリー内の行をソースとして使用するようにビュー・オブジェクトの問合せモードを変更したときに、ソート処理が強制されるようにできます。このプロパティは、ビュー・プロパティの行セットが小さく、クライアントがメモリー内ソートの実行を必要としている場合に使用できます。次のように、問合せモードを設定後、このビュー・オブジェクト・プロパティを設定して、デフォルトのメモリー内ソート処理を指定します。
vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS); vo.setProperty(ViewObject.PROP_ALWAYS_USE_SORT, "true");
メモリー内ソート処理の拡張ポイント
メモリー内での行のソート方法をカスタマイズする必要がある場合は、次の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
ビュー基準によるメモリー内フィルタ処理で使用できる演算子を表11-1に示します。カッコを使用して副次式をグループ化でき、副次式の間にはAND
とOR
の演算子を使用できます。
表11-1 ビュー基準によるメモリー内フィルタ処理でサポートされているSQL演算子
演算子 | 操作 |
---|---|
|
比較 |
|
論理否定 |
|
論理積 |
|
論理和 |
次の例は、前述のデータベースに対するものとメモリー内の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
// 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 reapply 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
オブジェクトを作成するには、次のように、問合せ条件式をコンストラクタに渡します。
RowMatch rm = new RowMatch("LastName = 'Popp' or (FirstName like 'A%' and LastName like 'K%')");
SortBy
句と同じように、ビュー・オブジェクトの属性名に関するRowMatch
の式を、表11-2に示す、サポートされる演算子を使用して表現します。カッコを使用して副次式をグループ化でき、副次式の間にはAND
とOR
の演算子を使用できます。
表11-2 RowMatchによるメモリー内フィルタ処理でサポートされているSQL演算子
演算子 | 操作 |
---|---|
|
比較 |
|
論理否定 論理否定演算
|
|
論理積 |
|
論理和 |
表11-3に示すように、RowMatch
式ではSQL機能の一部も使用できます。
表11-3 RowMatchによるメモリー内フィルタ処理でサポートされているSQL関数
演算子 | 操作 |
---|---|
|
文字列内のすべての文字を大文字に変換する。 |
|
数または日付を文字列に変換する。 |
|
文字列を日付形式に変換する。 |
|
文字列をタイムスタンプに変換する。 |
ノート:
SQL問合せ条件では列名の大文字と小文字は区別されませんが、RowMatch
式の属性名では大文字と小文字が区別されます。
ビュー・オブジェクトへのRowMatchの適用
ビュー・オブジェクトにRowMatch
を適用するには、setRowMatch()
メソッドを呼び出します。ViewCriteria
とは異なり、RowMatch
はメモリー内フィルタ処理にのみ使用するため、設定する一致モードはありません。サポートされるすべてのSQLモードのビュー・オブジェクトに対してRowMatch
を使用でき、適用した結果は、次にexecuteQuery()
メソッドを呼び出したときに表示されます。
ビュー・オブジェクトにRowMatch
を適用するとき、RowMatch
の式では、SQL文で使用するものと同じ:VarName
表記法を使用して、ビュー・オブジェクトの名前付きバインド変数を参照できます。たとえば、ビュー・オブジェクトにStatusCode
という名前の名前付きバインド変数がある場合は、次の式でRowMatchを適用できます。
Status = :StatusCode or :StatusCode = '%'
次の例は、活動中の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]
// 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 requerying to see a full list again");
個別の行をテストするためのRowMatchの使用
RowMatch
を使用して行セットをフィルタするのみでなく、rowQualifies()
メソッドを使用すると、それがカプセル化している基準に個別の行が一致するかどうかをテストすることもできます。次に例を示します。
RowMatch rowMatch = new RowMatch("CountryId = 'US'"); if (rowMatch.rowQualifies(row)) { System.out.println("Customer is from the United States "); }
データベースからフェッチされた行に対するRowMatchの影響
RowMatch
を適用すると、ビュー・オブジェクトのSQLモードがデータベースからの行の取得に設定されている場合、executeQuery()
を呼び出すと、フェッチされた行にRowMatch
が適用されます。フェッチされた行が認定されない場合、その行は行セットに追加されません。
SQLのWHERE
句とは異なり、RowMatch
は、一時ビュー・オブジェクト属性およびまだポストされていない属性値を含む式でも評価できます。これは、Javaで値が計算される一時ビュー行属性を含むRowMatch
式に基づいて、問い合された行をフィルタするのに便利です。ただし、この興味深い機能は、アプリケーションが大きな行セットを処理する必要がある場合には注意して使用する必要があります。最初にデータベース・レベルのフィルタ処理を使用してできるかぎり小さい行セットを取得した後、RowMatchを使用してメモリーでそのリストからサブセットを作成することをお薦めします。
XMLの読取りと書込み
ADFビジネス・コンポーネント内のビュー・オブジェクトでは、XMLドキュメントでのデータの読取りおよび書込みをサポートします。このため、ビュー・オブジェクトではXMLドキュメントに基づいたCRUD操作を実行できます。
Worldwide Web Consortium (W3C)が定めるExtensible Markup Language (XML)標準では、電子データ交換用の言語に依存しない方式が定義されています。厳密な一連の規則により、判読可能なテキスト・ドキュメントを使用して、データに固有の構造を簡単にエンコードし、正確に解釈できます。
ビュー・オブジェクトでは、問い合されたデータに基づいて、XMLドキュメントを記述することができます。また、XMLドキュメントを読み取って、挿入、更新、削除などの変更をデータに適用することもできます。ビュー・リンクを導入すると、このXML機能は、任意の複雑さのマスター/ディテール階層の複数レベルでネストされた情報の読取りと書込みをサポートします。ビュー・オブジェクトが生成および使用するXMLは正規書式に従いますが、ビュー・オブジェクトのXML機能とXML Stylesheet Language Transformations (XSLT)を組み合せることで、正規のXML書式と使用する必要のある任意の書式の間の変換を簡単に行うことができます。
ノート:
この項の例では、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.readandwrite
パッケージを参照します。
問合せ済データのXMLの生成方法
ビュー・オブジェクトから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
パラメータ・フラグに基づくデータ・モデルにあるもののみをレンダリングすることを指示できます。
XMLを生成するときに行われる処理
writeXML()
を使用してXMLを生成すると、ビュー・オブジェクトは最初に、ラップするXML要素を作成します。そのデフォルト名は、ビュー・オブジェクト定義の名前と一致しています。たとえば、CustomersView
ビュー・オブジェクトの場合は、生成されるXMLをラップする一番外側のタグはCustomersViewタグになります。
次に、ビュー・オブジェクトは、適切な行の属性データをXML要素に変換します。デフォルトでは、各行のデータは、ビュー・オブジェクトの名前に接尾辞Row
を付加した名前の行要素でラップされます。たとえば、CustomersView
という名前のビュー・オブジェクトの各データ行は、CustomersViewRow
要素でラップされます。各行の属性データを表す要素は、この行要素内にネストされた子として記述されます。
いずれかの属性がビュー・リンク・アクセッサ属性であり、writeXML()
に渡されたパラメータによって有効になっている場合は、ビュー・リンク・アクセッサによって返されるディテール行セットのデータも含められます。このネストされたデータは、ビュー・リンク・アクセッサ属性の名前によって名前が決まる要素でラップされます。writeXML()
メソッドの戻り値は、W3Cの標準のNode
インタフェースを実装するオブジェクトで、生成されたXMLのルート要素を表します。
ノート:
writeXML()
メソッドは、ビュー・リンク・アクセッサ属性を使用して、プログラムでディテール・コレクションにアクセスします。データ・モデルにビュー・リンクのインスタンスを追加する必要はありません。
たとえば、CustomersView
ビュー・オブジェクト・インスタンスのすべての行に対するXML要素を生成し、存在するレベルの深さまでビュー・リンク・アクセッサをたどる場合は、次の例に示すようなコードが必要です。
ViewObject vo = am.findViewObject("CustomersView"); printXML(vo.writeXML(-1,XMLInterface.XML_OPT_ALL_ROWS));
CustomersView
ビュー・オブジェクトは、その個人が作成したオーダーを表すOrders
ビュー・オブジェクトにリンクされています。そして、Orders
ビュー・オブジェクトは、顧客がオーダーした品目の詳細を提供するOrderItems
ビュー・オブジェクトにリンクされています。次の例のコードを実行すると、ビュー・リンクによって定義されているネストされた構造を反映した、次の例に示すXMLが生成されます。
... <CustomersViewRow> <Id>211</Id> <Name>Kuhn's Sports</Name> <Phone>42-111292</Phone> <Address>7 Modrany</Address> <City>Prague</City> <CountryId>11</CountryId> <CreditRatingId>2</CreditRatingId> <SalesRepId>15</SalesRepId> <OrdersView> <OrdersViewRow> <Id>107</Id> <CustomerId>211</CustomerId> <DateOrdered>2012-12-13</DateOrdered> <DateShipped>2012-12-14</DateShipped> <SalesRepId>15</SalesRepId> <Total>142171</Total> <PaymentTypeId>2</PaymentTypeId> <PaymentOptionId>1082</PaymentOptionId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>183</Id> <CustomerId>211</CustomerId> <DateOrdered>2013-02-24</DateOrdered> <DateShipped>2013-02-27</DateShipped> <SalesRepId>13</SalesRepId> <Total>3742</Total> <PaymentTypeId>1</PaymentTypeId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>184</Id> <CustomerId>211</CustomerId> <DateOrdered>2012-11-19</DateOrdered> <DateShipped>2012-11-22</DateShipped> <SalesRepId>12</SalesRepId> <Total>987</Total> <PaymentTypeId>1</PaymentTypeId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>185</Id> <CustomerId>211</CustomerId> <DateOrdered>2012-08-08</DateOrdered> <DateShipped>2012-08-12</DateShipped> <SalesRepId>12</SalesRepId> <Total>2525.25</Total> <PaymentTypeId>1</PaymentTypeId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>186</Id> <CustomerId>211</CustomerId> <DateOrdered>2012-04-26</DateOrdered> <DateShipped>2012-04-30</DateShipped> <SalesRepId>12</SalesRepId> <Total>307.25</Total> <PaymentTypeId>1</PaymentTypeId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>187</Id> <CustomerId>211</CustomerId> <DateOrdered>2012-12-23</DateOrdered> <DateShipped>2012-12-30</DateShipped> <SalesRepId>13</SalesRepId> <Total>3872.9</Total> <PaymentTypeId>1</PaymentTypeId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>188</Id> <CustomerId>211</CustomerId> <DateOrdered>2013-01-14</DateOrdered> <DateShipped>2013-01-15</DateShipped> <SalesRepId>12</SalesRepId> <Total>12492</Total> <PaymentTypeId>1</PaymentTypeId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> ... </OrdersView> </CustomersViewRow> ...
XMLの読取りと書込みに関する必知事項
この項では、XMLの使用に役立つ追加情報を提供します。
XMLの要素名の制御
ビュー・オブジェクトの正規XML書式で使用されるデフォルトのXML要素名は、「プロパティ」ウィンドウを使用していくつかプロパティを設定することで変更できます。これを実現するには、ビュー・オブジェクトの概要エディタを開き、次の操作を行います。
-
「属性」ページで属性を選択し、「プロパティ」ウィンドウで「カスタム・プロパティ」ナビゲーション・タブを選択して、カスタム属性レベルのプロパティ「XML要素」に値
SomeOtherName
を設定すると、その属性に使用されるXML要素の名前が<SomeOtherName>
に変わります。たとえば、
CustomersView
ビュー・オブジェクトのEmail
属性で定義されているこのプロパティは、「XMLを生成するときに行われる処理」で説明されているXML要素を<Email>
から<EmailAddress>
に変更します。 -
「プロパティ」ウィンドウの「一般」ナビゲーション・タブを選択し、カスタム・ビュー・オブジェクト・レベルのプロパティ「XML行要素」に値
SomeOtherRowName
を設定すると、そのビュー・オブジェクトに使用されるXML要素の名前が<SomeOtherRowName>
に変わります。たとえば、
CustomersView
ビュー・オブジェクトで定義されているこのプロパティは、「XMLを生成するときに行われる処理」で説明されている行のXML要素名を<CustomersViewRow>
から<Customer>
に変更します。 -
ビュー・リンク・アクセッサ属性からのネストされた行セット・データをラップする要素の名前を変更するには、「ビュー・リンク・プロパティ」ダイアログを使用します。ダイアログを開くには、ビュー・リンクの概要エディタで、「関連」ページの「アクセッサ」セクションにある「アクセッサの編集」アイコンをクリックします。「アクセッサ名」フィールドに、ビュー・リンク・アクセッサ属性の適切な名前を入力します。
NULL値属性の要素抑止の制御
デフォルトでは、ビュー行属性がnull
の場合、生成されるXMLから対応する要素が省略されます。概要エディタの「属性」ページで属性を選択し、「プロパティ」ウィンドウで「カスタム・プロパティ」ナビゲーション・タブを選択して、カスタム属性レベルのプロパティ「XML明示的なNull」に任意の値(たとえばtrue
やyes
)を設定すると、値がNULLの属性についても要素が生成されます。たとえば、AssignedDate
という名前の属性にこのプロパティを設定すると、AssignedDateがnull
の行には、対応する要素AssignedDate null="true"/
が生成されます。この動作をビュー・オブジェクトのすべての属性に適用する場合は、属性ごとにプロパティを定義するかわりに、ビュー・オブジェクト・レベルでXml Explicit Nullカスタム・プロパティを定義できます。
生成されたXMLの印刷またはXPathを使用した検索
writeXML()
から返されるXML Node
オブジェクトに関する最も一般的な処理は次の2つです。
-
ネットワーク経由での送信やファイルへの保存などのために、ノードをシリアライズされたテキスト表現に印刷
-
生成されたXMLをW3C XPath式を使用して検索
残念ながら、標準のW3C Document Object Model (DOM) APIには、これらの役に立つ操作を行うためのメソッドは含まれません。しかし大丈夫です。ADFビジネス・コンポーネントはDOMのOracle XMLパーサーの実装を使用しているので、writeXML()
からのNode
戻り値をOracle固有のクラスであるXMLNode
やXMLElement
(oracle.xml.parser.v2
パッケージ内)にキャストして、次のような便利な追加機能にアクセスできます。
-
print()
メソッドを使用した、シリアライズされたテキスト形式へのXML要素の印刷 -
selectNodes()
メソッドを使用した、メモリー内のXML要素のXPath式による検索
-
valueOf()
メソッドを使用した、XML要素に関連するXPath式の値の検出
次の例は、TestClientWriteXML
のprintXML()
メソッドを示しています。Node
パラメータをXMLNode
にキャストし、print()
メソッドを呼び出してXMLをコンソールにダンプします。
// In TestClientWriteXML.java private static void printXML(Node n) throws IOException { ((XMLNode)n).print(System.out); }
生成されるXMLの属性マップによるきめ細かい制御
生成されるXMLに含める属性をきめ細かく制御する必要がある場合は、HashMap
を受け取るバージョンのwriteXML()
メソッドを使用します。次の例は、この手法を使用するTestClientWriteXML
クラスの興味深い行を示しています。HashMap
を作成した後、String[]
値のエントリを設定します。このエントリは、XMLに含める属性の名前であり、キーとしてはこれらの属性が属しているビュー定義の完全修飾名を使用します。例には、CustomersView
ビュー・オブジェクトの属性Name
、City
およびOrdersView
と、OrdersView
ビュー・オブジェクトの属性Id
、DateOrdered
およびTotal
が含まれます。
ノート:
旧バージョンのADFビジネス・コンポーネントとの互換性のため、writeXML()
メソッドが受け取るHashMap
はcom.sun.java.util.collections
パッケージのものです。
特定のビュー・オブジェクト・インスタンスのビュー行に対しては、次のような処理が行われます。
-
そのビュー・オブジェクトの完全修飾ビュー定義名とキーが一致する項目が属性マップに存在する場合は、対応する
String
配列に名前のある属性のみがXMLに含められます。さらに、文字列配列にビュー・リンク・アクセッサ属性の名前が含まれる場合は、そのディテール行セットのネストされた内容がXMLに含められます。ビュー・リンク・アクセッサ属性名が文字列配列に出現しない場合は、ディテール行セットの内容は含められません。
-
キーが一致する項目がマップ内に存在しない場合は、その行のすべての属性がXMLに含められます。
HashMap viewDefMap = new HashMap(); viewDefMap.put("oracle.summit.model.readandwritexml.queries.CustomersView", new String[]{"Name","City", "OrdersView" /* View link accessor attribute */ }); viewDefMap.put("oracle.summit.model.readandwritexml.queries.OrdersView", new String[]{"Id","DateOrdered","Total"}); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap));
前述の例を実行すると、提供された属性マップで指定されている属性とビュー・リンク・アクセッサのみが含まれる次のXMLが生成されます。
<CustomersViewRow> <Id>211</Id> <Name>Kuhn's Sports</Name> <City>Prague</City> <OrdersView> <OrdersViewRow> <Id>107</Id> <DateOrdered>2012-12-13</DateOrdered> <Total>142171</Total> </OrdersViewRow> <OrdersViewRow> <Id>183</Id> <DateOrdered>2013-02-24</DateOrdered> <Total>3742</Total> </OrdersViewRow> <OrdersViewRow> <Id>184</Id> <DateOrdered>2012-11-19</DateOrdered> <Total>987</Total> </OrdersViewRow> <OrdersViewRow> <Id>185</Id> <DateOrdered>2012-08-08</DateOrdered> <Total>2525.25</Total> </OrdersViewRow> <OrdersViewRow> <Id>186</Id> <DateOrdered>2012-04-26</DateOrdered> <Total>307.25</Total> </OrdersViewRow> <OrdersViewRow> <Id>187</Id> <DateOrdered>2012-12-23</DateOrdered> <Total>3872.9</Total> </OrdersViewRow> <OrdersViewRow> <Id>188</Id> <DateOrdered>2013-01-14</DateOrdered> <Total>12492</Total> </OrdersViewRow> ... </OrdersView> </CustomersViewRow> ...
双方向ビュー・リンクでの属性マップ手法の使用
ビュー・オブジェクトが、双方向に構成されたビュー・リンクを通して関連付けられている場合は、属性マップを使用するwriteXML()
方式を使用する必要があります。双方向ビュー・リンクの状況でwriteXML()
方式を使用し、最大深度に-1
を指定して存在するビュー・リンクの全レベルを含めるようにすると、writeXML()
メソッドは、双方向のビュー・リンクを前後に移動して無限ループに陥り、メモリーを使い果すまで重複したデータを含む深くネストしたXMLを生成します。このような場合は、かわりに属性マップを指定してwriteXML()
を使用してください。この方法を使用すること以外に、XMLに含める、または含めないビュー・リンク・アクセッサを制御し、XML生成中の無限再帰を回避する方法はありません。
生成されたXMLのXSLTスタイルシートによる変換
writeXML()
によって生成される正規のXML書式が要件を満たさない場合は、オプションの引数として、XSLTスタイルシートを提供できます。通常と同じようにXMLが生成されますが、最終的なXMLが呼出し元に返される前に、指定したスタイルシートを使用して結果の変換が行われます。
次の例に示すXSLTスタイルシートについて考えます。これは、「生成されるXMLの属性マップによるきめ細かい制御」で生成されるXMLのルート要素と一致して、新しいCustomerNames
要素を結果に作成する、テンプレートが1つのみの簡単な変換です。このテンプレートは、xsl:for-each
命令を使用してすべてのCustomersView
要素を処理します。対象であることが確認されたCustomersView
要素ごとに、結果にCustomerNames
要素を作成し、そのContact
属性に、CustomersView
のName
子要素の値を移入します。
<?xml version="1.0" encoding="windows-1252" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <CustomerNames> <xsl:for-each select="/CustomersView/CustomersViewRow)"> <xsl:sort select="Name"/> <Customer Contact="{Name}"/> </xsl:for-each> </CustomerNames> </xsl:template> </xsl:stylesheet>
次の例は、writeXML()
をコールするとこのXSLTスタイルシートを実行するTestClientWriteXML
クラスの興味深い行を示しています。
// In TestClientWriteXML.java XSLStylesheet xsl = getXSLStylesheet(); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap,xsl));
前述の例のコードを実行すると、次のような変換されたXMLが生成されます。
<CustomerNames> <Customer Contact="ABC Company"/> <Customer Contact="Acme Outfitters"/> <Customer Contact="Acme Sporting Goods"/> <Customer Contact="All Baseball"/> ... <Customer Contact="The Sports Emporium"/> <Customer Contact="Unisports"/> <Customer Contact="Value Valley"/> <Customer Contact="Wally's Mart"/> <Customer Contact="Wally's Weights"/> <Customer Contact="Westwind Sports"/> <Customer Contact="Womansport"/> <Customer Contact="Your Choice Sporting Goods"/> <Customer Contact="Z-Mart"/> <Customer Contact="Zibbers"/> <Customer Contact="Zip City"/> </CustomerNames>
次の例で示されているgetXSLStylesheet()
ヘルパー・メソッドも、実行時にクラスパスからXSLTスタイルシートなどのリソースを読み込む方法を学習するためのよい例です。このコードでは、Example.xsl
スタイルシートがTestClientWriteXML
クラスと同じディレクトリにあるものと想定しています。.class
演算子を使用してTestClientWriteXML
クラスのClass
オブジェクトを参照することで、コードはgetResource()
メソッドを使用してリソースに対するURL
を取得します。その後、URLをXSLProcessor
クラスのnewXSLStylesheet()
メソッドに渡して、返すための新しいXSLStylesheet
オブジェクトを作成します。このオブジェクトは、*.xsl
ファイルから読み込まれた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メタデータとともに出力ディレクトリに格納するリソースを扱うときは、「プロジェクト・プロパティ」ダイアログの「コンパイラ」ページで、「ファイル・タイプの出力ディレクトリへのコピー」フィールドを更新し、セミコロンで区切られたリストに.xsl
を含めることができます。
XMLドキュメントを使用して変更を適用する方法
ビュー・オブジェクトがXMLドキュメントを使用して挿入、更新、および削除を行うようにするには、readXML()
メソッドを使用します。
void readXML(Element elem, int depthcount)
readXML()
が受け入れる正規の書式は、同じビュー・オブジェクトでwriteXML()
メソッドを呼び出すと生成されるものと同じです。処理するXMLドキュメントが正規の書式に対応していない場合は、readXML()
に対するオプションの第3引数としてXSLTスタイルシートを提供し、ファイルを読み込んで処理する前に、入力XMLドキュメントを正規の書式に変換できます。
XMLドキュメントを使用するときに行われる処理
ビュー・オブジェクトは、正規の書式のXMLドキュメントを使用するとき、ドキュメントを処理して、行要素、その属性要素の子、およびビュー・リンク・アクセッサ属性を表すネストされた要素を認識します。depthcount
パラメータで指定されている最大レベルまで、再帰的にドキュメントを処理します。XMLドキュメントの全レベルを処理するよう要求するには、depthcount
に-1
を渡します。
ViewObject.readXML()によるXMLドキュメントの処理方法
認識する行要素ごとに、readXML()
メソッドは次の処理を行います。
-
行を処理するために関連するビュー・オブジェクトを識別します。
-
子属性要素を読み取り、行の主キー属性の値を取得します。
-
主キー属性を使用して
findByKey()
を実行し、行がすでに存在するかどうかを判定します。 -
行が存在する場合は、次の処理を行います。
-
行要素にマーカー属性
bc4j-action="remove"
が含まれる場合は、既存の行を削除します。 -
それ以外の場合は、XMLの現在の行要素の、属性要素の子の値を使用して、行の属性を更新します。
-
-
行が存在しない場合は、新しい行を作成し、ビュー・オブジェクトの行セットに挿入します。その属性は、XMLの現在の行要素の、属性要素の子の値を使用して移入します。
単一行でXMLを処理するためのreadXML()の使用
Row
オブジェクトに対しても、同じreadXML()
メソッドを使用できます。このメソッドが受け入れる正規のXMLの書式は、同じ行に対してwriteXML()
を呼び出すと生成される書式と同じです。行でreadXML()
メソッドを呼び出し、次の処理を行うことができます。
-
属性の値をXMLで更新します。
-
対応する行要素に
bc4j-action="remove"
マーカー属性がある場合は、行を削除します。 -
ビュー・リンク・アクセッサを介して、ネストされた行を挿入、更新、または削除します。
次の例に示すXMLドキュメントについて考えます。これは、CustomersView
ビュー・オブジェクトの単一行で期待される正規の書式です。ルートのCustomersViewRow
要素の内部にネストされているCity
属性は、顧客の住む市を表します。ネストされたOrdersView
要素はOrdersView
ビュー・リンク・アクセッサ属性に対応し、3つのOrdersViewRow
要素を含みます。それぞれが、OrdersView
行の主キーを表すId
要素を含みます。
<CustomersViewRow> <Id>16</Id> <!-- This will update Customer's ConfirmedEmail attribute --> <City>Houston</City> <OrdersView> <!-- This will be an update since it does exist --> <OrdersViewRow> <Id>1018</Id> <DateShipped>2013-03-13</DateShipped> </OrdersViewRow> <!-- This will be an insert since it doesn't exist --> <OrdersViewRow> <Id>9999</Id> <CustomerId>16</CustomerId> <Total>9999.00</Total> </OrdersViewRow> <!-- This will be deleted --> <OrdersViewRow bc4j-action="remove"> <Id>1008</Id> </OrdersViewRow> </OrdersView> </CustomersViewRow>
次の例は、TestClientReadXML
クラスの興味深いコード行を示しています。このクラスは、CustomersView
ビュー・オブジェクトの特定の行に、このXMLデータグラムを適用します。TestClientReadXML
クラスが実行する基本的なステップは次のとおりです。
-
キーにより、ターゲット行を検索します(たとえば、顧客であるThe Sports Emporium)。
-
変更を適用する前に、行に対して生成されたXMLを表示します。
-
ヘルパー・メソッドを使用して適用する変更を含む解析されたXMLドキュメントを取得します。
-
行に変更を適用するためにXMLドキュメントを読み込みます。
-
適用された保留中変更を含むXMLを表示します。
TestClientReadXML
クラスは、「問合せ済データのXMLの生成方法」で説明されているXMLInterface.XML_OPT_ASSOC_CONSISTENT
フラグを使用して、ポストされていない新しい行をXMLに含めています。
ViewObject vo = am.findViewObject("CustomersView"); Key k = new Key(new Object[] { 16 }); // 1. Find a target row by key (e.g. for customer "The Sports Emporium") Row sports = vo.findByKey(k, 1)[0]; // 2. Show the XML produced for the row before changes are applied printXML(sports.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 sports.readXML(getInsertUpdateDeleteXMLGram(), -1); // 5. Show the XML with the pending changes applied printXML(sports.writeXML(-1, XMLInterface.XML_OPT_ALL_ROWS | XMLInterface.XML_OPT_ASSOC_CONSISTENT));
前述の例のコードを実行すると、最初に、The Sports Emporiumの変更前の情報が表示されます。次のことに注意してください。
-
City
属性の値はLapeerです。 -
オーダーID
1018
にはShippedDate
属性がありません。 -
オーダーID
1008
にはオーダー行があり、 -
オーダーID
9999
に関連するオーダー行はありません。
<CustomersViewRow> <Id>16</Id> <Name>The Sports Emporium</Name> <Address>222 E Nepessing St</Address> <City>Lapeer</City> <State>MI</State> <CountryId>4</CountryId> <ZipCode>48446</ZipCode> <CreditRatingId>1</CreditRatingId> <SalesRepId>13</SalesRepId> <OrdersView> <OrdersViewRow> <Id>1018</Id> <CustomerId>16</CustomerId> <DateOrdered>2013-02-25</DateOrdered> <SalesRepId>11</SalesRepId> <Total>195.99</Total> <PaymentTypeId>2</PaymentTypeId> <PaymentOptionId>1009</PaymentOptionId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>1008</Id> <CustomerId>16</CustomerId> <DateOrdered>2013-02-26</DateOrdered> <SalesRepId>14</SalesRepId> <Total>100.97</Total> <PaymentTypeId>2</PaymentTypeId> <PaymentOptionId>1009</PaymentOptionId> <OrderFilled>N</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>1023</Id> <CustomerId>16</CustomerId> <DateOrdered>2013-01-11</DateOrdered> <DateShipped>2013-01-15</DateShipped> <SalesRepId>11</SalesRepId> <Total>2451.97</Total> <PaymentTypeId>2</PaymentTypeId> <PaymentOptionId>1009</PaymentOptionId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> </OrdersView> </CustomersViewRow>
readXML()
を使用してXMLドキュメントの変更を行に適用した後、writeXML()
を使用して再びXMLを表示すると、次のようになります。
-
City
属性には、今度はHoustonが表示されます。 -
オーダー
9999
の新規オーダー行が作成されました。 -
オーダー行
1018
の出荷日は2013-03-13に設定され、 -
オーダー
1008
のオーダー行は削除されました。
<CustomersViewRow> <Id>16</Id> <Name>The Sports Emporium</Name> <Address>222 E Nepessing St</Address> <City>Houston</City> <State>MI</State> <CountryId>4</CountryId> <ZipCode>48446</ZipCode> <CreditRatingId>1</CreditRatingId> <SalesRepId>13</SalesRepId> <OrdersView> <OrdersViewRow> <Id>9999</Id> <CustomerId>16</CustomerId> <Total>9999.00</Total> </OrdersViewRow> <OrdersViewRow> <Id>1018</Id> <CustomerId>16</CustomerId> <DateOrdered>2013-02-25</DateOrdered> <DateShipped>2013-03-13</DateShipped> <SalesRepId>11</SalesRepId> <Total>195.99</Total> <PaymentTypeId>2</PaymentTypeId> <PaymentOptionId>1009</PaymentOptionId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> <OrdersViewRow> <Id>1023</Id> <CustomerId>16</CustomerId> <DateOrdered>2013-01-11</DateOrdered> <DateShipped>2013-01-15</DateShipped> <SalesRepId>11</SalesRepId> <Total>2451.97</Total> <PaymentTypeId>2</PaymentTypeId> <PaymentOptionId>1009</PaymentOptionId> <OrderFilled>Y</OrderFilled> </OrdersViewRow> </OrdersView> </CustomersViewRow>
ノート:
この例では、readXML()
を使用して単一の行に変更を適用する場合が示されています。XMLドキュメントに、ラップするCustomersView
行が含まれ、そのネストされた1つ以上のCustomersViewRow
要素のそれぞれに主キー属性が含まれている場合は、CustomersView
ビュー・オブジェクトのreadXML()
メソッドを使用してドキュメントを処理し、複数のCustomersView
行に対する操作を処理できます。
カスタム・データ・ソースおよびFramework Base Class ProgrammaticViewObjectImplを使用したプログラム的操作
ADFビジネス・コンポーネントはユーザーが作成できるプログラム・ビュー・オブジェクトをサポートして、カスタム・データ・ソースと対話します。
フレームワーク・ベース・クラスoracle.jbo.server.ProgrammaticViewObjectImpl
およびoracle.jbo.serverProgrammaticViewRowImpl
を使用すれば、カスタム・データ・ソースとの対話の制御や、データ収集を戻すことが可能になります。ADFはデータ・オブジェクトを内部的にViewRow
に変換します。
ノート:
ProgrammaticViewObjectImpl
ベース・クラスの前に、アプリケーションはベース・クラスoracle.jbo.server.ViewObjectImpl
から拡張し、カスタム動作の多くのライフサイクルをオーバーライドしています。プログラム的にビュー・オブジェクトを使用するということは、様々なステージのビュー・オブジェクト・ライフサイクルを理解する必要なく、カスタム・データ・ソースとの統合がアプリケーション開発者により実行されることを意味します。
oracle.jbo.server.ViewObjectImpl
およびoracle.jbo.server.ViewRowImpl
ではなく、フレームワーク・ベース・クラスoracle.jbo.server.ProgrammaticViewObjectImpl
およびoracle.jbo.server.ProgrammaticViewRowImpl
からデフォルトで拡張します。
ノート:
ビュー・オブジェクトをプログラム的に作成する際にこの動作を確実に実行するには、ADFビジネス・コンポーネントの設定ダイアログで、ベース・クラスoracle.jbo.server.ViewObjectImpl
からの拡張を有効にする機能を選択解除する必要があります。ツール-設定ダイアログのADFビジネス・コンポーネント-ビュー・オブジェクトページのオプションはクラシック・プログラム・ビューです(選択解除してProgrammaticViewObjectImpl
からの拡張を可能にする)。
ProgrammaticViewObjectImplを拡張してビュー・オブジェクト・クラスを作成する方法
oracle.jbo.server.ProgrammaticViewObjectImpl
またはoracle.jbo.server.ProgrammaticViewRowImp
を拡張するビュー・オブジェクトをプログラム的に作成するには、ビュー・オブジェクト作成ウィザードを使用して、プログラムデータ・ソース・オプションを選択します。
始める前に:
クラシック・スタイルのプログラム的なビュー・オブジェクト生成を無効にする必要があります。ツール-設定ダイアログのADFビジネス・コンポーネント-ビュー・オブジェクトページで、オプションクラシック・プログラム・ビューの選択を解除します。このオプションを選択したまま(デフォルト)にしておくと、JDeveloperはoracle.jbo.server.ViewObjectImpl
およびoracle.jbo.server.ViewRowImpl
を拡張するビュー・オブジェクトJavaクラスを生成します。クラシック・モードの詳細は、「代替データ・ソースに対するクラシック・スタイル・プログラムでのビュー・オブジェクトの使用」を参照してください。
ProgrammaticViewObjectImplを拡張してプログラムによるビュー・オブジェクトを作成するには:
-
「アプリケーション」ウィンドウで、ビュー・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
-
「新規ギャラリ」で「ビジネス層」を展開し、「ADFビジネス・コンポーネント」、「ビュー・オブジェクト」を選択して、「OK」をクリックします。
-
ビュー・オブジェクト作成ウィザードの「名前」ページで、ビュー・オブジェクトの名前とパッケージを入力します。データ・ソースについて、「プログラム関連」を選択します。
-
「属性」ページで、「新規」を複数回クリックし、プログラムによるビュー・オブジェクトが必要とするビュー・オブジェクト属性を定義します。
-
「属性の設定」ページで、定義した属性に必要な設定を調節します。
-
「Java」ページで「ビュー・オブジェクト・クラスの生成」を選択して、カスタム・ビュー・オブジェクト・クラス(
ProgrammaticViewObjectImpl
)にコードを含められるようにします。 -
「終了」をクリックして、ビュー・オブジェクトを作成します。
ビュー・オブジェクトのカスタムJavaクラスで、「ProgrammaticViewObjectImplベースのビュー・オブジェクトをオーバーライドする主要なフレームワーク・メソッド」で説明されているメソッドをオーバーライドし、独自のデータ取得方法を実装します。
ProgrammaticViewObjectImplベースのビュー・オブジェクトをオーバーライドする主要なフレームワーク・メソッド
ビュー・オブジェクトのアクセス・モードプロパティの設定方法に応じて、次の2つのメソッドのいずれかがオーバーライドされます。
アクセス・モード・プロパティは、ビュー・オブジェクト・クエリによって戻される行数を決定します。このプロパティのデフォルト設定であるSCROLLABLE
では、読み込むビュー・オブジェクトの行数がキャッシュされます。RANGEPAGING
設定は、範囲サイズで指定された行数をフェッチします。アクセス・モードの詳細は、「大きい結果セットを効率的にスクロールするための範囲ページ移動の使用方法」を参照してください。
SCROLLABLE
アクセス・モードがSCROLLABLE
の場合は、getScrollableData()
APIをオーバーライドして具体的な実装を提供する必要があります。
-
protected Collection<Object> getScrollableData(ScrollableDataFilter dataFilter)
スクロール可能なプログラムによるビュー・オブジェクトの場合、フレームワークは次のAPIコールの結果として完全な
dataset
を提供する拡張機能を期待します。フレームワークは、クライアントがコレクション内の次の行または前の行を問い合せた際にAPIコールを内部的に管理します。
RANGEPAGING
アクセス・モードがRANGEPAGING
の場合、アプリケーションは次の2つのAPIメソッドを一緒にオーバーライドする必要があります。
-
/Apps should return the collection within a current Range
.protected Collection<Object> getRangePagingData(RangePagingDataFilter dataFilter)
-
protected Collection<Object> retrieveDataByKey(Key key , int size).
ページ範囲移動ビュー・オブジェクトの場合、フレームワークは現在の範囲の行コレクションのみを提供する拡張機能を期待します。フレームワークに異なる範囲の行が必要な場合、次のフォローアップAPIコールが行われます。現在の範囲の開始インデックスおよびサイズ情報は、引数として渡されるRangePagingDataFilter
オブジェクトから取得できます。
プログラム関連ビュー・オブジェクト・トリガーに関する必知事項
Groovyスクリプト言語で定義したトリガーを、ProgrammaticViewObjectImpl.java
のオーバーライド後のメソッドのかわりに使用して、カスタム・データソースでデータ取得操作を実行できます。トリガーは、様々なプログラム関連ビュー・オブジェクト・ライフサイクル・ポイントからコールできます。
アプリケーションでは、Groovyスクリプトで定義したトリガーをJavaフック・ポイントのかわりにコールできます。これらのトリガーは、カスタム・データ・ソースと対話し、データ取得操作を実行します。これらは、様々なプログラム関連ビュー・オブジェクト・ライフサイクル・ポイントからコールできます。フレームワーク・クラス(ProgrammaticViewObjectImpl
など)をサブクラス化し、カスタム・データソースに固有の実装を定義する特定のメソッドをオーバーライドするオプションは残りますが、トリガーを実行するためには、Javaフック・ポイントを定義しないでください。別の方法として、Javaフック・ポイントを定義した場合、そのフック・ポイントを実行するには、トリガーが存在していてはいけません。Javaフック・ポイントとトリガーの両方が定義されている場合、トリガーは実行されないことに注意してください。アプリケーションでは、オーバーライド後のメソッドでGroovyスクリプトを使用して、カスタム・データ・ソースと対話できます。これらのトリガーは、様々なプログラム関連ビュー・オブジェクト・ライフサイクル・ポイントからコールできます。
具体的なプログラム関連ビュー・オブジェクト・トリガーを次に示します。
FetchData
このトリガーは、カスタム・データソースからデータ・コレクションを取得する場合に使用します。このトリガーは、ビュー・オブジェクトのアクセス・モードがスクロールか範囲ページングかに応じて、ProgrammaticViewObjectImpl.java
のgetScrollableData()
メソッドまたはgetRangePagingData()
メソッドのかわりに起動されます。returnList
データ・コレクションには、カスタム・データ・ソースから取得されたレコード(オブジェクト/マップ)が移入されます。
次のサンプル・コードは、このトリガーの使用方法を示しています。このサンプル・コードでは、次のコンテキスト固有キーワードが使用されています。
-
dataFilter
- DataFilterオブジェクトを戻します。ビュー・オブジェクトのモードに応じて、ScrollableDataFilterかRangePagingDataFilterのどちらかになります。 -
dataFilter.fetchSize
- フェッチするデータの範囲サイズを戻します。 -
dataFilter.fetchStart
- フェッチするデータの範囲開始値を戻します。 -
dataFilter.searchCriteria
- ビュー・オブジェクトに適用されるViewCriteriaのリストを戻します。 -
dataFilter.sortCriteria
- ビュー・オブジェクトに適用されるSortCriteriaのリストを戻します。 -
dataFilter.bindParameterValues
- 現在実行中の行セットに対応するバインド・パラメータの名前と値を含むマップを戻します。 -
dataFilter.masterData
- ディテール・ビュー・オブジェクトのコンテキストで使用され、マスター行のdataproviderオブジェクトを戻します。 -
returnList
- カスタム・データ・ソースからのレコード(オブジェクト/マップ)を移入するコレクション。
@TriggerExpression(triggerType="FetchData", name="FetchData_Rule_0") def FetchData_Rule_0_ValidationRuleScript_Trigger() { def empData = model.data.DataCenter.getInstance().getEmpData() def empFilteredData = new ArrayList() model.data.DeptObject master = (model.data.DeptObject)dataFilter.masterData for (emp in empData) { if (master == null || emp.deptno.equals(master.deptno)) { empFilteredData.add(emp) } } def fetchStart = dataFilter.fetchStart def fetchSize = dataFilter.fetchSize if(fetchStart == -1) { fetchStart = 0; } for(int i = fetchStart; i < fetchSize + fetchStart && i < empFilteredData.size() ; i++) { returnList.add(empFilteredData.get(i)) @TriggerExpression(triggerType="RetrieveDataByKey", name="RetrieveDataByKey_Rule_0") def RetrieveDataByKey_Rule_0_ValidationRuleScript_Trigger() { def empData = model.data.DataCenter.getInstance().getEmpData() def keyValue = dataFilter.key.keyValues[0] def iter = empData.iterator() while (iter.hasNext()) { def emp = iter.next(); if (emp.empno.equals(keyValue)) { returnList.add(emp); } } } }
RetrieveDataByKey Trigger
このトリガーは、カスタム・データソースからキー値と一致するデータ・コレクションを取得する場合に使用します。これは、ProgrammaticViewObjectImpl.java
のretrieveDataByKey
メソッドのかわりに起動されます。このトリガーは、ビュー・オブジェクトにより取得された現在の行範囲にキーと一致する必要なデータが存在しないときに起動されます。このトリガーは、キー値と一致するオブジェクト/マップのコレクションを取得します。
次のコード・サンプルは、このトリガーの使用方法を示しています。このサンプル・コードでは、次のコンテキスト固有キーワードが使用されています。
-
dmlFilter.key
- 取得するデータ/オブジェクトのキー値を含むoracle.jbo.Keyを戻します。 -
dmlFilter.fetchSize
- フェッチ・サイズを戻します。 -
returnList
- カスタム・データ・ソースからキー値と一致するレコード(オブジェクト/マップ)を移入するコレクション。
@TriggerExpression(triggerType="RetrieveDataByKey", name="RetrieveDataByKey_Rule_0") def RetrieveDataByKey_Rule_0_ValidationRuleScript_Trigger() { def empData = model.data.DataCenter.getInstance().getEmpData() def keyValue = dataFilter.key.keyValues[0] def iter = empData.iterator() while (iter.hasNext()) { def emp = iter.next(); if (emp.empno.equals(keyValue)) { returnList.add(emp); } } }
ProgrammaticViewRowImplベースのビュー・オブジェクトをオーバーライドする主要なフレームワーク・メソッド
フレームワーク・ベース・クラスoracle.jbo.server.ProgrammaticViewRowImpl
はプログラム的なViewRow
の実装クラスです。
Oracle ADFは様々なライフサイクル・フェーズ間にプログラム的なビュー行と対話するための複数のフックポイントを提供します。次のすべてのAPIメソッドでオーバーライドが必須だというわけではありません。
-
createRowData(HashMap attrNameValueMap)
ユース・ケースでプログラム的なビュー・オブジェクトの作成が必要な場合は、次のAPIをオーバーライドします。このフックポイントでは、アプリケーションがこの新たに作成された行の
DataProvider
を作成し、それをフレームワークに返してさらに管理することができます。 -
updateDataProvider(Object rowDataProvider , ViewAttributeDefImpl attrDef , Object newValue)
基礎となるエンティティ・オブジェクトなしでプログラム的なビュー・オブジェクトが作成された場合は、このAPIをオーバーライドして、行の変更された属性値を保持します。アプリケーションは次のAPIをオーバーライドして、
newvalue
をDataProvider
に更新する必要があります。 -
convertToSourceType(ViewAttributeDefImpl vad, String sourceType , Object val)
アプリケーションは必要に応じて次のAPIをオーバーライドし、値をJavaTypeからsourceTypeに変換するカスタム実装を提供する必要があります。フレームワークは
DataProvider
のvalue
を更新する前に、このフックポイントを呼び出します。 -
convertToAttributeType(AttributeDefImpl attrDef, Class javaTypeClass , Object val)
アプリケーションは必要に応じて次のAPIをオーバーライドし、値を
sourceType
からJavaType
に変換するカスタム実装を提供する必要があります。フレームワークは値をDataProvider
からフェッチするときに、このフックポイントを呼び出します。
代替データ・ソースに対するクラシック・スタイル・プログラムでのビュー・オブジェクトの使用
ADFビュー・オブジェクトのカスタムJavaクラスでメソッドをオーバーライドして、インメモリー配列、Javaの*.properties
ファイル、REF CURSOR
などのデータソースからデータをプログラムで取得できます。
REF CURSOR
、メモリー内の配列、Javaの*.properties
ファイルなどの様々な代替データソースからプログラムでデータを取得するビュー・オブジェクトを作成できます。
ノート:
代替データ・ソースについて説明している項は、以前の目的のために残されています。JDeveloperリリース12.2.1.1.0から、ADF Business Componentsではプログラム的なビュー・オブジェクトに対する拡張サポートが提供されるようになりました。プログラム的なビュー・オブジェクトの新たなフレームワーク・ベース・クラスは、カスタム・データ・ソースに対する共通メソッドを実装し、ビュー・オブジェクト・ライフサイクルの様々な段階でビュー行と対話するためのフックポイントを提供します。この機能はツール-設定ダイアログの「ビュー・オブジェクト」ページで有効にする必要があります。詳細は、「ProgrammaticViewObjectImplを拡張してビュー・オブジェクト・クラスを作成する方法」を参照してください。
プログラムによるビュー・オブジェクトをデータベース以外で操作できるようにする一般的なプロセスでは、データ・ソースにビュー・オブジェクトのデフォルトのライフサイクル・メソッドのオーバーライドが含まれます。ライフサイクル・メソッドの詳細は、「プログラムのビュー・オブジェクト用にオーバーライドする主要なフレームワーク・メソッド」を参照してください。
代替データ・ソースに対してプログラムによるビュー・オブジェクトを有効にするには、次の一般的なプロセスに従います。
-
ビュー・オブジェクト・ウィザードでビュー・オブジェクトを作成し、データ・ソースに対して「プログラム関連」を選択します。
-
ビュー・オブジェクトの概要エディタで、カスタム・ビュー・オブジェクト実装クラス(
ViewObjImpl.java
)を生成します。 -
カスタム・ビュー・オブジェクト実装クラスで、
create()
をオーバーライドして、プログラムによるビュー・オブジェクトに必要な状態を初期化します。少なくとも、このビュー・オブジェクトのSQL問合せのトレースをすべて削除します。 -
executeQueryForCollection()
をオーバーライドし、setUserDataForCollection()
をコールして、カスタム問合せの結果をUserData
オブジェクトに関連付けます。 -
hasNextForCollection()
をオーバーライドし、UserData
オブジェクトを反復して、取得する他のレコードをテストします。hasNextForCollection()
がtrue
を返す間、次の操作が行われます。-
createRowFromResultSet()
をオーバーライドして、ビュー・オブジェクト問合せコレクションに行を作成します。 -
オーバーライドされた
createRowFromResultSet()
メソッドで、getUserDataForCollection()
メソッドをコールして格納されているUserData
オブジェクトのインスタンスを取得し、createNewRowForCollection()
をコールして新しい空白行を作成します。 -
必要に応じて、各レコードをADFビジネス・コンポーネント・ビュー・オブジェクト属性でサポートされている属性データ型に変換し、
populateAttributeForRow()
をコールして問合せコレクションに属性値を格納します。 -
hasNextForCollection()
がfalse
を返す場合は、setFetechCompleteForCollection()
をコールすることで問合せコレクションが移入されたことをビュー・オブジェクトに通知します。
-
-
releaseUserDataForCollection()
をオーバーライドして結果セットをリリースします。
ビュー・オブジェクト・コンポーネントは、実行時に複数のアクティブな行セットと関連付けられる可能性があるため、これらのライフサイクル・メソッドの多くは、Object
パラメータ(問合せコレクション)を受け取ります。このパラメータで、ADFビジネス・コンポーネント・フレームワークは、カスタム・コードが設定する行のコレクションと、特定のコレクションにどの行が移入されるかに影響する可能性のあるバインド変数値の配列を渡します。
UserData
オブジェクトを行の各コレクションとともに格納して、カスタム・データ・ソース実装が必要なデータ・ソース・コンテキスト情報を関連付けられるようにすることができます。フレームワークでは、このコレクションごとのコンテキスト情報を取得および設定するために、setUserDataForCollection()
メソッドとgetUserDataForCollection()
メソッドが提供されています。オーバーライドされたフレームワーク・メソッドのいずれかが呼び出されるたびに、getUserDataForCollection()
メソッドを使用して、フレームワークが移入する行のコレクションと関連付けられた正しいResultSet
オブジェクトを取得できます。
プログラムで読取り専用のビュー・オブジェクトを作成する方法
プログラムで読取り専用のビュー・オブジェクトを作成するには、ビュー・オブジェクト作成ウィザードを使用します。
プログラムで読取り専用のビュー・オブジェクトを作成するには:
- 「アプリケーション」ウィンドウで、ビュー・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
- 「新規ギャラリ」で「ビジネス層」を展開し、「ADFビジネス・コンポーネント」、「ビュー・オブジェクト」を選択して、「OK」をクリックします。
- ビュー・オブジェクト作成ウィザードの「名前」ページで、ビュー・オブジェクトの名前とパッケージを入力します。データ・ソースについて、「プログラム関連」を選択します。
- 「属性」ページで、「新規」を複数回クリックし、プログラムによるビュー・オブジェクトが必要とするビュー・オブジェクト属性を定義します。
- 「属性の設定」ページで、定義した属性に必要な設定を調節します。
- 「Java」ページで「ビュー・オブジェクト・クラスの生成」を選択して、カスタム・ビュー・オブジェクト・クラス(
ViewObjImpl
)にコードを含められるようにします。 - 「終了」をクリックして、ビュー・オブジェクトを作成します。
ビュー・オブジェクトのカスタムJavaクラスで、「プログラムのビュー・オブジェクト用にオーバーライドする主要なフレームワーク・メソッド」で説明されているメソッドをオーバーライドし、独自のデータ取得方法を実装します。
プログラムでエンティティ・ベースのビュー・オブジェクトを作成する方法
プログラムでデータを取得するエンティティ・ベースのビュー・オブジェクトを作成するには、通常の方法でビュー・オブジェクトを作成し、カスタムJavaクラスを有効にし、次の項で説明されているメソッドをオーバーライドして独自のデータ取得方法を実装します。
プログラムのビュー・オブジェクト用にオーバーライドする主要なフレームワーク・メソッド
プログラムによるビュー・オブジェクトでは、通常、ViewObjectImpl
ベース・クラスの次のメソッドをすべてオーバーライドして、データ取得の独自方法を実装します。
-
create()
このメソッドは、ビュー・オブジェクトのインスタンスが作成されるときに呼び出され、プログラムによるビュー・オブジェクトが必要とする任意の状態を初期化するために使用できます。このオーバーライドされるメソッドには、少なくとも次の行を組み込んで、プログラムによるビュー・オブジェクトが関係するSQL問合せをトレースしないようにします。
// Wipe out all traces of a query for this VO getViewDef().setQuery(null); getViewDef().setSelectClause(null); setQuery(null);
-
executeQueryForCollection()
このメソッドは、ビュー・オブジェクトの問合せを実行する(または再実行する)必要があるたびにコールされます。実装では、このメソッドをオーバーライドせず、問合せを完全に変更するか、パラメータ・リストを変更する必要があります。実装のベスト・プラクティスは、「executeQueryForCollection()メソッドのオーバーライド」を参照してください。
-
hasNextForCollection()
このメソッドは、このビュー・オブジェクトから作成される行セットの行セット・イテレータで
hasNext()
メソッドをサポートするために呼び出されます。実装では、プログラムのデータ・ソースから取得する行がまだある場合はtrue
を返します。 -
createRowFromResultSet()
このメソッドは、取得されたデータの各行を移入するために呼び出されます。実装では、
createNewRowForCollection()
を呼び出して新しい空白の行を作成した後、populateAttributeForRow()
を呼び出して行に対するデータの各属性を移入します。populateAttributeForRow()
のコールでは、各属性に、ADFビジネス・コンポーネント・ビュー・オブジェクトで許可されている正しい型の値を移入する必要があります。メソッドは、データ型変換を処理しないため、各値のデータ型が許可されるビュー・オブジェクト属性タイプと一致することはプログラムで確認する必要があります。 -
getQueryHitCount()
このメソッドは、
getEstimatedRowCount()
メソッドをサポートするために呼び出されます。実装では、プログラムによるビュー・オブジェクトの問合せで取得される行数のカウントまたは予想されるカウントを返します。 -
getCappedQueryHitCount()
この行カウントの問合せは、グローバルな行フェッチ制限によって指定されている上限値を超える行が問合せによって返されるかどうかを判断するために最適化されています。制限より多くの行が返される可能性がある場合、メソッドは負の数値を返します。ユーザー・インタフェースのスクロール・バーでは、正確なスクロール位置を示すための行カウント問合せを使用できない場合がありますが、この行カウント問合せは大規模な表でも適切に機能します。この実装により、
Cap
値を確認し、実際の行カウントを返すことができます。フレームワークでは、adf-config.xml
ファイルで指定された行フェッチ制限からCap
値が取得されます。oldCap
は使用されないことに注意してください。このメソッドは、値が
Cap
以下の場合に行カウントを返し、それ以外の場合は負の数値を返します。実装では、この情報を使用して、パフォーマンスの高い行カウント問合せを構築できます。パフォーマンスが重要でないときには、getQueryHitCount()
と同じ値を返すだけの場合もありますが、getCappedQueryHitCount()
では、検索される行数に関する追加情報も得られるようになりました。たとえば、DEPT
表に100万行が含まれているとします。getQueryHitCount()
をコールすると、フレームワークにより、データベースに対してSELECT COUNT(*) FROM DEPT
が実行され、100万行すべてが返されます。しかし、最大500行を表示する予定であれば、グローバルな行フェッチ制限を設定してgetCappedQueryHitCount()
をコールできます。この場合は、フレームワークによりSELECT COUNT(*) FROM DEPT WHERE ROWNUM <= 500
が実行されます。この問合せはずっと高速に完了し、パフォーマンスも向上します。このメソッドは、ビュー・オブジェクト問合せを、SELECT COUNT(*) FROM (SELECT DEPTNO, LOC FROM DEPT) WHERE ROWNUM <= :cap
(SELECT DEPTNO, LOC FROM DEPT
がビュー・オブジェクト問合せ文とする)のようなネストされた句にラップします。 -
protected void releaseUserDataForCollection()
コードでは、各行セットでユーザー・データ・コンテキスト・オブジェクトを格納および取得できます。このメソッドを呼び出すと、閉じられた行セットと関連付けられているリソースを解放できます。
以降の項の各例では、これらのメソッドをオーバーライドして、異なる種類のプログラムによるビュー・オブジェクトを実装しています。
REF CURSORでのビュー・オブジェクトの作成方法
アプリケーションでは、ストアド・プロシージャ内にカプセル化された問合せの結果を使用することが必要になる場合があります。PL/SQLを使用すると、カーソルを開いて問合せの結果を反復処理し、このカーソルへの参照をクライアントに返すことができます。このいわゆるREF CURSOR
は、クライアントが問合せの結果を反復処理するために使用できるハンドルです。これは、クライアントが元のSQL SELECT
文を実際に発行していない場合であっても可能です。
ノート:
この項の例では、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.viewobjectonrefcursor
パッケージを参照します。
REF CURSOR
を返すファンクションを含むPL/SQLパッケージの宣言は簡単です。パッケージは次の例のようになります。
CREATE OR REPLACE PACKAGE RefCursorExample IS TYPE ref_cursor IS REF CURSOR; FUNCTION get_orders_for_customer(p_custId NUMBER) RETURN ref_cursor; FUNCTION count_orders_for_customer(p_custId NUMBER) RETURN NUMBER; END RefCursorExample; . / show errors CREATE OR REPLACE PACKAGE BODY RefCursorExample IS FUNCTION get_orders_for_customer(p_custIdl NUMBER) RETURN ref_cursor IS the_cursor ref_cursor; BEGIN OPEN the_cursor FOR SELECT o.id, o.date_ordered, o.total FROM s_ord o, s_customer c WHERE o.customer_id = c.id AND c.id = p_custId; RETURN the_cursor; END get_orders_for_customer; FUNCTION count_orders_for_customer(p_custId NUMBER) RETURN NUMBER IS the_count NUMBER; BEGIN SELECT COUNT(*) INTO the_count FROM s_ord o, s_customer c WHERE o.customer_id = c.id AND c.id = p_cust_id; RETURN the_count; END count_orders_for_customer; END RefCursorExample; . / show errors
Order
エンティティ・オブジェクトに対するエンティティ・オブジェクトの慣用名を使用してエンティティ・ベースのOrdersForCustomer
ビュー・オブジェクトを定義した後、そのカスタムJavaクラスOrdersForCustomerImpl.java
に移動します。次に、以降の項で説明するように、ビュー・オブジェクトのメソッドをオーバーライドします。
create()メソッドのオーバーライド
create()
メソッドは、このビュー・オブジェクトに対するSQL問合せのすべてのトレースを削除します。次の例は、この目的でcreate()
メソッドをオーバーライドする方法を示しています。
protected void create() { getViewDef().setQuery(null); getViewDef().setSelectClause(null); setQuery(null); }
executeQueryForCollection()メソッドのオーバーライド
executeQueryForCollection()
メソッドは、このビュー・オブジェクトに基づいて、フレームワークが問合せコレクションのデータベース問合せを発行する必要があるときに実行されます。1つのビュー・オブジェクトは、多数の関連結果セットを生成でき、それぞれが異なるバインド変数値の結果である可能性があります。問合せの行セットがフレームワーク調整したマスター/ディテール・ビュー・リンクと関連性がある場合、params
配列には、ソース・ビュー・オブジェクトのバインド・パラメータのフレームワークが提供した1つ以上の名前と値のペアが含まれます。ユーザーが提供したバインド・パラメータ値がある場合、これはparams
配列においてフレームワークが提供したバインド変数値よりも優先され、ユーザー・パラメータの数はnumUserParams
引数の値によって示されます。
このメソッドは、ヘルパー・メソッドretrieveRefCursor()
を呼び出して、ストアド・ファンクションを実行してREF CURSOR
戻り値を返し、JDBC ResultSet
としてキャストします。次の例は、この目的でexecuteQueryForCollection()
メソッドをオーバーライドする方法を示しています。
protected void executeQueryForCollection(Object qc,Object[] params, int numUserParams) { storeNewResultSet(qc,retrieveRefCursor(qc,params)); super.executeQueryForCollection(qc, params, numUserParams); }
次に、ヘルパー・メソッドstoreNewResultSet()
を呼び出します。このヘルパー・メソッドは、setUserDataForCollection()
メソッドを使用して、フレームワークが問合せの実行を要求している行のコレクションとともにこのResultSet
を格納します。次の例は、この目的でstoreNewResultSet()
メソッドを定義する方法を示しています。
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()
メソッドは、「ストアド・プロシージャとストアド・ファンクションの呼出し」で説明されているヘルパー・メソッドを使用して、ストアド・ファクションを起動し、REF CURSOR
を返します。次の例は、この目的でretrieveRefCursor()
メソッドを定義する方法を示しています。
private ResultSet retrieveRefCursor(Object qc, Object[] params) { ResultSet rs = (ResultSet)callStoredFunction(OracleTypes.CURSOR, "RefCursorExample.get_orders_for_customer(?)", new Object[]{getNamedBindParamValue("bv_custId",params)}); return rs ; }
createRowFromResultSet()メソッドのオーバーライド
フレームワークは、データ・ソースからフェッチする必要のある行ごとに、オーバーライドされたcreateRowFromResultSet()
メソッドを呼び出します。この実装では、コレクション固有のResultSet
オブジェクトがユーザー・データ・コンテキストから取得されます。これは、getResultSet()
メソッド使用して結果セット・ラッパーを問合せコレクション・ユーザー・データから取得し、createNewRowForCollection()
メソッドを使用して新しい空白の行をコレクション内に作成した後、populateAttributeForRow()
メソッドを使用して、ビュー・オブジェクトの概要エディタで設計時に定義された各属性に属性値を移入します。
ノート:
populateAttributeForRow()
のコールでは、ビュー・オブジェクトに移入する属性値がADFビジネス・コンポーネント・ビュー・オブジェクト属性で期待されるデータ型にすでに準拠していることを想定します。メソッドは、データ型変換を処理しないため、移入される値のデータ型から許可されるビュー・オブジェクト属性タイプへの変換はプログラムで行う必要があります。
次の例は、この目的でcreateRowFromResultSet()
メソッドをオーバーライドする方法を示しています。
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 type correct 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; }
hasNextForCollectionMethod()のオーバーライド
フレームワーク・メソッドhasNextForCollection()
のオーバーライドされた実装は、フェッチする行がまだあるかどうかに基づいて、true
またはfalse
を返します。最後に達したら、setFetchCompleteForCollection()
を呼び出して、このコレクションは移入が終了したことをビュー・オブジェクトに伝えます。次の例は、この目的でhasNextForCollection()
メソッドをオーバーライドする方法を示しています。
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()メソッドのオーバーライド
コレクションのフェッチ処理が終了すると、オーバーライドされたreleaseUserDataForCollection()
メソッドが呼び出されて、データベース・カーソルが開いたままにならないように確実にResultSet
を閉じます。次の例は、この目的でreleaseUserDataForCollection()
メソッドをオーバーライドする方法を示しています。
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); }
getQueryHitCount()メソッドのオーバーライド
最後に、ビュー・オブジェクトのgetEstimatedRowCount()
メソッドを正しくサポートするため、オーバーライドされたgetQueryHitCount()
メソッドは、すべての行が行セットからフェッチされた場合に取得された行のカウントを返します。ここでは、コードでストアド・ファンクションを使用してこの作業を完了しています。問合せはストアド・ファンクションAPIの背後に完全にカプセル化されているため、コードもカウント・ロジックの実装およびこの機能のサポートをPL/SQLパッケージに依存しています。次の例は、この目的でgetQueryHitCount()
メソッドをオーバーライドする方法を示しています。
public long getQueryHitCount(ViewRowSetImpl viewRowSet) { Long result = (Long)callStoredFunction(NUMBER, "RefCursorExample.count_orders_for_customer(?)", viewRowSet.getParameters(true)); return result.longValue(); }
複数の更新可能エンティティによるビュー・オブジェクトの作成
セカンダリ・エンティティ慣用名を複数のエンティティ慣用名を持つADFビュー・オブジェクトに追加する場合、デフォルトではセカンダリ・エンティティ慣用名は更新可能ではありません。このデフォルトの動作をオーバーライドして更新可能にすることができます。
デフォルトでは、複数のエンティティ・オブジェクト慣用名を持つビュー・オブジェクトを作成するとき、概要エディタでビュー・オブジェクトに追加する2次エンティティ・オブジェクト慣用名それぞれが次の設定で構成されます。
-
「更新可能」チェック・ボックスを選択解除
-
「参照」チェック・ボックスを選択
ビュー・オブジェクト概要エディタの「エンティティ・オブジェクト」ページの「選択済」リストで「使用方法」を選択して、「更新可能」チェック・ボックスを選択し、デフォルトの動作を、2次エンティティ・オブジェクトの慣用名を更新可能にできます。
また、各セカンダリ・エンティティ・オブジェクト慣用名に関して、「参照」を選択したままにして、エンティティ参照情報が変更されたときにセカンダリ・エンティティの属性をリフレッシュするかどうかを決定できます。デフォルトでは、「参照」が選択されていて、各2次エンティティ・オブジェクトの属性がリフレッシュされます。複数のエンティティ・オブジェクト慣用名を使用する行の挿入を許可する場合のこの設定の詳細は、「実行時の処理: ビュー行の作成」を参照してください。
表11-4に、ビュー・オブジェクトにセカンダリ・エンティティ・オブジェクト慣用名を定義する際に選択できる組合せをまとめます。
表11-4 ビュー行作成動作を制御するビュー・オブジェクト・プロパティ
更新可能 | 参照 | ビュー行の動作 |
---|---|---|
|
true |
この組合せでは、エンティティ・オブジェクト慣用名の属性を更新し、その属性の主キーの値との同期を保ちます。この組合せは、ビュー・リンク一貫性機能での動作が良好であるため、これを使用して、ビュー・オブジェクトにある、挿入を行うエンティティ・オブジェクト慣用名を1つにできます。 |
|
false |
この組合せでは、エンティティ・オブジェクト慣用名の属性を更新しますが、その属性は主キー参照によって変更されません。これはかなり稀な組合せで、ビュー・オブジェクトを既存のデータの更新または削除のみに使用する状況に最適です。この組合せでは、ユーザーは非参照の更新可能なエンティティ・オブジェクト慣用名に関連する属性を更新でき、ビュー行は適切な基礎のエンティティ行に変更を委譲します。 ノート: ビュー・リンク一貫性機能で、ビュー・オブジェクトの一部のセカンダリ・エンティティ・オブジェクト慣用名が |
false |
true |
「エンティティ・ベースのビュー・オブジェクトに対する結合の作成方法」で説明するように、これはデフォルトの動作です。この組合せは、エンティティ・オブジェクト慣用名を更新しないことを想定しています。 |
新しい行の作成をサポートするための複数の更新可能エンティティ(Updatable=true
、Reference=false
)と、コンポジットでないエンティティ・オブジェクト間のアソシエーションを持つビュー・オブジェクトが必要な場合は、「複数の更新可能エンティティ・オブジェクト慣用名を持つ新規行をプログラムで作成する方法」の説明に従って、コードを少し記述する必要があります。
複数の更新可能エンティティ・オブジェクト慣用名を持つ新規行をプログラムで作成する方法
新しい行の作成をサポートするための複数の更新可能エンティティ(Updatable=true
、Reference=false
)と、コンポジットでないエンティティ・オブジェクト間のアソシエーションを持つビュー・オブジェクトが必要な場合は、ビュー・オブジェクトのカスタム・ビュー行クラスのcreate()
メソッドをオーバーライドしてこれを有効にし、正しく動作するようにする必要があります。
ノート:
作成する必要があるのは、更新可能なエンティティ間のアソシエーションがコンポジットでない場合に新しい行の作成を処理するコードのみです。アソシエーションがコンポジットである場合は、ADFビジネス・コンポーネントにより自動的に処理されます。
複数の更新可能エンティティを持つビュー・オブジェクトでcreateRow()
を呼び出すと、更新可能なエンティティ・オブジェクトの慣用名ごとに新しいエンティティ行の部分が作成されます。このシナリオの複数エンティティはアソシエーションによって関連付けられているので、新しい関連のあるエンティティ行を正常に保存できるようにするには、3つのコードを実装する必要があります。
-
正しいポスト順序を制御するために含まれるエンティティ・オブジェクトで、
postChanges()
メソッドをオーバーライドすることが必要な場合があります。 -
関連付けられたエンティティの主キーが
DBSequence
を使用してデータベース順序によって移入され、複数のエンティティ・オブジェクトが関連付けられてはいても、コンポジットではない場合は、postChanges()
メソッドとrefreshFKInNewContainees()
メソッドをオーバーライドし、更新された主キー値を、一時的な値を参照していた関連行にカスケードする必要があります。 -
ビュー・オブジェクトのカスタム・ビュー行クラスの
create()
メソッドをオーバーライドし、親エンティティ・オブジェクトのコンテキストを新しく作成される子エンティティに渡すよう、デフォルトの行作成動作を変更する必要があります。
ステップ1および2のコードを理解するには、「制約違反を防ぐためのエンティティ・ポスト順序を制御する方法」で説明する、関連するエンティティ・オブジェクトの例を参照してください。最後に理解しておく必要があることは、ビュー行でcreate()
メソッドをオーバーライドする方法です。プライマリ・エンティティ・オブジェクト慣用名がProduct
で、セカンダリ・エンティティ・オブジェクト慣用名がInventory
である、ProductInventoryVO
ビュー・オブジェクトについて考えます。エンティティ・オブジェクトの慣用名Product
は、更新可能で非参照としてマークされ、エンティティ・オブジェクトの慣用名Inventory
は、参照エンティティ・オブジェクト慣用名であるものとします。
ノート:
この項の例では、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.multieoupdate
パッケージを参照します。
次の例は、ビュー行作成操作の間に、複数の更新可能エンティティ行部分を正しい順序で作成するために必要なコメント付きのコードを示しています。
/** * By default, the framework will automatically create the new * underlying entity object instances that make up this * view object row being created. * * We override this default view object row creation to explicitly * pre-populate the new (detail) InventoryImpl instance using * the new (master) ProductImpl instance. Since all entity objects * implement the AttributeList interface, we can directly pass the * new ProductImpl instance to the InventoryImpl create() * method that accepts an AttributeList. */ protected void create(AttributeList attributeList) { // The view row will already have created "blank" entity instances // so be sure to use the respective names of the entity instance. ProductImpl newProduct = getProduct(); InventoryImpl newInventory = getInventory(); try { // Let inventory "blank" entity instance to do programmatic defaulting newProduct.create(attributeList); // Let inventory "blank" entity instance to do programmatic // defaulting passing in new ProductImpl instance so its attributes // are available to the ProductInventoryVORowImpl's create method. newInventory.create(newProduct); } catch (JboException ex) { newProduct.revert(); newInventory.revert(); throw ex; } catch (Exception otherEx) { newProduct.revert(); newInventory.revert(); throw new RowCreateException(true /* EO Row? */, "Inventory" /* EO Name */, otherEx /* Details */); } }
このProductInventoryVO
ビュー・オブジェクトのビュー行クラス(ProductInventoryVORowImpl
クラス)で、Product
エンティティ・オブジェクトおよびInventory
エンティティ・オブジェクトの保護されたcreate()
メソッドを呼び出せるようにするには、各エンティティ・オブジェクト・クラスで次のようにcreate()
メソッドをオーバーライドし、メソッドへのアクセスを可能にする必要があります。
/** * Overridding this method in this class allows friendly access * to the create() method by other classes in this same package, like the * ProductInventoryVO view object implementation class, whose overridden * create() method needs to call this. * @param nameValuePair */ protected void create(AttributeList nameValuePair) { super.create(nameValuePair); }
create()
メソッドをオーバーライドするとき、メソッドの宣言は次の条件によって異なります
-
ビュー・オブジェクトとエンティティ・オブジェクトが同じパッケージ内にある場合、オーバーライドされた
create()
メソッドは保護されたアクセスが可能で、ProductInventoryVORowImpl
クラスがこれらにアクセスします。 -
いずれかのエンティティ・オブジェクトが異なるパッケージにある場合、
ProductImpl.create()
とInventoryImpl.create()
(どちらが別のパッケージにある場合でも)は、ProductInventoryVORowImpl
クラスがこれらを呼び出すことができるように、public
宣言される必要があります。
実行時の処理: ビュー行の作成
新しい行の作成をサポートするための複数の更新可能エンティティを持つビュー・オブジェクトが必要な場合、Referenceフラグがビュー行作成に関する動作および情報の自動アソシエーション・ドリブン参照を制御することを理解する必要があります。指定のエンティティ・オブジェクト慣用名のReferenceフラグを無効にすると、次のようになります。
-
新しいビュー行が作成されるたびに、そのエンティティ・オブジェクト慣用名に対して新しいエンティティ・インスタンスが作成されます。
-
そのエンティティ・オブジェクト慣用名に関するビュー行属性のストレージに対してビュー行が指すエンティティ行は、フレームワークによって自動的に変更されなくなります。
逆に、エンティティ・オブジェクト慣用名のReferenceフラグを有効のまま(デフォルト)にすると、次のようになります。
-
新しいビュー行が作成されても、そのエンティティ・オブジェクト慣用名に対して新しいエンティティ・インスタンスは作成されません。
-
そのエンティティ・オブジェクト慣用名に関するビュー行属性のストレージに対してビュー行が指すエンティティ行は、前記のエンティティのアソシエーションを行ったビュー行の属性に対して実行された変更との同期が自動的に保たれます。
PRODUCT表とINVENTORY表の情報を結合するProductInventoryVO
ビュー・オブジェクトを考えてみます。これは、ProductId
、PName
、ProductInventoryId
(ProductInventoryVO
の属性であることがわかるようにビュー・オブジェクト・エディタで選択された名前)、InventoryInventoryId
(ProductInventoryVO
の属性であることがわかるようにビュー・オブジェクト・エディタで選択された名前)およびAmountInStock
の各属性を表示します。
ここで、updatableとreferenceの両方のマークを付けられたセカンダリ・エンティティ・オブジェクトを設定するデフォルトのケースの実行時の処理について考えてみます。
-
Product
エンティティ・オブジェクトは、プライマリ・エンティティ・オブジェクト慣用名です。 -
Inventory
エンティティ・オブジェクトはセカンダリ・エンティティ・オブジェクト慣用名で、Referenceのマークを付けられています。
ユーザーが新しい行をProductInventoryVO
に作成すると、ADFビジネス・コンポーネントでは新しいProduct
エンティティ・インスタンスのみが作成されます(Inventory
エンティティ・オブジェクト慣用名のReferenceフラグが有効であるため)。ユーザーが製品のInventoryInventoryId
属性を10
に変更すると、ADFビジネス・コンポーネントでは、エンティティ・キャッシュで主キー10
のInventory
エンティティが自動的に検索され(まだキャッシュされていない場合にはデータベースから読み込む)、この新しいビュー行のInventory
エンティティ・オブジェクト慣用名がこの在庫10
のエンティティを指すようになります。その結果、在庫10
の正しいAmountInStock
値が自動的に反映されます。
デフォルトのシナリオでは、Inventory
のエンティティ・オブジェクト慣用名がreferenceとしてマークされていることと、Product
エンティティとInventory
エンティティの間にアソシエーションが存在することの両方によって、参照検索が発生します。アソシエーション定義を介して、ADFビジネス・コンポーネントは、どの属性がこのアソシエーションのどちら側と関連性があるかを認識します。ProductToInventory
アソシエーションのProduct
側の任意の属性が変更された場合、Inventory
エンティティ・オブジェクト慣用名がReferenceとマークされていると、ADFビジネス・コンポーネントにより自動参照検索が実行されます。ユーザーがこの新しいビュー行でAmountInStock
を20
に変更すると、変更のコミット後、データベースには在庫10
に対応する新しい製品が格納され、在庫量10
が20
に更新されます。
ここで、updatableとマークを付けられたセカンダリ・エンティティ・オブジェクトを設定し、referenceは無効にした場合の実行時の処理について考えてみます。
-
Product
エンティティ・オブジェクトは、プライマリ・エンティティ・オブジェクト慣用名です。 -
Inventory
エンティティ・オブジェクトはセカンダリ・エンティティ・オブジェクト慣用名ですが、この慣用名はReferenceのマークを付けられていません。
このシナリオでは、ユーザーが新しい行をProductInventoryVO
に作成すると、ADFビジネス・コンポーネントで新しいProduct
エンティティ・インスタンスと新しいInventory
エンティティ・インスタンスの両方が作成されます(Inventory
エンティティ・オブジェクト慣用名のReferenceフラグが無効であるため)。ユーザーが製品のInventoryId
属性を10
に変更しても、行に表示されているAmountInStock
属性の値には影響しません。また、ユーザーがこの新しいビュー行でInventoryInventoryId
を99
に、AmountInStock
を20
に設定すると、変更のコミット後、データベースには在庫10
に対応する新しい製品と、新しい在庫数99
の両方が格納されます。
プログラムによるビュー定義とビュー・オブジェクトの作成
ADFビュー・オブジェクトを作成する際、アプリケーションではXMLファイルを利用してビュー定義オブジェクトを作成します。あるいは、フレームワーク・クラスを使用してビュー定義オブジェクトをプログラムで作成できます。
oracle.jbo.server.ViewDefImpl
クラスでは、ビュー・オブジェクト・インスタンスのビュー定義メタオブジェクトを動的に定義できます。ビュー定義は、ビュー・オブジェクトの構造を示します。
一般に、アプリケーションでは、JDeveloperの概要エディタを使用して作成したXMLファイルをロードして、ビュー定義オブジェクトを作成します。アプリケーションでビュー・オブジェクト・インスタンスを作成する必要がある場合、ビュー定義名を使用してビュー・オブジェクトのビュー定義についてMetaObjectManager
に問い合せます。その後、XMLファイルを検索し、開いて解析して、メモリー内にビュー定義オブジェクトを作成します。
ViewDefImpl
クラスのメソッドを使用して、プログラムでビュー定義を作成することもできます。プログラムでビュー定義を作成する場合、アプリケーション・コードは次のようなコードで始まります。
ViewDefImpl viewDef = new ViewDefImpl("MyViewDef"); viewDef.setFullName("sessiondef.mypackage.MyViewDef");
作成するビュー定義は、その完全な名前により一意に識別される必要があります。この場合、完全な名前はパッケージ修飾名です。したがって、ビュー定義オブジェクトのパッケージ修飾名(たとえばsessiondef.mypackage.MyViewDef
)を渡すためにsetFullName()
をコールします。
最初に渡すMyViewDef
名は、作成するビュー定義の短縮名です。APIからビュー定義名が要求されたときにアプリケーションは短縮名を渡す場合があります。たとえば、ApplicationModule.createViewObject(String, String)
を呼び出すときにアプリケーションはdefName
パラメータをリクエストできます。
ビュー定義を作成した後、この定義に基づいてビュー・オブジェクト・インスタンスを作成するには、次の基本ステップに従います(後に例を示しています)。
-
ビュー定義オブジェクトを作成して、完全な名前を設定します。
-
ビュー・オブジェクトSQL文を定義します。
-
ビュー定義を解決して、MDSリポジトリに保存します。
-
ビュー定義を使用して、ビュー定義に基づくビュー・オブジェクトのインスタンスを作成します。
ノート:
ビュー定義をMDSリポジトリに保存するには、保存されるビュー定義に対してadf-config.xml
ファイルが適切に構成されている必要があります。adf-config.xml
ファイルの構成の詳細は、「MDSリポジトリの構成に関する必知事項」を参照してください。
/* * 1. Create the view definition object. */ ViewDefImpl v = new ViewDefImpl("DefNameForTheObject"); v.setFullName("sessiondef.some.unique.DefNameForTheObject"); /* * 2. Then, define the view object's SQL statement by using a fully- * specified custom 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); /* * 3. Then resolve and save the view definition. */ v.resolveDefObject(); v.writeXMLContents(); v.saveXMLContents(); /* * 4. Finally, use the dynamically-created view definition to construct * instances of view objects based on it. myAM is an instance of * oracle.jbo.ApplicationModule that will parent this VO instance. */ ViewObject vo = myAM.createViewObject("SomeInstanceName", v.getFullName());
MDSリポジトリの構成に関する必知事項
ビュー定義を作成したら、ビュー定義を記述してMDSリポジトリに保存することが重要です。正しく保存しないと、リクエストがクラスタ内の別のノードにリダイレクトされた場合に、他のノードから定義をロードしてアクセスできないため、問題が発生することがあります。
定義を保存するには、adf-config.xml
のmds-config
要素を定義する必要があります。たとえば、adf-config.xml
ファイルには、次の例に示すような定義を含める必要があります。
<mds-config version="11.1.1.000"> <persistence-config> <!-- metadata-namespaces must define /sessiondef and /persdef namespaces --> <metadata-namespaces> <namespace path="/sessiondef" metadata-store-usage="mymdsstore"> <namespace path="/persdef" metadata-store-usage="mymdsstore"> </metadata-namespaces> <metadata-store-usages> <metadata-store-usage id="mymdsstore" default-cust-store="true"> <metadata-store name="fs1" class-name="oracle.mds.persistence.stores.file.FileMetadataStore"> <!-- metadata-path value should be the absolute dir path where you want the metadata documents to be written --> <property name="metadata-path" value="/tmp"> </metadata-store> </metadata-store-usage> </metadata-store-usages> </persistence-config> <cust-config> <match path="/"> <customization-class name="oracle.adf.share.config.UserCC"> </match> </cust-config> </mds-config>
adf-config.xml
ファイルですでにmetadata-store-usage
要素が定義されている場合は、metadata-store-usage
定義を使用するように、2つのネームスペース/sessiondef
と/persdef
を定義できます。adf-config.xml
ファイルのMDS構成エントリの詳細は、「adfc-config.xml」を参照してください。MDSリポジトリの構成の詳細は、『Oracle Fusion Middlewareの管理』の「メタデータ・リポジトリの管理」を参照してください。
実行時のビュー・オブジェクト作成に関する必知事項
「プログラムによるビュー定義とビュー・オブジェクトの作成」で説明しているように、実行時のビュー・オブジェクトの作成に関連するオーバーヘッドを理解しておくことが重要です。ビジネス要件でどうしても必要でないかぎり、行わないようにしてください。たとえば、アプリケーションで発行する問合せで、対象の表の名前が設計時にわかっていて、取得する列のリストも固定している場合は、設計時にビュー・オブジェクトを作成します。このようにすると、SQL文は適切にカプセル化され、開発中に簡単に説明およびチューニングでき、結果の行の構造とデータ型を検出するために実行時にオーバーヘッドをかけることがなくなります。
これに対し、実行時にApplicationModule
インタフェースでcreateViewObjectFromQueryStmt()
APIを使用すると、問合せはコードに埋もれてしまい、前もってSQLをチューニングすることが困難になり、ビュー・オブジェクトを作成するたびにパフォーマンスが犠牲になります。理論上、動的に作成されるビュー・オブジェクトのSQL問合せ文は、このAPIを使用して新しいインスタンスが作成されるたびに異なるので、実行中に問合せ結果の形状を検出するために、余分なデータベース・ラウンドトリップが必要になります。問い合せる表の名前が実行時までわからない場合にのみ、問合せを動的に作成します。それ以外のほとんどの要件には、設計時に作成するビュー・オブジェクトと、固定のWHERE句にバインド変数を設定する、または実行時にWHERE句(オプションのバインド変数を含む)を追加するランタイムAPIを組み合せることで対応できます。
挿入、更新および削除の宣言による禁止
ADFビジネス・コンポーネントには拡張クラスが用意されており、ビュー・オブジェクトでの挿入、更新および削除を制御できます。
Oracle Formsなどの一部の4GLツールでは、特定のデータ・コレクションが挿入、更新または削除を許可するかどうかを制御する宣言的プロパティが提供されています。現在のリリースでは、まだビュー・オブジェクトはこれを組込み機能としてサポートしていませんが、ビュー・オブジェクトでの挿入、更新または削除を制御するための開発者提供のフラグとしてカスタム・メタデータ・プロパティを利用するフレームワーク拡張クラスを使用すれば、この機能を簡単に追加できます。
ノート:
この項の例では、SummitADF_Examples
アプリケーション・ワークスペースのoracle.summit.model.declblock
パッケージを参照します。
開発者が個別のビュー・オブジェクト・インスタンスを制御できるようにするには、ビュー・オブジェクト・インスタンスと同じ名前でアプリケーション・モジュール・カスタム・プロパティを使用するという規則を導入します。たとえば、アプリケーション・モジュールに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; }
次の例は、ビュー行が実装を完了するために、カスタム・フレームワーク拡張クラスで必要な他のコードを示しています。これは、次のメソッドをオーバーライドします。
-
isAttributeUpdateable()
挿入が許可されていない場合に新しい行のフィールドを無効にするため、または更新が許可されていない場合に既存の行のフィールドを無効にするために、ユーザー・インタフェースを有効化します。
-
setAttributeInternal()
挿入が許可されていない場合に新しい行の属性値が設定されないようにします。また、更新が許可されていない場合に既存の行の属性が設定されないようにします。
-
remove()
削除が許可されない場合に削除を禁止します。
-
create()
挿入が許可されない場合に作成を禁止します。
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 }
プログラム関連ビュー・オブジェクト・トリガーに関する必知事項
Groovyスクリプト言語で定義したトリガーを、ProgrammaticViewObjectImpl.java
のオーバーライド後のメソッドのかわりに使用して、カスタム・データソースでデータ取得操作を実行できます。トリガーは、様々なプログラム関連ビュー・オブジェクト・ライフサイクル・ポイントからコールできます。
アプリケーションでは、Groovyスクリプトで定義したトリガーをJavaフック・ポイントのかわりにコールできます。これらのトリガーは、カスタム・データ・ソースと対話し、データ取得操作を実行します。これらは、様々なプログラム関連ビュー・オブジェクト・ライフサイクル・ポイントからコールできます。フレームワーク・クラス(ProgrammaticViewObjectImpl
など)をサブクラス化し、カスタム・データソースに固有の実装を定義する特定のメソッドをオーバーライドするオプションは残りますが、トリガーを実行するためには、Javaフック・ポイントを定義しないでください。別の方法として、Javaフック・ポイントを定義した場合、そのフック・ポイントを実行するには、トリガーが存在していてはいけません。Javaフック・ポイントとトリガーの両方が定義されている場合、トリガーは実行されないことに注意してください。アプリケーションでは、オーバーライド後のメソッドでGroovyスクリプトを使用して、カスタム・データ・ソースと対話できます。これらのトリガーは、様々なプログラム関連ビュー・オブジェクト・ライフサイクル・ポイントからコールできます。
具体的なプログラム関連ビュー・オブジェクト・トリガーを次に示します。
FetchData
このトリガーは、カスタム・データソースからデータ・コレクションを取得する場合に使用します。このトリガーは、ビュー・オブジェクトのアクセス・モードがスクロールか範囲ページングかに応じて、ProgrammaticViewObjectImpl.java
のgetScrollableData()
メソッドまたはgetRangePagingData()
メソッドのかわりに起動されます。returnList
データ・コレクションには、カスタム・データ・ソースから取得されたレコード(オブジェクト/マップ)が移入されます。
次のサンプル・コードは、このトリガーの使用方法を示しています。このサンプル・コードでは、次のコンテキスト固有キーワードが使用されています。
-
dataFilter
- DataFilterオブジェクトを戻します。ビュー・オブジェクトのモードに応じて、ScrollableDataFilterかRangePagingDataFilterのどちらかになります。 -
dataFilter.fetchSize
- フェッチするデータの範囲サイズを戻します。 -
dataFilter.fetchStart
- フェッチするデータの範囲開始値を戻します。 -
dataFilter.searchCriteria
- ビュー・オブジェクトに適用されるViewCriteriaのリストを戻します。 -
dataFilter.sortCriteria
- ビュー・オブジェクトに適用されるSortCriteriaのリストを戻します。 -
dataFilter.bindParameterValues
- 現在実行中の行セットに対応するバインド・パラメータの名前と値を含むマップを戻します。 -
dataFilter.masterData
- ディテール・ビュー・オブジェクトのコンテキストで使用され、マスター行のdataproviderオブジェクトを戻します。 -
returnList
- カスタム・データ・ソースからのレコード(オブジェクト/マップ)を移入するコレクション。
@TriggerExpression(triggerType="FetchData", name="FetchData_Rule_0") def FetchData_Rule_0_ValidationRuleScript_Trigger() { def empData = model.data.DataCenter.getInstance().getEmpData() def empFilteredData = new ArrayList() model.data.DeptObject master = (model.data.DeptObject)dataFilter.masterData for (emp in empData) { if (master == null || emp.deptno.equals(master.deptno)) { empFilteredData.add(emp) } } def fetchStart = dataFilter.fetchStart def fetchSize = dataFilter.fetchSize if(fetchStart == -1) { fetchStart = 0; } for(int i = fetchStart; i < fetchSize + fetchStart && i < empFilteredData.size() ; i++) { returnList.add(empFilteredData.get(i)) @TriggerExpression(triggerType="RetrieveDataByKey", name="RetrieveDataByKey_Rule_0") def RetrieveDataByKey_Rule_0_ValidationRuleScript_Trigger() { def empData = model.data.DataCenter.getInstance().getEmpData() def keyValue = dataFilter.key.keyValues[0] def iter = empData.iterator() while (iter.hasNext()) { def emp = iter.next(); if (emp.empno.equals(keyValue)) { returnList.add(emp); } } } }
RetrieveDataByKey Trigger
このトリガーは、カスタム・データソースからキー値と一致するデータ・コレクションを取得する場合に使用します。これは、ProgrammaticViewObjectImpl.java
のretrieveDataByKey
メソッドのかわりに起動されます。このトリガーは、ビュー・オブジェクトにより取得された現在の行範囲にキーと一致する必要なデータが存在しないときに起動されます。このトリガーは、キー値と一致するオブジェクト/マップのコレクションを取得します。
次のコード・サンプルは、このトリガーの使用方法を示しています。このサンプル・コードでは、次のコンテキスト固有キーワードが使用されています。
-
dmlFilter.key
- 取得するデータ/オブジェクトのキー値を含むoracle.jbo.Keyを戻します。 -
dmlFilter.fetchSize
- フェッチ・サイズを戻します。 -
returnList
- カスタム・データ・ソースからキー値と一致するレコード(オブジェクト/マップ)を移入するコレクション。
@TriggerExpression(triggerType="RetrieveDataByKey", name="RetrieveDataByKey_Rule_0") def RetrieveDataByKey_Rule_0_ValidationRuleScript_Trigger() { def empData = model.data.DataCenter.getInstance().getEmpData() def keyValue = dataFilter.key.keyValues[0] def iter = empData.iterator() while (iter.hasNext()) { def emp = iter.next(); if (emp.empno.equals(keyValue)) { returnList.add(emp); } } }
プログラムによるビュー・オブジェクトのトリガーの定義
ADFビジネス・コンポーネントを使用すると、プログラムによるビュー・オブジェクトのライフサイクルでトリガーを適用できます。トリガーは、設定されたライフサイクル・イベント時に実行されます。Groovyスクリプトを使用して、これらのプログラムによるビュー・オブジェクトのトリガーに対応して実行されるビジネス・ルールを実装できます。
- アプリケーション・ナビゲータで、トリガーを追加するビュー・オブジェクトをダブルクリックします。
- 概要エディタで、「ビジネス・ルール」ナビゲーション・タブをクリックします。
- 「ビジネス・ルール」ページで、ビュー・レベル・トリガーのノードを選択し、 「新規トリガーの作成」アイコンをクリックします。
- 「トリガーの追加」ダイアログ・ボックスで、「タイプ」ドロップダウン・リストから適切なトリガー・ポイントを選択します。
- 「トリガー定義」タブで、Groovy式を入力します。
<trigger>
タグが作成され、ビュー・オブジェクトに関連付けられた.bcs
ファイルへの移入も行われます。Groovy式は、実行時に評価されるとき、信頼できる式として明示的に指定されていないかぎり、信頼できないと想定されます。実行時例外を回避するには、「プロパティ」ウィンドウを使用し、信頼できる式としてその式を指定します。