| Oracle® Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド 11g リリース2(11.1.2.3.0) B69399-02 |
|
![]() 前 |
![]() 次 |
この章では、最も一般的なビジネス・ルールの実装に関する、主要なエンティティ・オブジェクトのイベントと機能について説明します。
この章の内容は次のとおりです。
内蔵された宣言型検証機能を補完するため、エンティティ・オブジェクトとビュー・オブジェクトにはJavaコードを使用して、カプセル化されたビジネス・ロジックをプログラミングによって実装できるメソッド・バリデータといくつかのイベントがあります。これらの概念を、図8-1に示します。
属性レベルのMethod Validatorは、属性値が変更されると、検証コードをトリガーします。
エンティティ・レベルのMethod Validatorは、エンティティ行が検証されると、検証コードをトリガーします。
あるエンティティに対して、次の主要メソッドをカスタムJavaクラスでオーバーライドできます。
create(): 行の作成時にデフォルト値を割り当てます。
initDefaultExpressionAttributes(): 行の作成時、または新規行のリフレッシュ時にデフォルト値を割り当てます。
remove(): 条件付きで削除を制限します。
isAttributeUpdateable(): 属性を条件付きで更新可能にします。
setAttribute(): 属性レベルのMethod Validatorをトリガーします。
validateEntity(): エンティティ・レベルのMethod Validatorをトリガーします。
prepareForDML(): エンティティ行が保存される前に属性値を割り当てます。
beforeCommit(): 指定された型のすべてのエンティティ行に関連する規則を実行します。
afterCommit(): エンティティ・オブジェクトの状態の変更に関する通知を送信します。
ほとんどの検証は基本的な宣言的操作で実装できますが、メソッド・バリデータを使用してカスタム検証コードを起動することにより、必要に応じてビジネス・ドメイン・レイヤーにより複雑なビジネス・ルールを実装できます。
プログラム的なビジネス・ルールを使用する場合としては、次の例があります。
データベース順序からの属性値のデフォルト設定
複雑な計算から導出された値の割当て
エンティティ・オブジェクトに対する保留中の変更の取消し
現在のユーザー・セッションに関する情報のアクセスと格納
属性の条件付き更新可能性の判断
プログラム的な検証を使用する前に、他のADF機能を理解しておくと役立つ場合があります。次に、関連する他の機能へのリンクを示します。
ビジネス・ルールをプログラム的に実装する前に、宣言的検証でアプリケーションのニーズに対応できるかどうかを確認する必要があります。詳細は、第7章「検証とビジネス・ルールの宣言的な定義」を参照してください。
リソース・バンドルを使用して、ローカライズ可能な検証エラー・メッセージを用意できます。4.7項「リソース・バンドルの使用」を参照してください。
4.10.9項「トリガーが割り当てられた値との同期方法」で、主キーの属性にDBSequence型を使用する方法を説明しています。この種の属性の値は、コミット時にデータベース・シーケンスによって取り込む必要があります。
認証されたユーザーに関する情報へのアクセス方法など、Oracle Fusion Webアプリケーションのセキュリティ機能の詳細は、第35章「Fusion WebアプリケーションでのADFセキュリティの有効化」を参照してください。
エンティティ・オブジェクトのビジネス・ロジックでのGroovyスクリプトの使用方法の詳細は、3.6項「Groovyスクリプト言語サポートの概要」を参照してください。
Method Validatorは、独自のJavaコードを使用した宣言的な検証規則とGroovyスクリプト式を補完する主要な手段です。Method Validatorでは、独自の検証メソッド内に記述したJavaコードが、エンティティ・オブジェクト検証サイクルにおいて適切なタイミングでトリガーされます。Method Validatorにより、属性またはエンティティ全体で、様々な型の検証をコーディングできます。
それぞれがコード内の異なるメソッド名をトリガーしていれば、任意の数の属性レベルまたはエンティティレベルのメソッドを追加できます。すべての検証メソッド名はvalidateというワードで開始する必要があります。ただし、この規則にさえ従っていれば、機能を明確に示す任意の方法で名前を付けることができます。属性レベルのバリデータでは、メソッドはエンティティ属性と同じ型の引数を1つだけ取ります。エンティティレベルのバリデータでは、メソッドは引数を取りません。メソッドはパブリックで、ブール値を返す必要があります。メソッドからfalseが返されると、検証は失敗します。
|
注意: これらの規則を知っておくことは必要ですが、JDeveloperを使用してMethod Validatorを作成する場合、そのクラスの正しいインタフェースが作成されます。 |
実行時には、Method Validatorにより、エンティティ属性がエンティティ・オブジェクト・クラスで実装されているメソッドに渡されます。
例8-1では、メソッドは大文字で始まり、NULL値で例外をスローする文字列、空の文字列、および大文字で始まらない文字列を受け取ります。
例8-1 先頭が大文字の場合に検証を行うメソッド
public boolean validateIsCapped(String text)
{
if (text != null &&
text.length() != 0 &&
text[0] >= 'A' &&
text[0] <= 'Z')
{
return true;
}
}
メソッド・バリデータの使用は、宣言的検証規則をプログラム的に補完する手段です。
作業を始める前に、次のようにします。
メソッド・バリデータに関する知識があると役立つ場合があります。詳細は、8.2項「Method Validatorの使用」を参照してください。
また、他の検証機能を使用して追加できる機能についても理解しておくと役立ちます。詳細は、8.1.2項「プログラム的なビジネス・ルールの追加機能」を参照してください。
属性レベルのMethod Validatorを作成する手順:
アプリケーション・ナビゲータで、目的のエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックします。
Javaページには、そのエンティティ・オブジェクトに対して現在有効なJava生成オブジェクトが表示されます。エンティティ・オブジェクトにカスタム・エンティティ・オブジェクト・クラスがない場合は、Methodバリデータの追加前に生成しておく必要があります。カスタムJavaクラスを生成するには、「編集」アイコンをクリックし、次に「エンティティ・オブジェクト・クラスの生成」を選択し、「OK」をクリックして、*.javaファイルを生成します。
「ビジネス・ルール」ナビゲーション・タブをクリックし、「属性」ノードを展開して、検証する属性を選択します。
「新規」アイコンをクリックし、検証規則を追加します。
「ルール・タイプ」ドロップダウン・リストから、「メソッド」を選択します。
「検証ルールの追加」ダイアログに、属性レベルの検証メソッドで期待されるメソッド・シグネチャが表示されます。次のいずれかを選択できます。
エンティティ・オブジェクトのカスタムJavaクラスに、適切なシグネチャのあるメソッドがすでに存在する場合、リストに表示されます。これは「メソッドの作成と選択」チェック・ボックスの選択を解除した後に選択できます。
「メソッドの作成と選択」チェック・ボックスを選択したままにすると(図8-2を参照)、validateから始まるどのようなメソッド名でも「メソッド名」ボックスに入力できます。「OK」をクリックすると、適切なシグネチャとともに、メソッドがエンティティ・オブジェクトのカスタムJavaクラスに追加されます。
最後に、この検証規則が失敗した場合にエンド・ユーザーに表示される、デフォルト・ロケールのエラー・メッセージのテキストを追加します。
Method Validatorを新たに追加すると、JDeveloperでは新しい検証規則を反映するように、XMLコンポーネント定義が更新されます。メソッドの作成を指定した場合、そのメソッドはエンティティ・オブジェクトのカスタムJavaクラスに追加されます。例8-2は、オーダーのOrderShippedDateが現在月の日付であることを確認する、簡単な属性レベルの検証規則を示しています。メソッドには、対応する属性と同じ型の引数が与えられ、その条件付きのロジックはこの入力パラメータの値に基づいています。属性バリデータの起動時、属性値は対象となる新しい新しい値にはまだ設定されていません。したがって、getOrderShippedDate()メソッドをOrderShippedDate属性に対する属性バリデータ内でコールしても、クライアントが設定しようとしている候補値ではなく、属性の現在の値が返されます。
例8-2 簡単な属性レベルのMethod Validator
public boolean validateOrderShippedDate(Date data) {
if (data != null && data.compareTo(getFirstDayOfCurrentMonth()) <= 0) {
return false;
}
return true;
}
|
注意:
|
エンティティ・レベルのメソッド・バリデータは、有効範囲がより広く、単一の属性ではなくエンティティ全体が範囲になる点を除けば、属性レベルのメソッド・バリデータと似ています。
作業を始める前に、次のようにします。
メソッド・バリデータに関する知識があると役立つ場合があります。詳細は、8.2項「Method Validatorの使用」を参照してください。
また、他の検証機能を使用して追加できる機能についても理解しておくと役立ちます。詳細は、8.1.2項「プログラム的なビジネス・ルールの追加機能」を参照してください。
エンティティ・レベルのMethod Validatorを作成する手順:
アプリケーション・ナビゲータで、目的のエンティティ・オブジェクトをダブルクリックします。
概要エディタで、「Java」ナビゲーション・タブをクリックします。
Javaページには、そのエンティティ・オブジェクトに対して現在有効なJava生成オブジェクトが表示されます。エンティティ・オブジェクトにカスタム・エンティティ・オブジェクト・クラスがない場合は、Methodバリデータの追加前に生成しておく必要があります。カスタムJavaクラスを生成するには、「編集」アイコンをクリックし、次に「エンティティ・オブジェクト・クラスの生成」を選択し、「OK」をクリックして、*.javaファイルを生成します。
「ビジネス・ルール」ナビゲーション・タブをクリックし、「エンティティ」ノードを選択します。
「新規」アイコンをクリックし、検証規則を追加します。
「ルール・タイプ」ドロップダウン・リストから、「メソッド」を選択します。
「検証ルールの追加」ダイアログに、エンティティレベルの検証メソッドで期待されるメソッド・シグネチャが表示されます。次のいずれかを選択できます。
エンティティ・オブジェクトのカスタムJavaクラスに、適切なシグネチャのあるメソッドがすでに存在する場合、リストに表示されます。これは「メソッドの作成と選択」チェック・ボックスの選択を解除した後に選択できます。
「メソッドの作成と選択」チェック・ボックスを選択したままにすると(図8-3を参照)、validateから始まるどのようなメソッド名でも「メソッド名」ボックスに入力できます。「OK」をクリックすると、適切なシグネチャとともに、メソッドがエンティティ・オブジェクトのカスタムJavaクラスに追加されます。
最後に、この検証規則が失敗した場合にエンド・ユーザーに表示される、デフォルト・ロケールのエラー・メッセージのテキストを追加します。
Method Validatorを新たに追加すると、JDeveloperでは新しい検証規則を反映するように、XMLコンポーネント定義が更新されます。メソッドの作成を指定した場合、そのメソッドはエンティティ・オブジェクトのカスタムJavaクラスに追加されます。例8-3は、オーダーのOrderShippedDateがOrderDateより後の日付であることを確認する、簡単なエンティティ・レベル検証規則を示しています。
エンティティ・オブジェクト属性のロケール固有のUIコントロールのヒントと同様に、検証規則のエラー・メッセージは、エンティティ・オブジェクトのコンポーネント・メッセージ・バンドル・ファイルに追加されます。メッセージ・バンドル内のこれらのエンティティは、アプリケーションのデフォルトのロケールの文字列を表します。検証エラー・メッセージの翻訳バージョンについては、4.7項「リソース・バンドルの使用」で説明したUIコントロール・ヒントの翻訳と同様の手順で追加してください。
宣言的なデフォルト設定では不十分なとき、次の場合にエンティティ・オブジェクトでプログラムによるデフォルト設定を実行できます。
エンティティ行が最初に作成される場合
エンティティ行が最初に作成される場合、またはリフレッシュされてNULL値に設定される場合
エンティティ行がデータベースに保存される場合
エンティティ属性値が設定される場合
create()メソッドは、エンティティ行の最初の作成時にデフォルト値の初期化処理を可能にするエンティティ・オブジェクト・イベントを提供します。例8-4は、Fusion Order DemoのStoreFrontモジュールにあるOrderEOエンティティ・オブジェクトの上書きされたcreateメソッドを示しています。このメソッドは、新しいオーダー・エンティティ行に、OrderDate属性を移入する属性のsetterメソッドをコールします。
デフォルト値は、Groovy式を使用して定義することもできます。詳細は、4.10.6項「静的なデフォルト値を定義する方法」を参照してください。
例8-4 新しい行の属性値に対する、プログラムによるデフォルト設定
// In OrderEOImpl.java in Fusion Order Demo
protected void create(AttributeList nameValuePair) {
super.create(nameValuePair);
this.setOrderDate(new Date());
}
|
注意: 上書きされた |
行が最初に作成されたとき、および初期化状態にリフレッシュされたときの両方で実行するプログラム的なデフォルト設定ロジックでは、initDefaultExpressionAttributes()メソッドをオーバーライドする必要があります。
エンティティ行がNew状態であり、そのエンティティ行に対してrefresh()メソッドをコールした場合、REFRESH_REMOVE_NEW_ROWSフラグまたはREFRESH_FORGET_NEW_ROWSフラグを指定しないと、そのエンティティ行はInitialized状態に戻ります。このプロセスの一環として、エンティティ・オブジェクトのinitDefaultExpressionAttributes()メソッドが実行されますが、create()メソッドは再度実行されません。
4.10.9項「トリガーが割り当てられた値との同期方法」で、主キーの属性にDBSequence型を使用する方法を説明しています。この種の属性の値は、コミット時にデータベース・シーケンスによって取り込む必要があります。エンティティ行の作成時にユーザーが値を参照でき、この値がデータ保存時に変更されないように、順序番号を事前に割り当てる必要が生じることがあります。そのためには、例8-5に示すように、上書きされたcreate()メソッドでoracle.jbo.serverパッケージのSequenceImplヘルパー・クラスを使用します。この例は、Fusion Order DemoのStoreFrontモジュールにあるWarehouseEOエンティティ・オブジェクトのカスタムJavaクラスのコードを示しています。super.create()をコールした後に、順序名と現在のトランザクション・オブジェクトを渡し、SequenceImplオブジェクトの新規インスタンスを作成します。続いてSequenceImplのgetSequenceNumber()メソッドからの戻り値を含むsetWarehouseId()属性のsetterメソッドをコールします。
例8-5 作成時に順序からの属性値のデフォルト設定
// In WarehouseEOImpl.java
import oracle.jbo.server.SequenceImpl;
// Default WarehouseId value from WAREHOUSE_SEQ sequence at entity row create time
protected void create(AttributeList attributeList) {
super.create(attributeList);
SequenceImpl sequence = new SequenceImpl("WAREHOUSE_SEQ",getDBTransaction());
setWarehouseId(sequence.getSequenceNumber());
}
行を保存する前に、プログラムによるデフォルト値をエンティティ・オブジェクトの属性値に割り当てる場合は、prepareForDML()メソッドをオーバーライドし、適切な属性のsetterメソッドをコールして、導出された属性値を移入します。INSERT、UPDATEまたはDELETEの処理中にのみ割当てを実行するには、DML_INSERT、DML_UPDATE、DML_DELETEのそれぞれの整数値の定数と、このメソッドに渡されたoperationパラメータの値を比較できます。
例8-6は、導出値を割り当てる上書きされたprepareForDML()メソッドを示しています。
例8-6 PrepareForDMLを使用した保存前の導出値の割当て
protected void prepareForDML(int operation, TransactionEvent e) {
super.prepareForDML(operation, e);
//Populate GL Date
if (operation == DML_INSERT) {
if (this.getGlDate() == null) {
String glDateDefaultOption =
(String)this.getInvoiceOption().getAttribute("DefaultGlDateBasis");
if ("I".equals(glDateDefaultOption)) {
setAttribute(GLDATE, this.getInvoiceDate());
} else {
setAttribute(GLDATE, this.getCurrentDBDate());
}
}
}
//Populate Exchange Rate and Base Amount if null
if ((operation == DML_INSERT) || (operation == DML_UPDATE)) {
BigDecimal defaultExchangeRate = new BigDecimal(1.5);
if ("Y".equals(this.getInvoiceOption().getAttribute("UseForeignCurTrx"))) {
if (!(this.getInvoiceCurrencyCode().equals(
this.getLedger().getAttribute("CurrencyCode")))) {
if (this.getExchangeDate() == null) {
setAttribute(EXCHANGEDATE, this.getInvoiceDate());
}
if (this.getExchangeRateType() == null) {
String defaultConvRateType =
(String)this.getInvoiceOption().getAttribute("DefaultConvRateType");
if (defaultConvRateType != null) {
setAttribute(EXCHANGERATETYPE, defaultConvRateType);
} else {
setAttribute(EXCHANGERATETYPE, "User");
}
}
if (this.getExchangeRate() == null) {
setAttribute(EXCHANGERATE, defaultExchangeRate);
}
if ((this.getExchangeRate() != null) &&
(this.getInvoiceAmount() != null)) {
setAttribute(INVAMOUNTFUNCCURR,
(this.getExchangeRate().multiply(this.getInvoiceAmount())));
}
} else {
setAttribute(EXCHANGEDATE, null);
setAttribute(EXCHANGERATETYPE, null);
setAttribute(EXCHANGERATE, null);
setAttribute(INVAMOUNTFUNCCURR, null);
}
}
}
}
他の属性値が設定されているときに、導出された属性値を割り当てるには、最後の属性のsetterメソッドにコードを追加します。例8-7は、エンティティ・オブジェクトのAssignedTo属性に対するsetterメソッドを示しています。
例8-7 AssignedTo属性の変更に応じて割当て日を設定するメソッド
public void setAssignedTo(Number value) {
setAttributeInternal(ASSIGNEDTO, value);
setAssignedDate(getCurrentDateWithTime());
}
setAttributeInternal()をコールしてAssignedTo属性の値を設定すると、AssignedDate属性のsetterメソッドにより、現在日時の値が設定されます。
|
注意: ここで説明した生成済の属性のgetterメソッドとsetterメソッドへのカスタム・コードの追加は、安全に行うことができます。JDeveloperでクラスのコードが変更された場合でも、カスタム・コードはそのまま保持されます。 |
refresh(int flag)メソッドを行に対して使用すると、保留中のすべての変更をリフレッシュできます。refresh()メソッドの動作は、パラメータとして渡すフラグによって決まります。この動作を制御する3つの主要なフラグの値は、Rowインタフェースの次の定数です。
REFRESH_WITH_DB_FORGET_CHANGESは、現在のトランザクションによる行への変更を破棄し、行データがデータベースからリフレッシュされます。行が変更されたかどうかにかかわらず、現在の行はデータベースの最新データで上書きされます。
REFRESH_WITH_DB_ONLY_IF_UNCHANGEDは、unmodified以外は、REFRESH_WITH_DB_FORGET_CHANGESと同一の機能をします。現在のトランザクションで行がすでに変更されている場合は、行はリフレッシュされません。
REFRESH_UNDO_CHANGESは、unmodified行に対してREFRESH_WITH_DB_FORGET_CHANGESと同一の機能をします。変更された行については、このモードは、トランザクションの開始時点での属性値でリフレッシュします。リフレッシュ操作前に現在のトランザクション内でこの行がポスト済だったが未コミットの場合、この行は修正済状態を維持します。
デフォルトでは、refresh()を実行したNew状態のすべてのエンティティ行がInitialized状態の空白行に戻ります。宣言的なデフォルト値と、initDefaultExpressionAttributes()メソッドに記述されたプログラム的なデフォルトはリセットされますが、エンティティ・オブジェクトのcreate()メソッドは、この無効化のプロセスでは実行されません。
このデフォルトの動作は、次の2つのフラグのうちの1つと、8.4項のフラグの1つをビット単位のOR演算子によって組み合せて変更できます。
REFRESH_REMOVE_NEW_ROWS: リフレッシュ時に新しい行が削除されます。
REFRESH_FORGET_NEW_ROWS: 新しい行はDeadとマークされます。
ビジネス・ロジックでSQL問合せの実行が必要とされる場合、一般的には、そのタスクの実行にはビュー・オブジェクトを使用します。検証のために実行するSQL文では、エンティティ・ベースのビュー・オブジェクトであれば、エンティティ・キャッシュ内の保留中の変更が参照されます。読取り専用ビュー・オブジェクトでは、データベースに送信されたデータのみを取得します。
エンティティ・オブジェクトは任意のアプリケーション・シナリオで再利用されるので、特定のアプリケーション・モジュールのデータ・モデル内のビュー・オブジェクト・インスタンスに直接依存してはなりません。依存する場合、他のアプリケーション・モジュールで再使用ができないという事態が発生します。
かわりに、ビュー・アクセッサを使用して、ビュー・オブジェクトに対して検証する必要があります。詳細は、10.4.1項「エンティティ・オブジェクトまたはビュー・オブジェクトのビュー・アクセッサの作成方法」を参照してください。
例8-8に示すように、検証コードはビュー・アクセッサを使用してビュー・オブジェクトにアクセスし、バインド変数を設定します。
例8-8Methodバリデータでの検証ビュー・オブジェクトの使用
// Sample entity-level validation method
public boolean validateSomethingUsingViewAccessor() {
RowSet rs = getMyValidationVO();
rs.setNamedBindParameter("Name1", value1);
rs.setNamedBindParameter("Name2", value2);
rs.executeQuery();
if ( /* some condition */) {
/*
* code here returns true if the validation succeeds
*/
}
return false;
}
|
ベスト・プラクティス: プログラミングで行セットにアクセスする際は、必ず行セットのセカンダリ・イテレータの作成を検討してください。これにより、ビュー・オブジェクトをユーザー・インタフェース・プロジェクトのデータ・コントロールとして表示する場合に使用される、デフォルト行セットイテレータの現在の行セットに混乱が生じないようにします。作業中の行セットに対して |
サンプル・コードで示すように、検証に使用されるビュー・オブジェクトには通常、1つ以上の名前付きのバインド変数が含まれます。この例では、setNamedBindParameter()メソッドを使用してバインド変数を設定します。ただし、ビュー・アクセッサ定義用ページでGroovy式を使用して、JDeveloperで変数を宣言して設定することもできます。
ビュー・オブジェクトが取得するデータの種類によって、この例の「/* some condition */」式は異なります。たとえば、ビュー・オブジェクトのSQL問合せがCOUNT()などの集約関数を選択した場合、条件では一般的にrs.first()メソッドを使用して最初の行にアクセスし、続いてgetAttribute()メソッドを使用して属性値にアクセスし、そのカウントに対してデータベースが戻す結果を参照します。
問合せから0行または1行のどちらが戻されたかによって、検証が成功または失敗した場合、この条件はrs.first()がnullを戻したかどうかのみを検証します。rs.first()がnullを戻した場合、最初の行は存在しません。つまり、問合せで行は検出されていません。また、ビュー・オブジェクトから取得された1つ以上の問合せ結果に対し、検証の成否を繰り返し確認することもあります。
データベースに変更がポストされた後、コミットされる前に、変更保留中のリストの各エンティティに対して、beforeCommit()メソッドがコールされます。これは、特定の型のすべてのエンティティ行に対して規則を適用する必要のある、ビュー・オブジェクト・ベースの検証を実行する場合に役立つメソッドです。
beforeCommit()ロジックで例外ValidationExceptionがスローされた場合、構成中にjbo.txn.handleafterpostexcプロパティをtrueに設定する必要があります。これにより、フレームワークは現在のコミット・サイクルにおいて、データベースに送信済(かつコミット前)の他のエンティティ・オブジェクトのメモリー内の状態のロールバックを自動的に処理します。
|
注意: この項の例では、Fusion Order Demoアプリケーションの |
たとえば、例8-9に示す、オーバーライドされたbeforeCommit()について考えます。この例では、多相エンティティ・オブジェクトに基づいた3つのビュー・オブジェクト(Persons、StaffおよびSupplier)があり、それらが多相化識別子としてPersonTypeCode属性を持っています。PersonsImpl.javaファイルには、検証メソッドをコールするようにオーバーライドされたbeforeCommit()メソッドがあります。この検証メソッドでは、各ユーザー・タイプを通じてプリンシパル名が一意になっていることを確認するための4番目のビュー・オブジェクト、PersonsValidatorが使用されています。たとえば、Staffビュー・オブジェクトのPrincipalNameとしてSKINGが存在する場合、このユーザー・タイプまたは他のユーザー・タイプに別のSKINGにできません。
例8-9 特定タイプのすべてのエンティティを検証するためのbeforeCommit()のオーバーライド
// from the PersonsImpl.java file
. . .
@Override
public void beforeCommit(TransactionEvent transactionEvent) throws ValidationException {
String principalName = getPrincipalName();
if (!validatePrincipalNameIsUniqueUsingViewAccessor(principalName)) {
throw new ValidationException("Principal Name must be unique across person types");
}
super.beforeCommit(transactionEvent);
}
public boolean validatePrincipalNameIsUniqueUsingViewAccessor(String principalName) {
RowSet rs = getPersonsValidatorVO();
rs.setNamedWhereClauseParam("principalName", principalName);
rs.setRangeSize(-1);
rs.executeQuery();
Row[] validatorRows = rs.getAllRowsInRange();
if (validatorRows.length > 1)
// more than one row has the same princpalName
{
return false;
}
rs.closeRowSetIterator();
return true;
}
エンティティ・オブジェクトまたはビュー・オブジェクトのビジネス・ロジックが独自のビュー・アクセッサ行セット上で反復処理を行い、そのビュー・アクセッサがモデルによって定義された値のリストでも使用されていない場合、セカンダリ行セット・イテレータを使用する必要はありません。たとえば、ある名前のバインド・パラメータを取るビュー・オブジェクトのAirportValidationVAという名前のビュー・アクセッサがエンティティ・オブジェクトにある場合、GroovyスクリプトまたはJavaを使用して独自のアクセッサ行セットでの反復処理を行うことができます。例8-10に、ビュー・アクセッサ行セット上で反復処理を行うGroovyスクリプトを示します。
例8-10 Groovyスクリプトでのビュー・アクセッサの使用
AirportValidationVA.setNamedWhereClauseParam("VarTla",newValue)
AirportValidationVA.executeQuery();
return AirportValidationVA.first() != null;
例8-11に、ビュー・アクセッサ行セット上で反復処理を行うJavaメソッド・バリデータを示します。
関連するエンティティ・オブジェクトから情報にアクセスするには、エンティティ・オブジェクトのカスタムJavaクラスにあるアソシエーション・アクセッサ・メソッドを使用します。アクセッサ・メソッドをコールすると、アソシエーションのカーディナリティに応じて、すべての関連するエンティティ行、またはエンティティ行のセットに簡単にアクセスできます。
アクセッサを使用して関連するエンティティ行にアクセスできます。例8-12は、Fusion Order DemoのAdvancedEntityExamplesモジュールにあるControllingPostingOrderプロジェクトのコードを示しており、ProductsBaseエンティティ・オブジェクトのカスタムJavaクラスの上書きされたpostChanges()メソッドを示しています。この例では、getSupplier()アソシエーション・アクセッサを使用して、製品の関連サプライヤを取得します。
例8-12 Createメソッドにおける親エンティティ行へのアクセス
// In ProducstBaseImpl.java in the ControllingPostingOrder project
// of the Fusion Order Demo Advanced Entity Examples
@Override
public void postChanges(TransactionEvent transactionEvent) {
/* If current entity is new or modified */
if (getPostState() == STATUS_NEW || getPostState() == STATUS_MODIFIED) {
/* Get the associated supplier for the product */
SuppliersImpl supplier = getSupplier();
/* If there is an associated product */
if (supplier != null) {
/* And if its post-status is NEW */
if (supplier.getPostState() == STATUS_NEW) {
/* Post the supplier first, before posting this entity */
supplier.postChanges(transactionEvent);
}
}
}
super.postChanges(transactionEvent);
}
アソシエーションのカーディナリティが、複数の行が戻されるようになっている場合、アソシエーション・アクセッサを使用してエンティティ行のセットを戻すことができます。
例8-13は、Suppliersエンティティ・オブジェクトのカスタムJavaクラスの上書きされたpostChanges()メソッドのコードを示しています。このコードでは、setSupplierId()アソシエーション・アクセッサを使用して各行のSupplierId属性を更新するために、getProductsBase()アソシエーション・アクセッサを使用して、ProductsBase行のRowSetオブジェクトを取得します。
例8-13 アソシエーション・アクセッサを使用した関連するエンティティ行セットへのアクセス
// In SuppliersImpl.java in the ControllingPostingOrder project
// of the Fusion Order Demo Advanced Entity Examples
RowSet newProductsBeforePost = null;
@Override
public void postChanges(TransactionEvent transactionEvent) {
/* Only update references if Supplier is new */
if (getPostState() == STATUS_NEW) {
/*
* Get a rowset of products related to this new supplier before calling super
*/
newProductsBeforePost = (RowSet)getProductsBase();
}
super.postChanges(transactionEvent);
}
...
protected void refreshFKInNewContainees() {
if (newProductsBeforePost != null) {
Number newSupplierId = getSupplierId().getSequenceNumber();
/*
* Process the rowset of suppliers that referenced the new product prior
* to posting, and update their ProdId attribute to reflect the refreshed
* ProdId value that was assigned by a database sequence during posting.
*/
while (newProductsBeforePost.hasNext()){
ProductsBaseImpl product = (ProductsBaseImpl)newProductsBeforePost.next();
product.setSupplierId(newSupplierId);
}
closeNewProductRowSet();
}
}
アプリケーションでADFセキュリティの構成ウィザードを実行して、ADF認証サーブレットによるユーザーのログインとログアウトのサポートを有効にした場合、oracle.jbo.server.SessionImplオブジェクトでは、認証されたユーザーの名前と、そのユーザーの属するロールを取得するメソッドを使用できます。これは、クライアントがアクセス可能なoracle.jbo.Sessionインタフェースの実装クラスです。
認証済ユーザーに関する情報へのアクセス方法の詳細は、35.11.3.3項「現在のユーザー名、エンタープライズ名、またはエンタープライズIDの特定方法」および35.11.3.4項「Java EEセキュリティ・ロールのメンバーシップ特定方法」を参照してください。
Oracle Fusion Webアプリケーションのセキュリティ機能の詳細は、第35章「Fusion WebアプリケーションでのADFセキュリティの有効化」を参照してください。
エンティティ属性値が現在のトランザクション内で変更されている場合、属性getterメソッドをその属性値に対してコールすると、この保留中の変更値が戻されます。変更前の元の値を取得する場合があります。getPostedAttribute()メソッドを使用すると、エンティティ・オブジェクトのビジネス・ロジックで、エンティティ行が変更される前にデータベースから読み取った、属性の元の値を確認できます。このメソッドは属性indexを引数として取るので、JDeveloperに保管されている正しく生成された属性インデックス列挙を渡します。
現在のユーザー・セッションに関連した情報を、エンティティ・オブジェクトのビジネス・ロジックにより参照可能な方法で格納する必要がある場合、Sessionオブジェクトで提供されるユーザー・データのハッシュテーブルを使用できます。
新規ユーザーがアプリケーション・モジュールに初めてアクセスする場合、prepareSession()メソッドがコールされます。例8-14に示すように、アプリケーション・モジュールはprepareSession()を上書きし、ビュー・オブジェクト・インスタンスのretrieveUserInfoForAuthenticatedUser()メソッドをコールして、認証されたユーザーに関する情報を取得します。次に、ユーザーの数値IDをユーザー・データ・ハッシュテーブルに保存するため、setUserIdIntoUserDataHashtable()ヘルパー・メソッドをコールします。
例8-14 ユーザー情報の動問合せのためのprepareSession()の上書き
// In the application module
protected void prepareSession(Session session) {
super.prepareSession(session);
/*
* Query the correct row in the VO based on the currently logged-in
* user, using a custom method on the view object component
*/
getLoggedInUser().retrieveUserInfoForAuthenticatedUser();
setUserIdIntoUserDataHashtable();
}
例8-15に、ビュー・オブジェクトのretrieveUserInfoForAuthenticatedUser()メソッドのコードを示します。これは、それ自体のEmailAddressバインド変数を、セッションの認証されたユーザー名に設定し、executeQuery()をコールしてUSERS表から追加のユーザー情報を取得します。
例8-15 ユーザー詳細の追加取得のための、認証されたユーザー名へのアクセス
// In the view object's custom Java class
public void retrieveUserInfoForAuthenticatedUser() {
SessionImpl session = (SessionImpl)getDBTransaction().getSession();
setEmailAddress(session.getUserPrincipalName());
executeQuery();
first();
}
ビュー・オブジェクトが取得する、認証されたユーザーの情報の1つには、メソッドの結果として戻されるユーザーの数値ID番号があります。たとえば、ユーザーskingには、数値のUserIdとして300が割り当てられています。
例8-16は、例8-14のprepareSession()のコードで使用される、setUserIdIntoUserDataHashtable()ヘルパー・メソッドを示しています。このメソッドでは、文字列定数CURRENT_USER_IDで指定されるキーを使用して、数値ユーザーIDをユーザー・データ・ハッシュテーブルに格納します。
例8-16 エンティティ・オブジェクトからのアクセスのためのユーザー・データ・ハッシュテーブルへの情報の設定
// In the application module
private void setUserIdIntoUserDataHashtable() {
Integer userid = getUserIdForLoggedInUser();
Hashtable userdata = getDBTransaction().getSession().getUserData();
userdata.put(CURRENT_USER_ID, userid);
}
この例の対応するエンティティ・オブジェクトでは、例8-17のようなヘルパー・メソッドを使用して、この数値ユーザーIDを参照するようにcreate()メソッドを上書きできます。上書きされたメソッドでは、CreatedBy属性を現在認証されているユーザーの数値ユーザーIDにプログラム的に設定します。
フレームワークでGroovyスクリプトを使用できる、オブジェクトにアクセス可能なadfという名前のトップレベルのオブジェクトが用意されています。adf.userSessionオブジェクトは、ADF Business Componentsユーザー・セッションへの参照を返します。これを使用してセッションに含まれているuserDataハッシュ・マップ内の値を参照できます。
例8-18に、MyKeyという名前のuserDataハッシュ・マップ・キーの参照に使用するGroovyスクリプトを示します。
エンティティ・オブジェクトのビジネス・ロジックにおいては、現在の日時の参照が役立つ場合があります。次のGroovyスクリプト式を使用すると、現在の日付または現在の日付と時間を参照できます。
adf.currentDate: 現在の日付(時間を切捨て)を戻します。
adf.currentDateTime: 現在の日付と時間を戻します。
エンティティ・オブジェクトのビジネス・ロジックでのGroovyスクリプトの使用方法の詳細は、3.6項「Groovyスクリプト言語サポートの概要」を参照してください。
変更保留中のリストに含まれていた各エンティティ行に対してafterCommit()メソッドがコールされ、データベースに正しく格納されます。このメソッドを使用して、コミットに関する通知を送信できます。
コミット成功時に通知を送信するもっとよい方法は、ビジネス・イベントを宣言する方法です。ビジネス・イベントの作成方法の詳細は、4.12項「ビジネス・イベントの作成」を参照してください。
remove()メソッドは、エンティティ行削除の前に、その行に対して起動します。remove()メソッドでは、JboExceptionをスローし、該当する条件を満たさない場合は行の削除を回避するよう設定できます。
たとえばremove()メソッド内で、エンティティ・オブジェクトの状態を判断するテストを追加し、それが新規レコードの場合にのみ削除可能にすることができます。例8-19に、この方法を示します。
|
注意: この例は、Fusion Order Demoアぷケーションの |
例8-19 remove()メソッドのオーバーライドによる削除前のエンティティ状態の確認
// In the Addresses entity object custom Java class
private boolean isDeleteAllowed() {
byte s = this.getEntityState();
return s==STATUS_NEW;
}
/**
* Add entity remove logic in this method.
*/
public void remove() {
if (isDeleteAllowed())
super.remove();
else
throw new JboException("Delete not allowed in this view");
}
|
注意: このエンティティ・オブジェクトでは、既存の構成される側の子行がある場合、マスター・エンティティ行の削除が宣言的に回避されます。このオプションは、アソシエーションの概要エディタの「関連」ページで設定します。 |
エンティティ・オブジェクト・クラス内のisAttributeUpdateable()メソッドをオーバーライドすると、特定の属性が更新可能かどうかを、実行時に適切な条件に従ってプログラム的に判断できます。
例8-20は、現在の認証されたユーザーがスタッフ・メンバーである場合にのみPersonTypeCode属性を更新可能にするため、エンティティ・オブジェクトによりisAttributeUpdateable()がどのように上書きされるかを示しています。エンティティ・オブジェクトでは、このメソッドを実行する際、更新可能性の確認対象となる整数属性索引を渡します。
特定の属性に対する条件付きの更新可能性ロジックは、属性索引に基づいて、if文またはswitch文内に実装できます。ここでは、PERSONTYPECODEがエンティティ・オブジェクトのカスタムJavaクラスに保持されている整数属性の索引列挙を参照します。
例8-20 属性の更新可能性の実行時の条件付きの判断
// In the entity object custom Java class
public boolean isAttributeUpdateable(int index) {
if (index == PERSONTYPECODE) {
if (!currentUserIsStaffMember()) {
return super.isAttributeUpdateable(index);
}
return CUSTOMER_TYPE.equals(getPersonTypeCode()) ? false : true;
}
return super.isAttributeUpdateable(index);
}
|
注意: エンティティ・ベースのビュー・オブジェクトは、エンティティ・オブジェクト内でカプセル化された他の要素と同様に、この条件付きの更新可能性も継承します。この種の条件付き更新可能性ロジックを、一時ビュー・オブジェクト属性に固有の方法で実装する必要がある場合、または、ビュー・オブジェクト内の複数のエンティティ・オブジェクトのデータを含む一部の条件を適用する場合、ビュー・オブジェクトのビュー行クラス内の同じメソッドをオーバーライドすると、目的の結果が得られます。 |
ADF Business Componentsには、開発者が使用できる組込みの宣言的検証規則の基本セットが付属しています。ただし、エンティティ・オブジェクト用のバリデータ・アーキテクチャの強力な機能は、独自のカスタム検証規則を作成できることです。同じ種類の検証コードを繰り返し作成している場合は、カスタム検証規則クラスを作成し、このような共通の検証パターンをパラメータ化された方法で取得できます。
定義したカスタム検証規則クラスは、JDeveloperに登録し、組込み規則と同じように簡単に使用できます。実際、カスタム検証規則をカスタムUIパネルにバンドルすることもできます。JDeveloperはこれを利用して、検証規則で必要とするパラメータを開発者が簡単に使用および構成できるようにします。
エンティティ・オブジェクト用のカスタム検証規則を作成するには、oracle.jbo.rulesパッケージのJboValidatorInterfaceを実装するJavaクラスが必要です。「新規ギャラリ」からスケルトン・クラスを作成できます。
作業を始める前に、次のようにします。
カスタム検証規則に関する知識があると役立つ場合があります。詳細は、8.14項「カスタム検証規則の実装」を参照してください。
また、他の検証機能を使用して追加できる機能についても理解しておくと役立ちます。詳細は、8.1.2項「プログラム的なビジネス・ルールの追加機能」を参照してください。
カスタム・バリデータを作成する手順:
アプリケーション・ナビゲータで、バリデータを作成するプロジェクトを右クリックし、ポップアップ・メニューから「新規」を選択します。
「新規ギャラリ」で「ビジネス層」を展開し、「ADF Business Components」をクリックして「検証規則」を選択し、「OK」をクリックします。
例8-21に示すように、JBOValidatorInterfaceには、1つのメインvalidate()メソッドと、Descriptionプロパティ用のgetterおよびsetterメソッドが含まれます。
例8-21 すべての検証規則が実装する必要のあるJboValidatorInterface
package oracle.jbo.rules;
public interface JboValidatorInterface {
void validate(JboValidatorContext valCtx) { }
java.lang.String getDescription() { }
void setDescription(String description) { }
}
検証規則の動作がパラメータ化されて柔軟性が増してから、検証クラスの各パラメータにBeanプロパティを追加します。たとえば、例8-22のコードにはDateMustComeAfterRuleという名前のカスタム検証規則が含まれており、ある日付属性が別の日付属性より後でなければならないことを検証します。規則を使用する開発者が検証対象の2つの日付として使用する日付属性の名前を構成できるように、このクラスではinitialDateAttrNameとlaterDateAttrNameという2つのプロパティが定義されています。
例8-22は、カスタム検証規則を実装するコードを示しています。これは、AbstractValidatorを拡張し、エンティティ・オブジェクトのカスタム・メッセージ・バンドルの使用を継承しています。開発者がエンティティ・オブジェクトの規則を使用すると、JDeveloperは検証エラー・メッセージをここに保存します。
検証規則のvalidate()メソッドは、実行時に規則クラスが機能を実行する必要がある場合に常に呼び出されます。このコードが実行する基本的な手順は次のとおりです。
エンティティ・レベルでバリデータが正しくアタッチされていることを確認します。
検証するエンティティ行を取得します。
先の日と後の日の属性の値を取得します。
先の日が後の日より前であることを検証します。
検証が失敗した場合は例外をスローします。
例8-22 カスタムのDateMustComeAfterRule
// NOTE: package and imports omitted
public class DateMustComeAfterRule extends AbstractValidator
implements JboValidatorInterface {
/**
* This method is invoked by the framework when the validator should do its job
*/
public void validate(JboValidatorContext valCtx) {
// 1. If validator is correctly attached at the entity level...
if (validatorAttachedAtEntityLevel(valCtx)) {
// 2. Get the entity row being validated
EntityImpl eo = (EntityImpl)valCtx.getSource();
// 3. Get the values of the initial and later date attributes
Date initialDate = (Date) eo.getAttribute(getInitialDateAttrName());
Date laterDate = (Date) eo.getAttribute(getLaterDateAttrName());
// 4. Validate that initial date is before later date
if (!validateValue(initialDate,laterDate)) {
// 5. Throw the validation exception
RulesBeanUtils.raiseException(getErrorMessageClass(),
getErrorMsgId(),
valCtx.getSource(),
valCtx.getSourceType(),
valCtx.getSourceFullName(),
valCtx.getAttributeDef(),
valCtx.getNewValue(),
null, null);
}
}
else {
throw new RuntimeException("Rule must be at entity level");
}
}
/**
* Validate that the initialDate comes before the laterDate.
*/
private boolean validateValue(Date initialDate, Date laterDate) {
return (initialDate == null) || (laterDate == null) ||
(initialDate.compareTo(laterDate) < 0);
}
/**
* Return true if validator is attached to entity object
* level at runtime.
*/
private boolean validatorAttachedAtEntityLevel(JboValidatorContext ctx) {
return ctx.getOldValue() instanceof EntityImpl;
}
// NOTE: Getter/Setter Methods omitted
private String description;
private String initialDateAttrName;
private String laterDateAttrName;
}
カスタム検証規則を簡単に再利用できるように、通常は、規則を利用するアプリケーションでの参照用として規則をJARファイルにパッケージします。
検証規則クラスはBeanであるため、標準のJavaBeanカスタマイザ・クラスを実装して、設計時にBeanのプロパティを設定しやすくすることができます。例8-22のDateMustComeAfter規則の例では、開発者はinitialDateAttrNameとlaterDateAttrNameの2つのプロパティを構成する必要があります。
図8-4は、Swing用のJDeveloperビジュアル・デザイナを使用して作成されたDateMustComeAfterRuleCustomizerを示しており、タイトル付きの境界を持つJPanelに、ドロップダウン・リスト用の2つのJLabelプロンプトと2つのJComboBoxコントロールが含まれています。クラスのコードによって、IDEで現在編集されているエンティティ・オブジェクトのDate値属性の名前がドロップダウン・リストに移入されます。これにより、DateMustComeAfterRule検証をエンティティ・オブジェクトに追加する開発者は、検証の開始日と終了日に使用する日付属性を簡単に選択できます。
カスタマイザとDateMustComeAfterRule Java Beanを関連付けるには、BeanInfoクラスを作成するときの標準的な手順に従います。例8-23で示されているように、DateMustComeAfterRuleBeanInfoは、カスタマイザ・クラスをDateMustComeAfter Beanクラスと関連付けるBeanDescriptorを返します。
通常は、カスタマイザ・クラスとこのBean情報を、設計時専用の独立したJARファイルにパッケージします。
例8-23 カスタマイザとカスタム検証規則を関連付けるためのBeanInfo
package oracle.fodemo...frameworkExt.rules;
import java.beans.BeanDescriptor;
import java.beans.SimpleBeanInfo;
public class DateMustComeAfterRuleBeanInfo extends SimpleBeanInfo {
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(DateMustComeAfterRule.class,
DateMustComeAfterRuleCustomizer.class);
}
}
カスタム検証規則は作成後、JDeveloper IDEのプロジェクトまたはアプリケーションレベルに追加できるため、他の開発者がその規則を宣言的に使用できます。
カスタム検証規則をプロジェクト・レベルで登録した場合は、プロジェクト内でその検証規則を使用できます。
作業を始める前に、次のようにします。
カスタム検証規則に関する知識があると役立つ場合があります。詳細は、8.14項「カスタム検証規則の実装」を参照してください。
また、他の検証機能を使用して追加できる機能についても理解しておくと役立ちます。詳細は、8.1.2項「プログラム的なビジネス・ルールの追加機能」を参照してください。
カスタム検証規則を登録するには、8.14.1項「カスタム検証規則の作成方法」で説明しているように、先にその検証規則を作成しておく必要があります。
エンティティ・オブジェクトを含むプロジェクトでカスタム検証規則を登録する手順:
アプリケーション・ナビゲータで、目的のプロジェクトを右クリックして、ポップアップ・メニューから「プロジェクト・プロパティ」を選択します。
「プロジェクト・プロパティ」ダイアログで、「ビジネス・コンポーネント」を展開し、「登録済の規則」を選択します。
「登録済の規則」ページで、「追加」をクリックします。
「検証規則の登録」ダイアログで、作成した検証規則を参照して見つけ(8.14.1項「カスタム検証規則の作成方法」で作成したような検証規則)、「OK」をクリックします。
カスタム検証規則をJDeveloperのIDEレベルで登録すると、現在のプロジェクトのみでなく、他のプロジェクトでもその検証規則を使用できます。
作業を始める前に、次のようにします。
カスタム検証規則に関する知識があると役立つ場合があります。詳細は、8.14項「カスタム検証規則の実装」を参照してください。
また、他の検証機能を使用して追加できる機能についても理解しておくと役立ちます。詳細は、8.1.2項「プログラム的なビジネス・ルールの追加機能」を参照してください。
カスタム検証規則を登録するには、8.14.1項「カスタム検証規則の作成方法」で説明しているように、先にその検証規則を作成しておく必要があります。
IDEレベルのカスタム・バリデータを登録する手順:
「ツール」メニューで「設定」を選択します。
「設定」ダイアログで、「ビジネス・コンポーネント」を開き、「ルールの登録」を選択します。
「ルールの登録」ページから、1つ以上の検証規則を追加できます。
検証規則を追加するときは、検証規則クラスの完全修飾名を指定し、JDeveloperの使用可能なバリデータのリストで表示される検証規則の名前を設定します。