| Oracle® Fusion Middleware Oracle Application Development FrameworkによるFusion Webアプリケーションの開発 12c (12.1.2) E48099-02 |
|
![]() 前 |
![]() 次 |
この章では、Oracle ADFアプリケーションでADFビュー・オブジェクトを設計および利用するときに使用できる、ADFビジネス・コンポーネントAPIのプログラムによる使用方法について説明します。
この章には次の項が含まれます:
前述したとおり、ビュー・オブジェクトの基本的な問合せ機能はすべて、カスタムJavaコードを使用せずに実行できます。クライアントは、ビュー・オブジェクト開発者側のカスタム・コードに頼ることなく、任意のSQL問合せのデータを取得および反復処理できます。要するに、多くのビュー・オブジェクトでは、SQL文を定義すればそれで完了です。ただし、必要になった場合に備えて、ビュー・オブジェクトでのカスタムJava生成を有効化する方法を理解しておくことが重要です。たとえば、カスタムJavaクラスでコードを作成する理由には次のようなものがあります。
検証メソッドを追加するため(ただし、Groovyスクリプトの式でサポートされているため、それを使用すればJavaは不要)
カスタム・ロジックを追加するため
組込み動作を拡張するため
通常、カスタム・ビュー・オブジェクトおよびビュー行クラスで記述、使用およびオーバーライドする一般的なコードの詳細は、付録D「ADFビジネス・コンポーネントのよく使用されるメソッド」を参照してください。
ビュー・オブジェクトのカスタムJavaクラスの生成を有効にするには、ビュー・オブジェクトの概要エディタの「Java」ページを使用します。図10-1に示すように、ビュー・オブジェクトに関連付けることができるオプションのJavaクラスが3つあります。リストの初めの2つは、非常によく使用されます。
問合せを実行し、実行のライフサイクルを制御するコンポーネントを表すビュー・オブジェクト・クラス
問合せ結果の各行を表すビュー行クラス
カスタム・ビュー・オブジェクト・クラスの生成を有効にする場合、図10-1に示すように「バインド変数アクセッサを含める」チェック・ボックスも選択すると、JDeveloperによりビュー・オブジェクト・クラスにgetterメソッドおよびsetterメソッドが生成されます。ProductViewビュー・オブジェクトには、バインド変数(bv_ProductName)があるため、カスタムのProductViewImpl.javaビュー・オブジェクト・クラスの対応するメソッドは次のようになります。
public String getbv_ProductName() {...}
public void setbv_ProductName(String value){...}
これらのメソッドを使用すると、バインド変数にコンパイル時型検証を設定できるため、適切な型の値を設定していることを確認できます。つまり、bv_ProductNameの値を設定する場合、次のようなコードは、さらに簡略化できます。
vo.setNamedWhereClauseParam("bv_ProductName","ball");
次のようなコードを記述できます:
vo.setbv_ProductName("ball");
後者の方法では、setbv_ProductNameではなく誤ってsetbv_Nameと入力したときにJavaコンパイラによって入力ミスが捕捉されます。
// spelling name wrong gives compile error
vo.setbv_Name("ball");
または、String値ではなく「150」を渡すなど、不適切なデータ型の値を渡した場合は、次のようになります。
// passing Integer where String expected gives compile error vo.setbv_ProductName(150);
バインド変数アクセッサが生成されていない場合、次のような不適切なコード行をコンパイラで捕捉できません。
// Both variable name and value wrong, but compiler cannot catch it.
// Note the method setNamedWhereClauseParam() sets the value only on the efault
// RowSet and not on the secondary RowSet.
vo.setNamedWhereClauseParam("bv_Name",150);
これには、スペルが正しくないバインド変数名とデータ型が誤っているバインド変数値が両方とも含まれています。ViewObjectインタフェース上で汎用APIを使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
カスタム・ビュー行クラスの生成を有効にする場合に、図10-1に示すように「アクセッサを含める」チェック・ボックスも選択すると、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を使用する場合、このようなエラーはコンパイル時に捕捉されないため、実行時に例外が発生します。
カスタム・ビュー行クラスの生成を有効にする場合に、ビュー行属性アクセッサを生成する場合は、「クライアントへのアクセッサの公開」チェック・ボックスも選択できます。これにより、生成される付加的なカスタム行インタフェース(アプリケーション・クライアントで使用可能)が、実装クラスに直接依存することなく行のカスタム・メソッドにアクセスできるようになります。
|
ベスト・プラクティス: ビジネス・コンポーネントのクライアント・コードを作成する場合、具体的なクラスのかわりにビジネス・サービス・インタフェースを使用する必要があります。実装クラスではなくインタフェースを使用することで、サーバー側の実装が変更されてもクライアント・コードを変更する必要が生じません。クライアント・コードの使用の詳細は、3.5.4項「カスタム・インタフェースのサポートに関する必知事項」を参照してください。 |
たとえば、ProductViewビュー・オブジェクトの場合、クライアントへのアクセッサの公開によって、ProductViewRowという名前のカスタム行インタフェースが生成されます。このインタフェースは、ビュー・オブジェクトがあるパッケージのcommonサブパッケージに作成されます。行インタフェースを持つことにより、クライアントでは、強く型付けされた方式で問合せ結果の属性にアクセスするコードを記述できるようになります。例10-1は、printAllAttributes()やtestSomethingOnProductRows()のようなメソッドをコールできるようにProductViewRowインタフェースへのproductByName.first()メソッドの結果をキャストする、TestClientサンプル・クライアント・プログラムを示しています。
例10-1 アクセッサによるクライアント行インタフェースの使用の単純な例
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クラスの生成方法を示しました。
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」または「適用」ボタンをクリックしても、ファイルが知らぬ間に再生成されることはありません。かわりに、管理が必要なメソッドはスマートに更新され、独自のカスタム・コードはそのまま残されます。
ビュー・オブジェクトは、XML専用モードで機能するか、XMLドキュメントとカスタムJavaクラスの組合せを使用して機能するように設計されています。属性値はビュー行クラスのプライベート・メンバー・フィールドには格納されないため、XML専用モードの場合、このようなクラスは存在しません。かわりに、属性はAttributesEnumタイプとして定義されます。このタイプでは、ビュー・オブジェクトのXMLドキュメントに基づいて、そのファイルの<ViewAttribute>タグ、関連<ViewLinkAccessor>タグおよび<ViewAccessor>タグの順序で、属性名(および各属性のアクセッサ)が指定されます。実行時には、ビュー行の属性値は、ビュー・オブジェクトの属性リスト内における属性の数値的位置による索引が付けられた状態で、ViewRowImplベース・クラスによって管理される構造に格納されます。
多くの場合、このプライベート実装に関する詳細は重要ではありません。ただし、ビュー行のカスタムJavaクラスを有効にする場合、ビュー行クラスでJDeveloperにより自動的に管理される生成コードの一部にこの実装の詳細が関連し、コードの使用目的を理解できます。たとえば、Usersビュー行のカスタムJavaクラスの場合、例10-2に示されているように、属性、ビュー・リンク・アクセッサ属性またはビュー・アクセッサ属性ごとに対応する生成済のAttributesEnum列挙定数があります。JDeveloperは、複数の開発者が新しい属性をXMLドキュメントに追加した結果発生する可能性があるマージ競合を防ぐために、定数ではなく列挙定数を定義します。
例10-2 カスタム・ビュー行Javaクラスで自動的に管理される属性定数
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);
}
属性に関連して生成されたこのコードに関する経験則は、次のとおりです。
必要に応じて、強く型付けされた属性のgetterメソッドおよびsetterメソッドの内部にカスタム・コードを追加してください。
ビュー・オブジェクトの概要エディタを使用して、ビュー・オブジェクト属性の順序または型を変更してください。
getterメソッドおよびsetterメソッドのJavaシグネチャや関連するXMLドキュメントは、自動的に変更されます。
生成されたAttributesEnum列挙定数のリストは変更しないでください。
getAttrInvokeAccessor()メソッドおよびsetAttrInvokeAccessor()メソッドは変更しないでください。
ビュー・オブジェクト・クラスまたはビュー・オブジェクト行クラスで実装するカスタム・メソッドは、アプリケーション・モジュールの戻り型に依存できません。実行時には、ある場合、そのような依存性を持って実行されるメソッドがClassCastExceptionをスローすることがあります。戻されたアプリケーション・モジュールが、期待された型と一致しないからです。そのため、実装するカスタム・メソッドには、次に示すように、特定のアプリケーション・モジュールやビュー・オブジェクトの実装を取得するコードを記述しないことをお薦めします。
((MyAM)getRootApplicationModule()).getMyVO
特に、次のシナリオにおいて、上のコードがClassCastExceptionで失敗します。
ADFビジネス・コンポーネント・フレームワークによって、ビュー・オブジェクトが定義されているコンテナとは別のコンテナ・アプリケーション・モジュールでインスタンス化される場合。通常、ビュー・オブジェクトは、その使用方法を宣言するコンテナ・アプリケーション・モジュール(XML定義)でインスタンス化されますが、ADFビジネス・コンポーネント・ランタイムでは、各アプリケーション・モジュールに関連付けられたコンテナを固定することが保証されていません。つまり、親アプリケーション・モジュール・タイプで依存性を持つメソッドを実装しても、それらのメソッドが一貫性を持って実行されない場合があります。
ルート・アプリケーション・モジュールの下に、手動でアプリケーション・モジュールをネストする場合。この場合、ネストされたアプリケーション・モジュールが同じTransactionオブジェクトを共有するため、前述のコードによって、期待されるアプリケーション・モジュールの型が戻される保証はありません。
ADFビジネス・コンポーネント・フレームワークの実装がリリースを追うごとに変化する場合。たとえば、以前のリリースでは、アプリケーションがADFタスク・フローを使用して定義した宣言トランザクションを制御するために、フレームワークで内部的なルート・アプリケーション・モジュールが作成されていました。
ビュー・オブジェクトの概要エディタで複数の名前付きビュー基準を定義し、必要に応じて実行時にその任意の組合せをビュー・オブジェクトに選択的に適用できます。設計時の名前付きビュー基準の処理の詳細は、5.9.1項「名前付きビュー基準を宣言的に作成する方法」を参照してください。
|
注意: この項の例では、 |
名前付きビュー基準を適用するには、setApplyViewCriteriaNames()メソッドを使用します。このメソッドは、適用する基準の名前のString配列を受け取ります。複数の名前付き基準を適用する場合は、実行時に生成されるWHERE句の中でANDにより結合されます。setApplyViewCriteriaNames()メソッドで適用した新しいビュー基準は、以前適用されたすべてのビュー基準を上書きします。以前適用されたビュー基準に単一のビュー基準を追加する場合、setApplyViewCriteriaName()メソッドを使用することもできます。
複数の名前付きビュー基準を適用する必要があるとき、ビュー・オブジェクトのクライアント・インタフェースでカスタム・メソッドを公開し、適用する名前付きビュー基準の組合せをカプセル化できます。たとえば、例10-3で示されているカスタム・メソッドshowCustomersForSalesRep()、showCustomersForCreditRating()およびshowCustomersForSalesRepForCreditRating()は、それぞれがsetApplyViewCriteriaNames()メソッドを使用して、名前付きビュー基準の適切な組合せを適用しています。これらのメソッドをビュー・オブジェクトのクライアント・インタフェースで公開すると、実行時に、クライアントは必要に応じてこれらのメソッドを呼び出し、ビュー・オブジェクトによって表示される情報を変更できます。
例10-3 適切な名前付き基準を有効にするためのクライアント・メソッドの公開
// 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();
}
現在適用されている名前付きビュー基準を除去するには、setApplyViewCriteriaNames(null)を使用します。たとえば、例10-4のshowAllCustomers()メソッドをCustomersViewビュー・オブジェクトに追加し、クライアント・インタフェースで公開できます。このようにすると、クライアントは必要に応じてフィルタ処理されないデータのビューに戻すことができます。
行レベル・バインド変数値が行セットに対してすでに適用されている可能性があるため、設計時ビュー基準は削除しないでください。確実を期すため、設計時にビュー・アクセッサに対して定義された名前付きビュー基準は、ビュー基準のライフサイクル・メソッドによって削除されないように、ビュー・オブジェクト・インスタンスに必須のビュー基準として適用されます。
例10-4 適用されたすべての名前付きビュー基準の除去
// In CustomerViewImpl.java
public void showAllCustomers() {
setApplyViewCriteriaNames(null);
executeQuery();
}
|
注意:
|
実行時に、アプリケーションでは、単一のビジネス・オブジェクト・インタフェースで異なるクライアント・メソッドを呼び出し、異なるフィルタ処理が行われたデータのセットを戻すことができます。例10-5は、前述のCustomerViewビュー・オブジェクトを使用するテスト・クライアント・クラスの行を示しています。showResults()メソッドは、ビュー・オブジェクトの行を反復処理して属性を表示するヘルパー・メソッドです。
例10-5 名前付きビュー基準を使用するテスト・クライアントのコード
// 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] ...
デフォルトでは、ビュー・オブジェクトはデータベースに対して問合せを実行し、結果の行セットに行を取得します。ただし、ビュー・オブジェクトを使用して、メモリー内で検索とソートを実行し、データベースへの不必要なトリップを避けることもできます。この種の操作は、ビュー・オブジェクト行セットのまだポストされていない新しい行をソートおよびフィルタ処理する場合に最適です。この操作が行われないと、ポストされていない行は行セットの先頭に追加されます。
|
注意: この項の例では、 |
ビュー・オブジェクトの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式では列名の大文字と小文字は区別されませんが、 |
setSortBy()およびsetQueryMode()メソッドを使用して、読取り専用のビュー・オブジェクトによって生成された行に対してメモリー内ソートを実行できます。例10-6は、読取り専用のビュー・オブジェクトCustomersInTxによって生成された行に対してメモリー内ソートを実行するために、setSortBy()とsetQueryMode()を使用する、TestClientSetSortByクラスの興味深いコード行を示しています。
例10-6 メモリー内ソートでのsetSortByとsetQueryModeの組合せ
// 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()の呼出しを含む例10-6の1行目では、ALTER SESSION SET SQL TRACEコマンドを発行して、現在のデータベース・セッションでSQLトレースを有効にしています。これにより、Oracleデータベースは、実行されるすべてのSQL文をサーバー側のトレース・ファイルに記録します。データベースが文を解析した回数や、問合せ結果を取得する間に行のバッチをフェッチするためにクライアントが行ったラウンドトリップの回数など、各SQL文のテキストに関する情報が記録されます。
|
注意:
|
生成されたトレース・ファイルは、データベースに付属するTKPROFユーティリティを使用して書式設定できます。
tkprof xe_ora_3916.trc trace.prf
TKPROFユーティリティでの作業の詳細は、『Oracle Databaseパフォーマンス・チューニング・ガイド』の「アプリケーション・トレース・ツールの使用」の章の、SQLトレースとTKPROFの理解に関する項およびSQLトレース機能とTKPROFの使用に関する項を参照してください。
このユーティリティでは、trace.prfファイルが生成されます。このファイルには、CustomersInTxビュー・オブジェクトによって実行されたSQL文に関する、例10-7のような興味深い情報が含まれます。最初に1回の実行で6行のデータを問い合せてデータベースからフェッチした後、その後に行われたこれらの結果に対する2回のソートでは実行が行われていないことがわかります。コードでSQLモードがViewObject.QUERY_MODE_SCAN_VIEW_ROWSに設定されているため、setSortBy()の後のexecuteQuery()ではメモリー内でソートが実行されました。
例10-7 メモリー内でソートが行われたことを示すトレースファイルのTKPROF出力
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
ビュー基準によるメモリー内フィルタ処理で使用できる演算子を表10-1に示します。カッコを使用して副次式をグループ化でき、副次式の間にはANDとORの演算子を使用できます。
表10-1 ビュー基準によるメモリー内フィルタ処理でサポートされているSQL演算子
| 演算子 | 操作 |
|---|---|
|
|
比較 |
|
|
論理否定 |
|
|
論理積 |
|
|
論理和 |
例10-8は、前述のデータベースに対するものとメモリー内の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
例10-8 ビュー基準によるデータベースおよびメモリー内でのフィルタ処理の実行
// 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 rm =
new RowMatch("LastName = 'Popp' or (FirstName like 'A%' and LastName like 'K%')");
SortBy句と同じように、ビュー・オブジェクトの属性名に関するRowMatchの式を、表10-2に示す、サポートされる演算子を使用して表現します。カッコを使用して副次式をグループ化でき、副次式の間にはANDとORの演算子を使用できます。
表10-2 RowMatchによるメモリー内フィルタ処理でサポートされているSQL演算子
| 演算子 | 操作 |
|---|---|
|
|
比較 |
|
|
論理否定 論理否定演算
|
|
|
論理積 |
|
|
論理和 |
表10-3に示すように、RowMatch式ではSQL機能の一部も使用できます。
表10-3 RowMatchによるメモリー内フィルタ処理でサポートされているSQL関数
| 演算子 | 操作 |
|---|---|
|
|
文字列内のすべての文字を大文字に変換する。 |
|
|
数または日付を文字列に変換する。 |
|
|
文字列を日付形式に変換する。 |
|
|
文字列をタイムスタンプに変換する。 |
|
注意: SQL問合せ条件では列名の大文字と小文字は区別されませんが、 |
ビュー・オブジェクトにRowMatchを適用するには、setRowMatch()メソッドを呼び出します。ViewCriteriaとは異なり、RowMatchはメモリー内フィルタ処理にのみ使用するため、設定する一致モードはありません。サポートされるすべてのSQLモードのビュー・オブジェクトに対してRowMatchを使用でき、適用した結果は、次にexecuteQuery()メソッドを呼び出したときに表示されます。
ビュー・オブジェクトにRowMatchを適用するとき、RowMatchの式では、SQL文で使用するものと同じ:VarName表記法を使用して、ビュー・オブジェクトの名前付きバインド変数を参照できます。たとえば、ビュー・オブジェクトにStatusCodeという名前の名前付きバインド変数がある場合は、次の式でRowMatchを適用できます。
Status = :StatusCode or :StatusCode = '%'
例10-9は、活動中の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]
例10-9 RowMatchによるメモリー内フィルタ処理の実行
// In TestClientRowMatch.java
// 1. Query the full customer list
ViewObject vo = am.findViewObject("CustomerList");
vo.executeQuery();
showRows(vo,"Initial database results");
// 2. Mark odd-numbered rows selected by setting Selected = Boolean.TRUE
markOddRowsAsSelected(vo);
showRows(vo,"After marking odd rows selected");
// 3. Use a RowMatch to subset row set to only those with Selected = true
RowMatch rm = new RowMatch("Selected = true");
vo.setRowMatch(rm);
// Note: Only need to set SQL mode when not defined at design time
vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_VIEW_ROWS);
vo.executeQuery();
showRows(vo, "After in-memory filtering on only selected rows");
// 5. Further subset rowset using more complicated RowMatch expression
rm = new RowMatch("LastName = 'Popp' "+
"or (FirstName like 'A%' and LastName like 'K%')");
vo.setRowMatch(rm);
vo.executeQuery();
showRows(vo,"After in-memory filtering with more complex expression");
// 5. Remove RowMatch, set query mode back to database, requery to see full list
vo.setRowMatch(null);
vo.setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES);
vo.executeQuery();
showRows(vo,"After requerying to see a full list again");
RowMatchを使用して行セットをフィルタするだけでなく、rowQualifies()メソッドを使用すると、それがカプセル化している基準に個別の行が一致するかどうかをテストすることもできます。例:
RowMatch rowMatch = new RowMatch("CountryId = 'US'");
if (rowMatch.rowQualifies(row)) {
System.out.println("Customer is from the United States ");
}
RowMatchを適用すると、ビュー・オブジェクトのSQLモードがデータベースからの行の取得に設定されている場合、executeQuery()を呼び出すと、フェッチされた行にRowMatchが適用されます。フェッチされた行が認定されない場合、その行は行セットに追加されません。
SQLのWHERE句とは異なり、RowMatchは、一時ビュー・オブジェクト属性およびまだポストされていない属性値を含む式でも評価できます。これは、Javaで値が計算される一時ビュー行属性を含むRowMatch式に基づいて、問い合された行をフィルタするのに便利です。ただし、この興味深い機能は、アプリケーションが大きな行セットを処理する必要がある場合には注意して使用する必要があります。最初にデータベース・レベルのフィルタ処理を使用してできるかぎり小さい行セットを取得した後、RowMatchを使用してメモリーでそのリストからサブセットを作成することをお薦めします。
Worldwide Web Consortium (W3C)が定めるExtensible Markup Language (XML)標準では、電子データ交換用の言語に依存しない方式が定義されています。厳密な一連の規則により、判読可能なテキスト・ドキュメントを使用して、データに固有の構造を簡単にエンコードし、正確に解釈できます。
ビュー・オブジェクトでは、問い合されたデータに基づいて、XMLドキュメントを記述することができます。また、XMLドキュメントを読み取って、挿入、更新、削除などの変更をデータに適用することもできます。ビュー・リンクを導入すると、このXML機能は、任意の複雑さのマスター/ディテール階層の複数レベルでネストされた情報の読取りと書込みをサポートします。ビュー・オブジェクトが生成および使用するXMLは正規書式に従いますが、ビュー・オブジェクトのXML機能とXML Stylesheet Language Transformations (XSLT)を組み合せることで、正規の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パラメータ・フラグに基づくデータ・モデルにあるもののみをレンダリングすることを指示できます。
writeXML()を使用してXMLを生成すると、ビュー・オブジェクトは最初に、ラップするXML要素を作成します。そのデフォルト名は、ビュー・オブジェクト定義の名前と一致しています。たとえば、CustomersViewビュー・オブジェクトの場合は、生成されるXMLをラップする一番外側のタグはCustomersViewタグになります。
次に、ビュー・オブジェクトは、適切な行の属性データをXML要素に変換します。デフォルトでは、各行のデータは、ビュー・オブジェクトの名前に接尾辞Rowを付加した名前の行要素でラップされます。たとえば、CustomersViewという名前のビュー・オブジェクトの各データ行は、CustomersViewRow要素でラップされます。各行の属性データを表す要素は、この行要素内にネストされた子として記述されます。
いずれかの属性がビュー・リンク・アクセッサ属性であり、writeXML()に渡されたパラメータによって有効になっている場合は、ビュー・リンク・アクセッサによって返されるディテール行セットのデータも含められます。このネストされたデータは、ビュー・リンク・アクセッサ属性の名前によって名前が決まる要素でラップされます。writeXML()メソッドの戻り値は、W3Cの標準のNodeインタフェースを実装するオブジェクトで、生成されたXMLのルート要素を表します。
|
注意:
|
たとえば、CustomersViewビュー・オブジェクト・インスタンスのすべての行に対するXML要素を生成し、存在する最大レベルの深さまでビュー・リンク・アクセッサをたどる場合は、例10-10に示すようなコードが必要です。
例10-10 ビュー・オブジェクトの全行の全ビュー・リンク・レベルまで含めたXMLの生成
ViewObject vo = am.findViewObject("CustomersView");
printXML(vo.writeXML(-1,XMLInterface.XML_OPT_ALL_ROWS));
CustomersViewビュー・オブジェクトは、その個人が作成したオーダーを表すOrdersビュー・オブジェクトにリンクされています。そして、Ordersビュー・オブジェクトは、顧客がオーダーした品目の詳細を提供するOrderItemsビュー・オブジェクトにリンクされています。例10-10のコードを実行すると、ビュー・リンクによって定義されているネストされた構造を反映した、例10-11で示されるXMLが生成されます。
例10-11 CustomersViewビュー・オブジェクトと2レベルのビュー・リンクされたディテールから生成される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要素」に値SomeOtherNameを設定すると、その属性に使用されるXML要素の名前が<SomeOtherName>に変わります。
たとえば、CustomersViewビュー・オブジェクトのEmail属性で定義されているこのプロパティは、例10-11のXML要素を<Email>から<EmailAddress>に変更します。
「プロパティ」ウィンドウの「一般」ナビゲーション・タブを選択し、カスタム・ビュー・オブジェクト・レベルのプロパティ「XML行要素」に値SomeOtherRowNameを設定すると、そのビュー・オブジェクトに使用されるXML要素の名前が<SomeOtherRowName>に変わります。
たとえば、CustomersViewビュー・オブジェクトで定義されているこのプロパティは、例10-11の行のXML要素名を<CustomersViewRow>から<Customer>に変更します。
ビュー・リンク・アクセッサ属性からのネストされた行セット・データをラップする要素の名前を変更するには、「ビュー・リンク・プロパティ」ダイアログを使用します。ダイアログを開くには、ビュー・リンクの概要エディタで、「関連」ページの「アクセッサ」セクションにある「アクセッサの編集」アイコンをクリックします。「アクセッサ名」フィールドに、ビュー・リンク・アクセッサ属性の適切な名前を入力します。
デフォルトでは、ビュー行属性がnullの場合、生成されるXMLから対応する要素が省略されます。概要エディタの「属性」ページで属性を選択し、「プロパティ」ウィンドウで「カスタム・プロパティ」ナビゲーション・タブを選択して、カスタム属性レベルのプロパティ「XML明示的なNull」に任意の値(たとえばtrueやyes)を設定すると、値がNULLの属性についても要素が生成されます。たとえば、AssignedDateという名前の属性にこのプロパティを設定すると、AssignedDateがnullの行には、対応する要素AssignedDate null="true"/が生成されます。この動作をビュー・オブジェクトのすべての属性に適用する場合は、属性ごとにプロパティを定義するかわりに、ビュー・オブジェクト・レベルでXml Explicit Nullカスタム・プロパティを定義できます。
writeXML()から返されるXML Nodeオブジェクトに関する最も一般的な処理は次の2つです。
ネットワーク経由での送信やファイルへの保存などのために、ノードをシリアライズされたテキスト表現に印刷
生成されたXMLをW3C XPath式を使用して検索
残念ながら、標準のW3C Document Object Model (DOM) APIには、これらの役に立つ操作を行うためのメソッドは含まれません。しかし大丈夫です。ADFビジネス・コンポーネントはDOMのOracle XMLパーサーの実装を使用しているので、writeXML()からのNode戻り値をOracle固有のクラスであるXMLNodeやXMLElement (oracle.xml.parser.v2パッケージ内)にキャストして、次のような便利な追加機能にアクセスできます。
print()メソッドを使用した、シリアライズされたテキスト形式へのXML要素の印刷
selectNodes()メソッドを使用した、メモリー内のXML要素のXPath式による検索
valueOf()メソッドを使用した、XML要素に関連するXPath式の値の検出
例10-12は、TestClientWriteXMLのprintXML()メソッドです。NodeパラメータをXMLNodeにキャストし、print()メソッドを呼び出してXMLをコンソールにダンプします。
生成されるXMLに含める属性をきめ細かく制御する必要がある場合は、HashMapを受け取るバージョンのwriteXML()メソッドを使用します。例10-13は、この手法を使用するTestClientWriteXMLクラスの行を示しています。HashMapを作成した後、String[]値のエントリを設定します。このエントリは、XMLに含める属性の名前であり、キーとしてはこれらの属性が属しているビュー定義の完全修飾名を使用します。例には、CustomersViewビュー・オブジェクトの属性Name、CityおよびOrdersViewと、OrdersViewビュー・オブジェクトの属性Id、DateOrderedおよびTotalが含まれます。
|
注意: 旧バージョンのADFビジネス・コンポーネントとの互換性のため、 |
特定のビュー・オブジェクト・インスタンスのビュー行に対しては、次のような処理が行われます。
そのビュー・オブジェクトの完全修飾ビュー定義名とキーが一致する項目が属性マップに存在する場合は、対応するString配列に名前のある属性のみがXMLに含められます。
さらに、文字列配列にビュー・リンク・アクセッサ属性の名前が含まれる場合は、そのディテール行セットのネストされた内容がXMLに含められます。ビュー・リンク・アクセッサ属性名が文字列配列に出現しない場合は、ディテール行セットの内容は含められません。
キーが一致する項目がマップ内に存在しない場合は、その行のすべての属性がXMLに含められます。
例10-13 生成される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));
例を実行すると、例10-14で示されるXMLが生成されます。これには、提供された属性マップで指定されている属性とビュー・リンク・アクセッサのみが含まれます。
例10-14 属性マップを使用して生成されたUsersビュー・オブジェクトからの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生成中の無限再帰を回避する方法はありません。
writeXML()によって生成される正規のXML書式が要件を満たさない場合は、オプションの引数として、XSLTスタイルシートを提供できます。通常と同じようにXMLが生成されますが、最終的なXMLが呼出し元に返される前に、指定したスタイルシートを使用して結果の変換が行われます。
例10-15に示すXSLTスタイルシートについて考えます。これは、例10-14で生成されるXMLのルート要素との一致を見つけて新しいCustomerNames要素を結果に作成する、テンプレートが1つのみの簡単な変換です。このテンプレートは、xsl:for-each命令を使用してすべてのCustomersView要素を処理します。対象であることが確認されたCustomersView要素ごとに、結果にCustomerNames要素を作成し、そのContact属性に、CustomersViewのName子要素の値を移入します。
例10-15 生成されたXMLを別の書式に変換するXSLTスタイルシート
<?xml version="1.0" encoding="windows-1252" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<CustomerNames>
<xsl:for-each
select="/CustomersView/CustomersViewRow)">
<xsl:sort select="Name"/>
<Customer Contact="{Name}"/>
</xsl:for-each>
</CustomerNames>
</xsl:template>
</xsl:stylesheet>
例10-16は、writeXML()を呼び出すとこのXSLTスタイルシートを実行するTestClientWriteXMLクラスの行です。
例10-16 結果のXMLを変換するためのwriteXML()へのXSLTスタイルシートの引渡し
// In TestClientWriteXML.java XSLStylesheet xsl = getXSLStylesheet(); printXML(vo.writeXML(XMLInterface.XML_OPT_ALL_ROWS,viewDefMap,xsl));
例10-16のコードを実行すると、次のような変換された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>
例10-17で示されているgetXSLStylesheet()ヘルパー・メソッドは、実行時にクラスパスからXSLTスタイルシートなどのリソースを読み込む方法を学習するためのよい例です。このコードでは、Example.xslスタイルシートがTestClientWriteXMLクラスと同じディレクトリにあるものと想定しています。.class演算子を使用してTestClientWriteXMLクラスのClassオブジェクトを参照することで、コードはgetResource()メソッドを使用してリソースに対するURLを取得します。その後、URLをXSLProcessorクラスのnewXSLStylesheet()メソッドに渡して、返すための新しいXSLStylesheetオブジェクトを作成します。このオブジェクトは、*.xslファイルから読み込まれたXSLTスタイルシートのコンパイル済バージョンを表しています。
例10-17 クラスパスからのリソースとしてのXSLTスタイルシートの読込み
private static XSLStylesheet getXSLStylesheet()
throws XMLParseException, SAXException,IOException,XSLException {
String xslurl = "Example.xsl";
URL xslURL = TestClientWriteXML.class.getResource(xslurl);
XSLProcessor xslProc = new XSLProcessor();
return xslProc.newXSLStylesheet(xslURL);
}
|
注意: XSLTスタイルシートのように、コンパイル済のJavaクラスやXMLメタデータとともに出力ディレクトリに格納するリソースを扱うときは、「プロジェクト・プロパティ」ダイアログの「コンパイラ」ページで、「ファイル・タイプの出力ディレクトリへのコピー」フィールドを更新し、セミコロンで区切られたリストに |
ビュー・オブジェクトがXMLドキュメントを使用して挿入、更新、および削除を行うようにするには、readXML()メソッドを使用します。
void readXML(Element elem, int depthcount)
readXML()が受け入れる正規の書式は、同じビュー・オブジェクトでwriteXML()メソッドを呼び出すと生成されるものと同じです。処理するXMLドキュメントが正規の書式に対応していない場合は、readXML()に対するオプションの第3引数としてXSLTスタイルシートを提供し、ファイルを読み込んで処理する前に、入力XMLドキュメントを正規の書式に変換できます。
ビュー・オブジェクトは、正規の書式のXMLドキュメントを使用するとき、ドキュメントを処理して、行要素、その属性要素の子、およびビュー・リンク・アクセッサ属性を表すネストされた要素を認識します。depthcountパラメータで指定されている最大レベルまで、再帰的にドキュメントを処理します。XMLドキュメントの全レベルを処理するよう要求するには、depthcountに-1を渡します。
認識する行要素ごとに、readXML()メソッドは次の処理を行います。
行を処理するために関連するビュー・オブジェクトを識別します。
子属性要素を読み取り、行の主キー属性の値を取得します。
主キー属性を使用してfindByKey()を実行し、行がすでに存在するかどうかを判定します。
行が存在する場合は、次の処理を行います。
行要素にマーカー属性bc4j-action="remove"が含まれる場合は、既存の行を削除します。
それ以外の場合は、XMLの現在の行要素の、属性要素の子の値を使用して、行の属性を更新します。
行が存在しない場合は、新しい行を作成し、ビュー・オブジェクトの行セットに挿入します。その属性は、XMLの現在の行要素の、属性要素の子の値を使用して移入します。
Rowオブジェクトに対しても、同じreadXML()メソッドを使用できます。このメソッドが受け入れる正規のXMLの書式は、同じ行に対してwriteXML()を呼び出すと生成される書式と同じです。行でreadXML()メソッドを呼び出し、次の処理を行うことができます。
属性の値をXMLで更新します。
対応する行要素にbc4j-action="remove"マーカー属性がある場合は、行を削除します。
ビュー・リンク・アクセッサを介して、ネストされた行を挿入、更新、または削除します。
例10-18に示すXMLドキュメントについて考えます。これは、CustomersViewビュー・オブジェクトの単一行で期待される正規の書式です。ルートのCustomersViewRow要素の内部にネストされているCity属性は、顧客の住む市を表します。ネストされたOrdersView要素はOrdersViewビュー・リンク・アクセッサ属性に対応し、3つのOrdersViewRow要素を含みます。それぞれが、OrdersView行の主キーを表すId要素を含みます。
例10-18 行を挿入、更新、および削除するための正規の書式のXMLドキュメント
<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>
例10-19は、TestClientReadXMLクラスのコード行を示しています。このクラスは、CustomersViewビュー・オブジェクトの特定の行に、このXMLデータグラムを適用します。TestClientReadXMLクラスが実行する基本的な手順は次のとおりです。
キーにより、ターゲット行を検索します(たとえば、顧客であるThe Sports Emporium)。
変更を適用する前に、行に対して生成されたXMLを表示します。
ヘルパー・メソッドを使用して適用する変更を含む解析されたXMLドキュメントを取得します。
行に変更を適用するためにXMLドキュメントを読み込みます。
適用された保留中変更を含むXMLを表示します。
TestClientReadXMLクラスは、10.4.1項「問合せ済データのXMLの生成方法」で説明されているXMLInterface.XML_OPT_ASSOC_CONSISTENTフラグを使用して、ポストされていない新しい行をXMLに含めています。
例10-19 readXML()による既存行への変更の適用
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));
例10-19のコードを実行すると、最初に、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>
|
注意: この例では、 |
デフォルトのビュー・オブジェクトは、データベースからデータを読み取り、データベースの結果セットを処理するためにJava Database Connectivity (JDBC)レイヤーを使用するタスクを自動化します。これに対し、カスタムJavaクラスで適切なメソッドをオーバーライドすることにより、REF CURSOR、メモリー内の配列、Javaの*.propertiesファイルなどの様々な代替データソースからプログラムでデータを取得するビュー・オブジェクトを作成できます。
プログラムで読取り専用のビュー・オブジェクトを作成するには、ビュー・オブジェクト作成ウィザードを使用します。
プログラムで読取り専用のビュー・オブジェクトを作成するには:
「アプリケーション」ウィンドウで、ビュー・オブジェクトを作成するプロジェクトを右クリックし、「新規」を選択します。
「新規ギャラリ」で「ビジネス層」を展開し、「ADFビジネス・コンポーネント」、「ビュー・オブジェクト」を選択して、「OK」をクリックします。
ビュー・オブジェクト作成ウィザードの「名前」ページで、ビュー・オブジェクトの名前とパッケージを入力します。データ・ソースの場合、「問合せベースではなく、プログラムによって移入された行」を選択します。
「属性」ページで、「新規」を複数回クリックし、プログラムによるビュー・オブジェクトが必要とするビュー・オブジェクト属性を定義します。
「属性の設定」ページで、定義した属性に必要な設定を調節します。
「Java」ページで「ビュー・オブジェクト・クラスの生成」を選択して、カスタム・ビュー・オブジェクト・クラス(ViewObjImpl)にコードを含められるようにします。
「終了」をクリックして、ビュー・オブジェクトを作成します。
ビュー・オブジェクトのカスタムJavaクラスで、10.5.3項「プログラムのビュー・オブジェクト用にオーバーライドする主要なフレームワーク・メソッド」で説明されているメソッドをオーバーライドし、独自のデータ取得方法を実装します。
プログラムでデータを取得するエンティティ・ベースのビュー・オブジェクトを作成するには、通常の方法でビュー・オブジェクトを作成し、カスタムJavaクラスを有効にし、次の項で説明されているメソッドをオーバーライドして独自のデータ取得方法を実装します。
プログラムによるビュー・オブジェクトでは、通常、ViewObjectImplベース・クラスの次のメソッドをすべてオーバーライドして、データ取得の独自方法を実装します。
create()
このメソッドは、ビュー・オブジェクトのインスタンスが作成されるときに呼び出され、プログラムによるビュー・オブジェクトが必要とする任意の状態を初期化するために使用できます。このオーバーライドされるメソッドには、少なくとも次の行を組み込んで、プログラムによるビュー・オブジェクトが関係するSQL問合せをトレースしないようにします。
// Wipe out all traces of a query for this VO getViewDef().setQuery(null); getViewDef().setSelectClause(null); setQuery(null);
executeQueryForCollection()
このメソッドは、ビュー・オブジェクトの問合せを実行する(または再実行する)必要があるたびにコールされます。実装では、このメソッドをオーバーライドせず、問合せを完全に変更するか、パラメータ・リストを変更する必要があります。そうすること。実装のベスト・プラクティスの詳細は、10.5.4.2項「executeQueryForCollection()メソッドのオーバーライド」を参照してください。
hasNextForCollection()
このメソッドは、このビュー・オブジェクトから作成される行セットの行セット・イテレータでhasNext()メソッドをサポートするために呼び出されます。実装では、プログラムのデータ・ソースから取得する行がまだある場合はtrueを返します。
createRowFromResultSet()
このメソッドは、フェッチされたデータの各行を移入するために呼び出されます。実装では、createNewRowForCollection()を呼び出して新しい空白の行を作成した後、populateAttributeForRow()を呼び出して行に対するデータの各属性を移入します。
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()
コードでは、各行セットでユーザー・データ・コンテキスト・オブジェクトを格納および取得できます。このメソッドを呼び出すと、閉じられた行セットと関連付けられているリソースを解放できます。
ビュー・オブジェクト・コンポーネントは、実行時に複数のアクティブな行セットと関連付けられる可能性があるため、前記のフレームワーク・メソッドの多くは、qcという名前のObjectパラメータを受け取ります。このパラメータで、フレームワークは、コードが設定していると予想される行のコレクションと、特定のコレクションに移入される行に影響する可能性のあるバインド変数値の配列を渡します。
行のコレクションごとにユーザー・データ・オブジェクトを格納できるので、カスタム・データ・ソースの実装は、必要なデータ・ソース・コンテキスト情報を関連付けることができます。フレームワークでは、このコレクションごとのコンテキスト情報を取得および設定するために、setUserDataForCollection()メソッドとgetUserDataForCollection()メソッドが提供されています。オーバーライドされたフレームワーク・メソッドのいずれかが呼び出されるたびに、getUserDataForCollection()メソッドを使用して、フレームワークが移入を望んでいる行のコレクションと関連付けられた正しいResultSetオブジェクトを取得できます。
以降の項の各例では、これらのメソッドをオーバーライドして、異なる種類のプログラムによるビュー・オブジェクトを実装しています。
アプリケーションでは、ストアド・プロシージャ内にカプセル化された問合せの結果を使用することが必要になる場合があります。PL/SQLを使用すると、カーソルを開いて問合せの結果を反復処理し、このカーソルへの参照をクライアントに返すことができます。このいわゆるREF CURSORは、クライアントが問合せの結果を反復処理するために使用できるハンドルです。これは、クライアントが元のSQL SELECT文を実際に発行していない場合であっても可能です。
|
注意: この項の例では、 |
REF CURSORを返すファンクションを含むPL/SQLパッケージの宣言は簡単です。例10-20に、パッケージの例を示します。
例10-20 REF_CURSORを返す関数
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()メソッドは、このビュー・オブジェクトに対するSQL問合せのすべてのトレースを削除します。例10-21に、この目的でcreate()メソッドをオーバーライドする方法を示します。
executeQueryForCollection()メソッドは、このビュー・オブジェクトに基づいて、フレームワークが問合せコレクションのデータベース問合せを発行する必要があるときに実行されます。1つのビュー・オブジェクトは、多数の関連結果セットを生成でき、それぞれが異なるバインド変数値の結果である可能性があります。問合せの行セットがフレームワーク調整したマスター/ディテール・ビュー・リンクと関連性がある場合、params配列には、ソース・ビュー・オブジェクトのバインド・パラメータのフレームワークが提供した1つ以上の名前と値のペアが含まれます。ユーザーが提供したバインド・パラメータ値がある場合、これはparams配列においてフレームワークが提供したバインド変数値よりも優先され、ユーザー・パラメータの数はnumUserParams引数の値によって示されます。
このメソッドは、ヘルパー・メソッドretrieveRefCursor()を呼び出して、ストアド・ファンクションを実行してREF CURSOR戻り値を返し、JDBC ResultSetとしてキャストします。例10-22に、この目的でexecuteQueryForCollection()メソッドをオーバーライドする方法を示します。
例10-22 ストアド・ファンクションの実行
protected void executeQueryForCollection(Object qc,Object[] params,
int numUserParams) {
storeNewResultSet(qc,retrieveRefCursor(qc,params));
super.executeQueryForCollection(qc, params, numUserParams);
}
次に、ヘルパー・メソッドstoreNewResultSet()を呼び出します。このヘルパー・メソッドは、setUserDataForCollection()メソッドを使用して、フレームワークが問合せの実行を要求している行のコレクションとともにこのResultSetを格納します。例10-23に、この目的でstoreNewResultSet()メソッドを定義する方法を示します。
例10-23 結果セットの格納
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()メソッドは、16.5項「ストアド・プロシージャとストアド・ファンクションの呼出し」で説明されているヘルパー・メソッドを使用して、ストアド・ファクションを呼び出し、REF CURSORを返します。例10-24に、この目的でretrieveRefCursor()メソッドを定義する方法を示します。
フレームワークは、データ・ソースからフェッチする必要のある行ごとに、オーバーライドされたcreateRowFromResultSet()メソッドを呼び出します。この実装では、コレクション固有のResultSetオブジェクトがユーザー・データ・コンテキストから取得されます。これは、getResultSet()メソッド使用して結果セット・ラッパーを問合せコレクション・ユーザー・データから取得し、createNewRowForCollection()メソッドを使用して新しい空白の行をコレクション内に作成した後、populateAttributeForRow()メソッドを使用して、ビュー・オブジェクトの概要エディタで設計時に定義された各属性に属性値を移入します。例10-25に、この目的でcreateRowFromResultSet()メソッドをオーバーライドする方法を示します。
例10-25 結果セットからの行の作成
protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet rs) {
/*
* We ignore the JDBC ResultSet passed by the framework (null anyway) and
* use the resultset that we've stored in the query-collection-private
* user data storage
*/
rs = getResultSet(qc);
/*
* Create a new row to populate
*/
ViewRowImpl r = createNewRowForCollection(qc);
try {
/*
* Populate new row by attribute slot number for current row in Result Set
*/
populateAttributeForRow(r,0, rs.getLong(1));
populateAttributeForRow(r,1, rs.getString(2));
populateAttributeForRow(r,2, rs.getString(3));
}
catch (SQLException s) {
throw new JboException(s);
}
return r;
}
フレームワーク・メソッドhasNextForCollection()のオーバーライドされた実装は、フェッチする行がまだあるかどうかに基づいて、trueまたはfalseを返します。最後に達したら、setFetchCompleteForCollection()を呼び出して、このコレクションは移入が終了したことをビュー・オブジェクトに伝えます。例10-26に、この目的でhasNextForCollection()メソッドをオーバーライドする方法を示します。
例10-26 さらにフェッチする行があるかのテスト
protected boolean hasNextForCollection(Object qc) {
ResultSet rs = getResultSet(qc);
boolean nextOne = false;
try {
nextOne = rs.next();
/*
* When were at the end of the result set, mark the query collection
* as "FetchComplete".
*/
if (!nextOne) {
setFetchCompleteForCollection(qc, true);
/*
* Close the result set, we're done with it
*/
rs.close();
}
}
catch (SQLException s) {
throw new JboException(s);
}
return nextOne;
}
コレクションのフェッチ処理が終了すると、オーバーライドされたreleaseUserDataForCollection()メソッドが呼び出されて、データベース・カーソルが開いたままにならないように確実にResultSetを閉じます。例10-27に、この目的でreleaseUserDataForCollection()メソッドをオーバーライドする方法を示します。
最後に、ビュー・オブジェクトのgetEstimatedRowCount()メソッドを正しくサポートするため、オーバーライドされたgetQueryHitCount()メソッドは、すべての行が行セットからフェッチされた場合に取得された行のカウントを返します。ここでは、コードでストアド・ファンクションを使用してこの作業を完了しています。問合せはストアド・ファンクションAPIの背後に完全にカプセル化されているため、コードもカウント・ロジックの実装およびこの機能のサポートをPL/SQLパッケージに依存しています。例10-28に、この目的でgetQueryHitCount()メソッドをオーバーライドする方法を示します。
デフォルトでは、複数のエンティティ・オブジェクト慣用名を持つビュー・オブジェクトを作成するとき、概要エディタでビュー・オブジェクトに追加する2次エンティティ・オブジェクト慣用名それぞれが次の設定で構成されます。
「更新可能」チェック・ボックスを選択解除
「参照」チェック・ボックスを選択
ビュー・オブジェクト概要エディタの「エンティティ・オブジェクト」ページの「選択済」リストで「使用方法」を選択して、「更新可能」チェック・ボックスを選択し、デフォルトの動作を、2次エンティティ・オブジェクトの慣用名を更新可能にできます。
また、各セカンダリ・エンティティ・オブジェクト慣用名に関して、「参照」を選択したままにして、エンティティ参照情報が変更されたときにセカンダリ・エンティティの属性をリフレッシュするかどうかを決定できます。デフォルトでは、「参照」が選択されていて、各2次エンティティ・オブジェクトの属性がリフレッシュされます。複数のエンティティ・オブジェクト慣用名を使用する行の挿入を許可する場合のこの設定の詳細は、10.6.2項「実行時の処理: ビュー行の作成」を参照してください。
表10-4に、ビュー・オブジェクトにセカンダリ・エンティティ・オブジェクト慣用名を定義する際に選択できる組合せをまとめます。
表10-4 ビュー行作成動作を制御するビュー・オブジェクト・プロパティ
| 更新可能 | 参照 | ビュー行の動作 |
|---|---|---|
|
|
true |
この組合せでは、エンティティ・オブジェクト慣用名の属性を更新し、その属性の主キーの値との同期を保ちます。この組合せは、ビュー・リンク一貫性機能での動作が良好であるため、これを使用して、ビュー・オブジェクトにある、挿入を行うエンティティ・オブジェクト慣用名を1つにできます。 |
|
|
false |
この組合せでは、エンティティ・オブジェクト慣用名の属性を更新しますが、その属性は主キー参照によって変更されません。これはかなり稀な組合せで、ビュー・オブジェクトを既存のデータの更新または削除のみに使用する状況に最適です。この組合せでは、ユーザーは非参照の更新可能なエンティティ・オブジェクト慣用名に関連する属性を更新でき、ビュー行は適切な基礎のエンティティ行に変更を委譲します。 注意: ビュー・リンク一貫性機能で、ビュー・オブジェクトの一部のセカンダリ・エンティティ・オブジェクト慣用名が |
|
false |
true |
これは、5.6.1項「エンティティ・ベースのビュー・オブジェクトに対する結合の作成方法」に記載されているデフォルトの動作です。この組合せは、エンティティ・オブジェクト慣用名を更新しないことを想定しています。 |
新しい行の作成をサポートするための複数の更新可能エンティティ(Updatable=true、Reference=false)と、コンポジットでないエンティティ・オブジェクト間のアソシエーションを持つビュー・オブジェクトが必要な場合は、10.6.1項「複数の更新可能エンティティ・オブジェクト慣用名を持つ新規行をプログラムで作成する方法」の説明に従って、コードを少し記述する必要があります。
新しい行の作成をサポートするための複数の更新可能エンティティ(Updatable=true、Reference=false)と、コンポジットでないエンティティ・オブジェクト間のアソシエーションを持つビュー・オブジェクトが必要な場合は、ビュー・オブジェクトのカスタム・ビュー行クラスのcreate()メソッドをオーバーライドしてこれを有効にし、正しく動作するようにする必要があります。
|
注意: 作成する必要があるのは、更新可能なエンティティ間のアソシエーションがコンポジットでない場合に新しい行の作成を処理するコードのみです。アソシエーションがコンポジットである場合は、ADFビジネス・コンポーネントにより自動的に処理されます。 |
複数の更新可能エンティティを持つビュー・オブジェクトでcreateRow()を呼び出すと、更新可能なエンティティ・オブジェクトの慣用名ごとに新しいエンティティ行の部分が作成されます。このシナリオの複数エンティティはアソシエーションによって関連付けられているので、新しい関連のあるエンティティ行を正常に保存できるようにするには、3つのコードを実装する必要があります。
正しいポスト順序を制御するために含まれるエンティティ・オブジェクトで、postChanges()メソッドをオーバーライドすることが必要な場合があります。
関連付けられたエンティティの主キーがDBSequenceを使用してデータベース順序によって移入され、複数のエンティティ・オブジェクトが関連付けられてはいても、コンポジットではない場合は、postChanges()メソッドとrefreshFKInNewContainees()メソッドをオーバーライドし、更新された主キー値を、一時的な値を参照していた関連行にカスケードする必要があります。
ビュー・オブジェクトのカスタム・ビュー行クラスのcreate()メソッドをオーバーライドし、親エンティティ・オブジェクトのコンテキストを新しく作成される子エンティティに渡すよう、デフォルトの行作成動作を変更する必要があります。
手順1と2のコードを理解するために、4.14.7項「制約違反を防ぐためのエンティティ・ポスト順序を制御する方法」に記載されている、関連エンティティ・オブジェクトの例を参照してください。最後に理解する必要があることは、ビュー行のcreate()メソッドをオーバーライドする方法です。プライマリ・エンティティ・オブジェクト慣用名がProductで、セカンダリ・エンティティ・オブジェクト慣用名がInventoryである、ProductInventoryVOビュー・オブジェクトについて考えます。エンティティ・オブジェクトの慣用名Productは、更新可能で非参照としてマークされ、エンティティ・オブジェクトの慣用名Inventoryは、参照エンティティ・オブジェクト慣用名であるものとします。
|
注意: この項の例では、 |
例10-29は、ビュー行作成操作の間に、複数の更新可能エンティティ行部分を正しい順序で作成するために必要なコメント付きのコードを示しています。
例10-29 複数の更新可能エンティティの場合のビュー行create()メソッドのオーバーライド
/**
* 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の両方が格納されます。
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パラメータをリクエストできます。
ビュー定義を作成した後、この定義に基づいてビュー・オブジェクト・インスタンスを作成するには、次の基本手順に従います(例10-30を参照)。
ビュー定義オブジェクトを作成して、完全な名前を設定します。
ビュー・オブジェクトSQL文を定義します。
ビュー定義を解決して、MDSリポジトリに保存します。
ビュー定義を使用して、ビュー定義に基づくビュー・オブジェクトのインスタンスを作成します。
|
注意: ビュー定義をMDSリポジトリに保存するには、保存されるビュー定義に対して |
例10-30 ViewDefImpl APIを使用したビュー定義の作成
/*
* 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 either using a fully-
* specified "expert-mode" SQL query.
*/
v.setQuery("select e.empno,e.ename,e.sal,e.deptno,d.dname,d.loc,"+
"d.deptno,trunc(sysdate)+1 tomorrow_date, "+
"e.sal + nvl(e.comm,0) total_compensation, "+
"to_char(e.hiredate,'dd-mon-yyyy') formated_hiredate"+
" from emp e, dept d "+
" where e.deptno = d.deptno (+)"+
" order by e.ename");
v.setFullSql(true);
/*
* Or, you can construct the SQL statement in parts like this.
*/
v.setSelectClause("e.empno,e.ename,e.sal,e.deptno,d.dname,d.loc,"+
"d.deptno,trunc(sysdate)+1 tomorrows_date,"+
"e.sal + nvl(e.comm,0) total_compensation, "+
"to_char(e.hiredate,'dd-mon-yyyy') formated_hiredate");
v.setFromClause("emp e, dept d");
v.setWhereClause("e.deptno = d.deptno (+)"); v.setOrderByClause("e.ename");
/*
* 3. Then resolve and 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リポジトリに保存することが重要です。正しく保存しないと、リクエストがクラスタ内の別のノードにリダイレクトされた場合に、他のノードから定義をロードしてアクセスできないため、問題が発生することがあります。
定義を保存するには、adf-config.xmlのmds-config要素を定義する必要があります。たとえば、adf-config.xmlファイルには例10-31に示すような定義を含める必要があります。
例10-31 ビュー定義を保存するためのMDS構成でのネームスペース定義
<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構成エントリの詳細は、A.9項「adfc-config.xml」を参照してください。MDSリポジトリの構成の詳細は、Oracle Fusion Middleware管理者ガイドの「メタデータ・リポジトリの管理」の章を参照してください。
10.7項「プログラムによるビュー定義とビュー・オブジェクトの作成」で説明しているように、実行時のビュー・オブジェクト作成に関連するオーバーヘッドを理解することは重要です。ビジネス上やむをえない必要性がある場合以外は、実行しないでください。たとえば、アプリケーションで発行する問合せで、対象の表の名前が設計時にわかっていて、取得する列のリストも固定している場合は、設計時にビュー・オブジェクトを作成します。このようにすると、SQL文は適切にカプセル化され、開発中に簡単に説明およびチューニングでき、結果の行の構造とデータ型を検出するために実行時にオーバーヘッドをかけることがなくなります。
これに対し、実行時にApplicationModuleインタフェースでcreateViewObjectFromQueryStmt() APIを使用すると、問合せはコードに埋もれてしまい、前もってSQLをチューニングすることが困難になり、ビュー・オブジェクトを作成するたびにパフォーマンスが犠牲になります。理論上、動的に作成されるビュー・オブジェクトのSQL問合せ文は、このAPIを使用して新しいインスタンスが作成されるたびに異なるので、実行中に問合せ結果の形状を検出するために、余分なデータベース・ラウンドトリップが必要になります。問い合せる表の名前が実行時までわからない場合にのみ、問合せを動的に作成します。それ以外のほとんどの要件には、設計時に作成するビュー・オブジェクトと、固定のWHERE句にバインド変数を設定する、または実行時にWHERE句(オプションのバインド変数を含む)を追加するランタイムAPIを組み合せることで対応できます。
Oracle Formsなどの一部の4GLツールでは、特定のデータ・コレクションが挿入、更新または削除を許可するかどうかを制御する宣言的プロパティが提供されています。現在のリリースでは、まだビュー・オブジェクトはこれを組込み機能としてサポートしていませんが、ビュー・オブジェクトでの挿入、更新または削除を制御するための開発者提供のフラグとしてカスタム・メタデータ・プロパティを利用するフレームワーク拡張クラスを使用すれば、この機能を簡単に追加できます。
|
注意: この項の例では、 |
開発者が個別のビュー・オブジェクト・インスタンスを制御できるようにするには、ビュー・オブジェクト・インスタンスと同じ名前でアプリケーション・モジュール・カスタム・プロパティを使用するという規則を導入します。たとえば、アプリケーション・モジュールに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;
}
例10-32は、ビュー行が実装を完了するために、カスタム・フレームワーク拡張クラスで必要な他のコードを示しています。これは、次のメソッドをオーバーライドします。
isAttributeUpdateable()
挿入が許可されていない場合に新しい行のフィールドを無効にするため、または更新が許可されていない場合に既存の行のフィールドを無効にするために、ユーザー・インタフェースを有効化します。
setAttributeInternal()
挿入が許可されていない場合に新しい行の属性値が設定されないようにします。また、更新が許可されていない場合に既存の行の属性が設定されないようにします。
remove()
削除が許可されない場合に削除を禁止します。
create()
挿入が許可されない場合に作成を禁止します。
例10-32 カスタム・プロパティに基づく挿入、更新、または削除の禁止
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
}