ADF Business Componentsは、あらゆる機能を備えた、XMLベースのビジネス・サービス作成用フレームワークです。ADF Business Componentsは、Oracle9i以前のJDeveloperで配布されていたBusiness Components for Java(BC4J)が発展したものです。ADF Business Componentsのランタイム・ライブラリはほとんどのビジネス・サービス機能を扱い、これらは宣言的(JDeveloperのRADツールでXMLファイルを変更)またはプログラム的(ライブラリ・クラスを拡張)にカスタマイズ可能です。Oracle ADF Business Componentsの機能は次のとおりです。
O/Rマッピングおよび永続性を自動的に処理します。
SQLを使用してデータ取得用の複雑なリクエストを作成できます。
コミット時ロックまたは即時ロックを含むトランザクション管理を自動的に処理します。
複雑なビジネス・ロジックを実装するためのフレームワークを提供します。
多数のJ2EEデザイン・パターンを自動的に実装します。
強力なキャッシュおよびデータの非アクティブ化システムにより、アプリケーションのパフォーマンスとスケーラビリティを向上させます。
これらの機能はどれも自由にカスタマイズできます。たとえば、ADF Business ComponentsによるO/Rマッピングの処理方法を使用しない場合はオーバーライドできます。
この章では、Oracle ADF Business Componentsの概要を説明します。ADF Business Componentsの各コンポーネント・タイプと、ADF Business Components設計におけるいくつかの基本的な問題について理解してください。
ADFエンティティ・オブジェクト定義は、アプリケーションで使用される項目に対し、データ、ルールおよび永続性の動作を含むビジネス・モデルをカプセル化するビジネス・コンポーネントです。たとえば、次の内容をエンティティ・オブジェクトで表すことができます。
製造ライン、部門、売上げおよび地域など、ビジネスの論理構造を構成する要素
請求書、注文変更およびサービス・リクエストなどのビジネス文書
倉庫、従業員および設備などの物理的項目
エンティティ・オブジェクト定義はデータソース内のシングル・オブジェクトにマップします。ほとんどの場合、データベース内の表、ビュー、シノニムまたはスナップショットです。熟練したプログラマであれば、スプレッドシート、XMLファイルまたはフラットなテキスト・ファイルなどの他のデータソース内のオブジェクトに基づいてエンティティ・オブジェクトを作成できます。
エンティティ・オブジェクト定義は、エンティティ・オブジェクト・インスタンス(データベース表の個々の行を表す1つのJavaオブジェクト)のテンプレートです。たとえば、Departmentsという名前のエンティティ・オブジェクト定義は、DEPARTMENTS表の個々の行を表すエンティティ・オブジェクト・インスタンス用のテンプレートを提供します。
エンティティ・オブジェクト定義は、最大で次の4つのパーツで構成されます。
XMLファイル: エンティティ・オブジェクト定義のうち、宣言的に開発可能な部分を表します。ほとんどの情報がO/Rマッピングに必要なものですが、Validatorと呼ばれる単純な検証ルールも含むことができます。多くのエンティティ・オブジェクトでは、XMLファイルのみで十分です。
エンティティ・オブジェクト・クラス: 個々のエンティティ・オブジェクト・インスタンスを表します。エンティティ・オブジェクト・クラスでは、XMLのValidatorが十分でない場合に、複雑なビジネス・ロジックをJavaで記述できます。エンティティ・オブジェクト・クラスはクラスoracle.jbo.server.EntityImpl
を拡張します。カスタムのJavaビジネス・ロジックが必要でない場合には、エンティティ・オブジェクト・クラスを作成する必要はありません。ADFでは、oracle.jbo.server.EntityImpl
をそのまま使用してデータソースの行を表すことが可能です。
エンティティ定義クラス: データ・ソース・オブジェクトを全体として表します。エンティティ定義クラスはXMLファイルに対するJavaラッパーの役割をします。このため、メタデータの特別な扱いが必要な場合(動的に変更する必要がある場合など)には、そのコードをエンティティ定義クラスに追加できます。エンティティ定義クラスはクラスoracle.jbo.server.EntityDefImpl
を拡張します。メタデータの特殊な扱いが必要でない場合には、エンティティ定義クラスを作成する必要はありません。ADFでは、oracle.jbo.server.EntityDefImpl
をそのまま使用してメタデータをラップすることが可能です。
エンティティ・コレクション・クラス: 1人のユーザー用にメモリーに格納される行のキャッシュ(エンティティ・オブジェクト・クラスのインスタンス)を表します。ほとんどの場合、エンティティ・コレクション・クラスを作成する必要はありません。ADF Business Componentsのデフォルトのキャッシュ方法をオーバーライドする場合にのみ、作成してください。
エンティティ・オブジェクトがデータベース・オブジェクトに基づいている場合、データベース・オブジェクト(データベース表など)内の列は、エンティティ・オブジェクト内の1つのエンティティ・オブジェクト属性にマップします。ただし、マッピングは1対1である必要はありません。これらの属性の定義は、これらの列のプロパティ(列のデータ型、列制約、および精度とスケールの仕様など)を反映します。エンティティ・オブジェクトが他のデータソースのオブジェクトに基づいている場合、エンティティ・オブジェクト属性は、プログラマの定義に従ってそのオブジェクトの列にマップします。
エンティティ・オブジェクト属性には次の2つのタイプがあります。
永続的属性: データソース・オブジェクトの列にマップする属性。
一時属性: データソース・オブジェクトの列にマップしない属性。一時属性はデータベース内の情報から導出でき、多くの場合一時的な格納と取得に使用されます。
エンティティ属性の値は、次のどちらかの方法で読込みまたは変更できます。
EntityImplクラスは、属性の名前をStringとして受け入れるgetAttribute()
とsetAttribute()
の2つのメソッドを提供します。たとえばエンティティ・オブジェクトにDepartmentNameという属性がある場合、その値にアクセスするにはgetAttribute("DepartmentName")
またはsetAttribute("DepartmentName", "Marketing")
をコールします。
エンティティ・オブジェクト・クラスを作成した場合、そのクラスには各属性に対するタイプセーフなgetterおよびsetterが含まれます。たとえばエンティティ・オブジェクトにDepartmentNameという属性があり、エンティティ・オブジェクト・クラスを作成した場合、この値にアクセスするにはgetDepartmentName()
またはsetDepartmentName("Marketing")
をコールします。
タイプセーフなgetterおよびsetterは、検証ロジックなどの属性レベルのビジネス・ルールの配置場所として最適です。getterはEntityImpl.getAttributeInternal()
メソッドへの、setterはEntityImpl.setAttributeInternal()
メソッドへのコールを含みます。そのコールを別のコード(IF-THENブロックなど)でラップすることで、属性の変更またはアクセスに条件を設定することや、属性がアクセスまたは変更される前後にロジックを強制することができます。
getterおよびsetterへの検証ロジックの追加に加え、エンティティ・オブジェクト・クラスを作成しているかどうかにかかわらず、エンティティ属性に宣言的にValidatorをアタッチすることもできます。JDeveloperには、次の4つの単純なValidatorが用意されています。
CompareValidator: 属性を値(リテラル値またはデータソースから引き出した値)と比較します。
ListValidator: 属性が値のリスト(リテラル・リストまたは問合せの結果)にあるかどうかをチェックします。
RangeValidator: 属性が2つのリテラル値の間にあるかどうかをチェックします。
MethodValidator: エンティティ・オブジェクト・クラス内の、ブール値を戻すあらゆるメソッドを起動できます。メソッドがTRUE
を戻した場合、検証は有効です。
この他に、独自のValidatorを作成できます。そのためにはコーディングが必要ですが、作成したValidatorはいくつもの異なるプロジェクトで宣言的に適用可能です。Validatorクラスはインタフェースoracle.jbo.server.rules.JbiValidator
を実装する必要があります。このインタフェースには次の3つが必要です。
validateValue()
メソッド。java.lang.Object
(新しい属性値)をパラメータとして受け入れて、ブール値(TRUE
またはFALSE
)を戻します。一般にこのメソッドでは、属性値が許容される場合にはTRUE
を、されない場合にはFALSE
を戻します。
vetoableChange()
メソッド。タイプoracle.jbo.server.util.PropertyChangeEvent
のパラメータを受け入れます。これは、属性値が変更されたときにEntityImpl
がコールするメソッドです。一般にこのメソッドでは、PropertyChangeEvent
インスタンスから値を抽出し、validateValue()
をコールし、validateValue()
がFALSE
を戻した場合に例外をスローします。
description
フィールド(アクセッサ・メソッド付き)。ADFはこのフィールドを使用しませんが、インタフェースで必要です。
検証ルールには、これらの必須項目の他にもフィールドを追加できます。エンティティ属性にルールを適用する際には、フィールドに値を指定して、宣言的にルールをカスタマイズできます。
行レベルの検証は、2つ以上の属性を一度に検証する必要がある場合に便利です。この種の検証は、EntityImpl.validateEntity()
をオーバーライドしてエンティティ・レベルに実装します。
エンティティ・オブジェクトのインスタンスがその現状を失ったとき(クライアントが特定の行の表示を終了したとき)や、クライアントがトランザクションのコミットを試みたときには、必ずvalidateEntity()
がコールされます。validateEntity()
が例外をスローした場合、そのエンティティ・オブジェクトではエラーが修正されるまで現状の損失が回避されます。
拡張メソッドには、必ずsuper.validateEntity()
へのコールを指定してください。JDeveloperには、これを実行するスケルトンのvalidateEntity()
メソッドを作成するツールが含まれています。このコールの前にコードを追加すれば、検証ロジックが満たされなかった場合に例外をスローできます。
エンティティ・オブジェクトが作成または削除のマーク付けをされると、メソッドEntityImpl.create()
またはEntityImpl.remove()
が必ずコールされます。これらのメソッドのどちらかまたは両方をオーバーライドすれば、行が作成または削除されるときに起動するビジネス・ルールを実装できます。
拡張メソッドには、必ずsuper.create()
またはsuper.remove()
へのコールを指定してください。JDeveloperには、これを実行するスケルトンのcreate()
メソッドおよびcreate()
メソッドを作成するツールが含まれています。これらのコールの後にさらにロジックを追加すれば、初期化またはクリーンアップのタスクも実行できます。
ADF BCは、必要に応じてINSERT、UPDATEまたはDELETEコマンドをデータベースへ発行し、DML操作を自動的に処理します。ただし、メソッドEntityImpl.doDML()
をオーバーライドすれば、異なる永続性動作(ストアド・プロシージャの使用や、データベース以外のデータソースへの書込みなど)を実装できます。
doDML()
は、リクエストされたDML操作を表すint
型のoperation
をパラメータとして取ります。int
の値は、常に3つ数字のいずれかで、それぞれの値は次のfinal
変数で表されます。
DML_INSERT
: 行挿入のリクエスト
DML_DELETE
: 行削除のリクエスト
DML_UPDATE
: 行更新のリクエスト
operationの値をこの3つの値と比較すれば、フレームワークのデフォルトの動作を上書きできます。
Oracle ADF Business Componentsは、Java Authentication and Authorization Service(JAAS)とともに使用してOracle ADFアプリケーションのユーザーの認証を行うこともできます。Oracle ADF Business Componentsは、OracleAS JAAS ProviderおよびJAAS準拠のあらゆる外部実装とともに動作します。
Oracle ADF Business Componentsは、Oracle Internet Directory(OID)または軽量JAZN-XMLファイル(テスト用および認証ユーザーの数が少ない場合に便利)のどちらかを介してOracleAS Single Sign-On(SSO)を使用します。
ビジネス・コンポーネントとともにJAAS認証を使用する場合には、エンティティ・オブジェクト属性の権限を付与し、特定のユーザーまたは特定グループのメンバーのみにアクセスを許可する(認証と呼ばれるプロセス)ことができます。アクセス・レベルはアクセス不可、読取り専用アクセス、またはフル・アクセスを設定できます。
また、履歴列属性と呼ばれるエンティティ・オブジェクト属性を使用して監査証跡を管理することもできます。この証跡には、行の挿入または変更を行った認証済ユーザーと、変更の実施時刻に関する情報が含まれます。
エンティティ・オブジェクト定義間の関連はOracle ADFアソシエーションで処理されます。アソシエーションは、2つのOracle ADFエンティティ・オブジェクト定義間の関連を、それぞれのエンティティ属性のセットに基づいて定義します。
アソシエーションはデータソース内のシングル・オブジェクト間の関連にマップします。ほとんどの場合、データベース内の表、ビュー、シノニムまたはスナップショット間のリレーションです。熟練したプログラマであれば、アソシエーションを使用して、スプレッドシート、XMLファイルまたはフラットなテキスト・ファイルなどの他のデータソース内での関連を表すことが可能です。
データソースがデータベースであれば、多くの場合、アソシエーションはデータベース内の表間の外部キー関連にマップします。対応するエンティティ・オブジェクト間に1対1または1対多のアソシエーションを作成するために表間に実際に外部キー制約を作成する必要はありませんが、表間には少なくとも適切な論理的関連が必要です。
2つのエンティティ・オブジェクト定義間にアソシエーションを作成する際には、関連元のエンティティ・オブジェクト定義または関連先のエンティティ・オブジェクト定義、あるいはその両方にアクセッサ属性を追加できます。これらのアクセッサ属性は、次のように他の属性と非常に似た動作をします。
属性名をEntityImpl.getAttribute()
に引数として渡すことができます。
エンティティ・オブジェクト・クラスを作成すると、アクセッサ属性用のgetterメソッドがそのクラスに含まれます。
getAttribute()
またはgetterメソッドへのコールで戻される内容は、アソシエーションのカーディナリティによって決まります。
アソシエーションは、外部キーに基づく単純な1対多の関連から、複雑な多対多の関連まで様々です。たとえば、次のような関連をアソシエーションで表すことができます。
顧客とその顧客によるすべての注文との間の1対多の関連
製品とその詳細説明との間の1対1の関連(それぞれが別々のエンティティ・オブジェクトで表されている場合)
製品と現在その製品の在庫がある倉庫との間の多対多の関連
1対1および1対多のアソシエーションの動作は、外部キー関連と非常によく似ています。関連元エンティティ・オブジェクトの属性セット(主キーを表すものなど)は、関連先エンティティ・オブジェクトの属性セット(外部キーを表すものなど)に一致します。
多対多のアソシエーションは、関連元と関連先のエンティティ・オブジェクト定義と、第3のエンティティ・オブジェクト定義である交差の、2つの1対多の関連と事実上同じです。たとえば、製品とその製品の在庫がある倉庫との多対多の関連は、次の2つの1対多の関連と考えることができます。
製品と製品を記載した倉庫目録のエントリとの間の1対多の関連
倉庫とその目録エントリとの間の1対多の関連
これらの関連には3つのエンティティ・オブジェクトが必要で、1つは製品を表し(関連元)、1つは倉庫を表し(関連先)、もう1つは倉庫目録のエントリを表します(交差)。
アソシエーションのカーディナリティは、アソシエーション・アクセッサによって戻される内容を左右します。
1対多または1対1のアソシエーションの「1」の方を戻すアソシエーション・アクセッサは、個々のエンティティ・オブジェクト・インスタンスを戻します。
1対多または多対多のアソシエーションの「多」の方を戻すアソシエーション・アクセッサは、行イテレータを戻します。
行イテレータは、エンティティ・オブジェクト・インスタンスまたはビュー行のコンテナです。行イテレータはADF BCアーキテクチャの複数の場所で発生しますが、アソシエーション・アクセッサが戻す行イテレータにはエンティティ・オブジェクト・インスタンスが含まれます。
行イテレータには、1つの特定のエンティティ・オブジェクト・インスタンスまたはビュー行をポイントする、現在行ポインタが含まれます。このポインタは移動可能で、イテレータからの行の抽出に使用できます。
行イテレータには、個々の行をナビゲートして抽出するための次のメソッドが含まれます。
next()
: 行イテレータ内の現在行ポインタを次に進め、その行を戻します。
hasNext()
: 行イテレータの現在行ポインタの後ろにも行があるかどうかをチェックします。next()
とhasNext()
を併用すると、イテレータ内の行を循環するループを作成できます。
first()
: 現在行ポインタを行イテレータ内の最初の行に移動し、その行を戻します。
last()
: 現在行ポインタを行イテレータ内の最後の行に移動し、その行を戻します。
previous()
: 行イテレータ内の現在行ポインタを1行前に戻し、その行を戻します。
hasPrevious()
: 行イテレータの現在行ポインタの前にも行があるかどうかをチェックします。previous()
とhasPrevious()
を併用すると、イテレータ内の行を逆方向に循環するループを作成できます。
コンポジットは、関連元オブジェクトが関連先オブジェクトのコンテナとなるアソシエーションです。挿入、更新および削除の際には、それぞれが独立したエンティティで単に関連付けられているのではなく、関連先エンティティ・オブジェクトのインスタンスが関連元エンティティ・オブジェクトのインスタンスの一部とみなされます。この種の関連の例としては、発注と発注内の明細項目があります。部門と所属従業員との関連(従業員は独立して存在するエンティティで、単にある部門のメンバーであるにすぎない)とは異なり、明細項目は発注の完全な一部で、独立して存在することはありません。
アソシエーションをコンポジットにすると、次の効果があります。
関連先インスタンスが関連元から独立して存在することがなくなります。ADF Business Componentsでは、関連元インスタンスが削除されたときに自動的に関連先インスタンスを削除するようにするか、または関連先インスタンスが存在している状態で関連元インスタンスが削除されたときに例外をスローするように指定できます。
関連先インスタンスが変更されると、関連元インスタンスには再検証が必要というマークが付けられます。
関連先インスタンスが関連元インスタンスの検証の一部として検証されます。
ADFドメインは、エンティティ属性などのOracle ADF Business Components属性で使用される特別なデータ型です。Oracle ADF Business Components属性はオブジェクトである必要があり、プリミティブJava型は使用できません。属性には、java.lang.String
などの標準のJava型か、またはドメインと呼ばれる特別なOracle ADF BCコンポーネントを使用できます。Oracle型マップは、VARCHAR2以外のすべてのSQL型をデフォルトでドメインにマップします(VARCHAR2はString
にマップされます)。
ドメインには次の3つのタイプがあります。
事前定義済ドメイン: NUMBERやBLOBなどのSQL型用のラッパー。
検証ドメイン: 型レベルでビジネス・ロジックを実装する際に使用します。
Oracleオブジェクト型ドメイン: Oracleオブジェクト型のラッピング専用に使用します。
事前定義済ドメインはADF BCライブラリ内のJavaクラスで、JDBCクラスをラップします。ラップされたJDBCクラスは、NUMBER
などのSQLデータ型用のJavaラッパーとなります。このドメインは、String
などの標準のJavaクラスが適切でない場合に使用できます。
Oracleデータベースに対して使用する主なドメインは、すべてパッケージoracle.jbo.domain
にあります。次の表はその一覧です。
ドメイン | JDBCクラス |
---|---|
Array
|
oracle.sql.ARRAY
|
BFileDomain
|
oracle.sql.BFILE
|
BlobDomain
|
oracle.sql.BLOB
|
Char
|
oracle.sql.CHAR
|
ClobDomain
|
oracle.sql.CLOB
|
Date
|
oracle.sql.DATE
|
Number
|
oracle.sql.NUMBER
|
Raw
|
oracle.sql.RAW
|
RowID
|
oracle.sql.ROWID
|
Struct
|
oracle.sql.STRUCT
|
Timestamp
|
oracle.sql.TIMESTAMP
|
前述のドメインのうちChar
、Date
およびNumber
の3つは汎用ドメインで、JDBCのあらゆる実装で(つまりJDBC準拠のあらゆるデータベースで)使用できます。
oracle.jbo.domain
には、この他にDBSequence
ドメインも含まれます。このドメインはNumber
のように機能しますが、一時的な順序値をデータがポストされるまでメモリーに格納し、データベース順序番号が無駄に使用されないようにします。
oracle.jbo.domain
の他に、ADF BCライブラリにはパッケージoracle.ord.im
も含まれます。このパッケージには、マルチメディア・アプリケーション用に、Oracle interMediaの型とADF BCとの統合を可能にするドメインが含まれています。
Oracleオブジェクト型は、次のどちらかの方法で表すことができます。
Oracleオブジェクト型をオブジェクト表の型としてのみ使用する場合は、単純にエンティティ・オブジェクト定義を使用して表を表すことができます。エンティティ属性がオブジェクト型の列と一致します。
Oracleオブジェクト型をオブジェクト列の型として使用する場合は、オブジェクト型はカスタム・ドメインとして表されます。
Oracleオブジェクト型用のドメインには、オブジェクト型内の各列を表す属性があります。列値には、エンティティ・オブジェクト属性の場合と同様、getterメソッドおよびsetterメソッドを使用してアクセスします。
型レベルの検証を行う場合は、カスタム・ドメインを作成します。このドメインは、属性値として使用可能な他の型(たとえば事前定義済ドメインやString
などの標準Javaクラス)をラップします。作成したドメインは、他のデータ型のかわり使用できます。たとえば、Employeesというエンティティ・オブジェクト定義に、String
型のEmailという属性があるとします。この場合、String
をラップするEmailDomain
というドメインを作成し、これをEmailの型としてかわりに使用できます。
検証ドメインにはvalidate()
メソッドが含まれ、ドメインがインスタンス化される際に必ずコールされます。このメソッドには、テストを実行し、パスしなかった場合には例外をスローするコードを記述できます。この方法により、ドメインを型として使用するすべての属性に検証ロジックを追加できます。
ADFビュー・オブジェクト定義は、データソースからデータを収集し、クライアント向けにデータを処理し、クライアントがOracle ADF Business Componentsキャッシュ内でデータを変更できるようにするビジネス・コンポーネントです。たとえば、ビュー・オブジェクト定義では、次のタスクに必要な情報をすべて収集できます。
フォームへの単独の表要素の移入
挿入または編集フォームの作成と処理
ドロップダウン・リスト移入用のLOVの作成
ビュー・オブジェクト定義には、データソースからデータを取得するためのメカニズムが必要です。通常データソースはデータベースで、メカニズムはSQL問合せです。Oracle ADF Business Componentsでは、この問合せをデータベースに渡して結果を受け取るために、自動的にJDBCを使用できます。
ビュー・オブジェクト定義がSQL問合せを使用する場合、問合せ列はビュー・オブジェクト定義のビュー属性にマップします。これらの属性の定義は、これらの列のプロパティ(列のデータ型、および精度とスケールの仕様など)を反映します。ビュー・オブジェクト定義が他のデータソースを使用する場合、ビュー・オブジェクト属性は、プログラマの定義に従ってそのデータソースのデータの列にマップします。
ビュー・オブジェクト定義はビュー・オブジェクト・インスタンスのテンプレートで、ビュー・オブジェクト・インスタンスはデータの行の特定キャッシュを表します。別々のユーザーは必ず別々のビュー・オブジェクト・インスタンスを使用しますが、同じ問合せから別々に保持されたキャッシュが必要な場合には、同じユーザーでも複数のビュー・オブジェクト・インスタンスを使用できます。
ビュー・オブジェクト定義は、最大で次の4つのパーツで構成されます。
XMLファイル: ビュー・オブジェクト定義のうち、宣言的に開発可能な部分を表します。この情報は、ビュー・オブジェクトがデータソースからのデータの取得に使用するメカニズム(通常はSQL問合せ)、およびSQL問合せの列とエンティティ属性とのマッピング方法(実際のO/Rマッピングを扱う)で構成されます。多くのビュー・オブジェクト定義では、XMLファイルのみで十分です。
ビュー・オブジェクト・クラス: 個々のビュー・オブジェクト・インスタンスを表します。ビュー・オブジェクト・クラスを使用すると、問合せの複数の行に作用するカスタム・メソッドを記述できます。ビュー・オブジェクト・クラスはクラスoracle.jbo.server.ViewObjectImpl
を拡張します。カスタムのビュー・オブジェクト・メソッドが必要でない場合には、ビュー・オブジェクト・クラスを作成する必要はありません。ADFでは、oracle.jbo.server.ViewObjectImpl
をそのまま使用して問合せ結果セットのインスタンスを表すことが可能です。
ビュー行クラス: 問合せ結果の個々の行を表します。ビュー行クラスでは1行のデータに作用するカスタム・メソッドの記述が可能で、データを取得および変更するためのタイプセーフなアクセッサが提供されます。ビュー行クラスはクラスoracle.jbo.server.ViewRowImpl
を拡張します。カスタムの行レベル・メソッドまたはタイプセーフなアクセッサが必要でない場合には、ビュー行クラスを作成する必要はありません。ADFでは、ViewRowImpl
をそのまま使用してビュー行を表すことが可能です。
ビュー定義クラス: 問合せ自体を表します。ビュー定義クラスはXMLファイルに対するJavaラッパーの役割をします。このため、メタデータの特別な扱いが必要な場合(動的に変更する必要がある場合など)には、このコードをビュー定義クラスに追加できます。ビュー定義クラスはクラスoracle.jbo.server.ViewDefImpl
を拡張します。メタデータの特殊な扱いが必要でない場合には、ビュー定義クラスを作成する必要はありません。ADFでは、ViewDefImpl
をそのまま使用してメタデータをラップすることが可能です。
エンティティ属性同様、ビュー属性の値は、ViewRowImpl
クラスのメソッドgetAttribute()
およびsetAttribute()
を使用するか、またはカスタムのビュー行クラスで生成されたgetterおよびsetterを使用して、読込みまたは変更できます。
ビュー属性には次の2つのタイプがあり、アクセッサ・メソッドの動作がかなり異なります。
SQL専用ビュー属性: エンティティ属性にはマップされません。この属性の場合、アクセッサ・メソッドは、ビュー・オブジェクト・インスタンスのビュー行のキャッシュ内で、データに対する値の読込みと変更を行います。
エンティティ導出ビュー属性: エンティティ・オブジェクト定義の属性にマップされます。この属性の場合、アクセッサ・メソッドは、対応するエンティティ・オブジェクト・インスタンスに対してgetAttribute()
およびsetAttribute()
をコールします。データは、ビュー・オブジェクト・インスタンスのビュー行のキャッシュではなく、エンティティ・コレクションのエンティティ・オブジェクト・インスタンスのキャッシュ内で変更されます。
エンティティ・オブジェクト定義がDML操作を処理するため、データベースへの変更には、エンティティ導出ビュー属性を使用する必要があります。ただし、ビュー・オブジェクト定義をデータの取得にのみ使用する場合は、その属性をすべてSQL専用にするとメリットがあります。このようなビュー・オブジェクト定義はSQL専用ビュー・オブジェクト定義と呼ばれ、エンティティ・コレクションのキャッシュをすべて無視し、エンティティ・オブジェクト・インスタンスの作成に必要なオーバーヘッドを減らしリソースを節約します。
ビュー・オブジェクト・インスタンスは行イテレータです。具体的には、ビュー行の行イテレータです。
他の行イテレータ同様、ビュー・オブジェクト・インスタンスには1つの特定のビュー行をポイントする、現在行ポインタが含まれます。このポインタは移動可能で、イテレータからの行の抽出に使用できます。
行イテレータには、個々の行をナビゲートして抽出するための次のメソッドが含まれます。
next()
: 行イテレータ内の現在行ポインタを次に進め、その行を戻します。
hasNext()
: 行イテレータの現在行ポインタの後ろにも行があるかどうかをチェックします。next()
とhasNext()
を併用すると、イテレータ内の行を循環するループを作成できます。
first()
: 現在行ポインタを行イテレータ内の最初の行に移動し、その行を戻します。
last()
: 現在行ポインタを行イテレータ内の最後の行に移動し、その行を戻します。
previous()
: 行イテレータ内の現在行ポインタを1行前に戻し、その行を戻します。
hasPrevious()
: 行イテレータの現在行ポインタの前にも行があるかどうかをチェックします。previous()
とhasPrevious()
を併用すると、イテレータ内の行を逆方向に循環するループを作成できます。
ViewObjectImpl
には、行を作成するための次のメソッドも含まれます。
createRow()
: ビュー・オブジェクト定義に適切なビュー行を作成します。
insertRow()
: ビュー・キャッシュに行を挿入します。
削除する行をマーク付けするには、Row.remove()
またはViewObjectImpl.removeCurrentRow()
をコールします。
キーは、ビュー・オブジェクト・インスタンスの問合せ結果から、1つ以上の行を簡単に取得するための属性のセットです。主キーに基づく永続的ビュー・オブジェクト属性は自動的にビュー・オブジェクトのキーの一部となり、他の属性もビュー・オブジェクトのキーの一部にすることができます。
属性値の部分的または完全なリストを含む配列を使用して、型がoracle.jbo.Key
のオブジェクトを設定できます。次にこのオブジェクトをメソッドViewObjectImpl.findByKey()
に渡し、キー値と一致する行の配列を戻すことができます。
ビュー基準は構造化基準オブジェクトで、検索の作成に使用できます。
ビュー基準は、ビュー基準行のコレクションです。ビュー基準行は、1つ以上のビュー・オブジェクト属性に対して例による問合せ(QBE)要件を指定します。すべての要件を満たしたビュー行が一致します。
ビュー基準をビュー・オブジェクト・インスタンスに適用する場合、問合せは、ビュー基準行の少なくとも1つと一致するビュー行を戻すよう制限されます。したがって、ビュー基準は論理積標準形でWEHRE句を組み立てます。つまり、WHERE句はQBE要件の論理積の論理和です。
ビュー基準はoracle.jbo.ViewCriteria
クラスで、ビュー基準行はoracle.jbo.ViewCriteriaRow
クラスで実装します。
ビュー・オブジェクト定義間の関連はOracle ADFビュー・リンク定義で処理されます。ビュー・リンク定義は、2つのOracle ADFビュー・オブジェクト定義間の関連を、それぞれのエンティティ属性のセットに基づいて定義します。アソシエーション同様、関連は、外部キーに基づく単純な1対多のものから、複雑な多対多のものまで様々です。
ビュー・オブジェクトの個々のインスタンスも、ビュー・リンクの個々のインスタンスによって互いに関連付けることができます。その結果、問合せ結果セット間にマスター/ディテール関係が作成されます。たとえば、部門情報への問合せと従業員情報への問合せを表す2つのビュー・オブジェクト定義があり、部門と所属従業員との関連を表すビュー・オブジェクト間のビュー・リンクがあるとします。1つ目のビュー・オブジェクト定義のインスタンスallDepartments
が、ビュー・リンクのインスタンスによって、2つ目のビュー・オブジェクト定義のインスタンスemployeesInDepartment
に関連付けられている場合、この2つのインスタンスは同期化されます。その結果、allDepartments
の特定の行が選択されると、employeesInDepartment
には常にその行の詳細のみが表示されます。
2つのビュー・オブジェクト定義間にビュー・リンク定義を作成する際には、関連元のビュー・オブジェクト定義または関連先のビュー・オブジェクト定義、あるいはその両方にアクセッサ属性を追加できます。これらのアクセッサ属性は、次のようにアソシエーションに対するアクセッサ属性と似た動作をします。
属性名をViewObjectImpl.getAttribute()
に引数として渡すことができます。
ビュー行クラスを作成すると、アクセッサ属性用のgetterメソッドがそのクラスに含まれます。
アクセッサ・メソッドは、ビュー・リンク定義のカーディナリティに応じてビュー行または行イテレータを戻します。
アクセッサ属性が戻す行または行イテレータは静的で、同期化されたマスター/ディテール関係は保持されません。たとえば、DepartmentViewの行からEmployeesViewの行を戻す、DepartmentEmployeesというアクセッサ属性があるとします。この場合、DepartmentViewの現在行に対して次のコードを実行するとします。
RowIterator details = current.getAttribute("DepartmentEmployees");
その後でDepartmentViewの現在行が変更になったとします。details
内の行イテレータは変更されず、最初に取得した行のディテールが格納されたままです。
同期化されたマスター/ディテール関係を保持するには、アプリケーション・モジュール・インスタンスでビュー・リンク・インスタンスを使用してください。
Oracle ADFアプリケーション・モジュール定義は、特定のアプリケーション・タスクを表すビジネス・コンポーネントです。アプリケーション・モジュール定義は、タスクに必要なビュー・オブジェクトとビュー・リンクのインスタンスを集計して、タスク用のデータ・モデルを提供します。また、クライアントによるタスクの実行に役立つサービスも提供します。たとえば、あるアプリケーション・モジュールでは、次のようなタスクの表現とサポートが可能です。
顧客情報の更新
新規注文の作成
昇給の処理
アプリケーション・モジュールの最も重要な機能は、そのデータ・モデル(モジュールに含まれるビュー・オブジェクトおよびビュー・リンクのインスタンス)です。データ・モデルは、クライアントがアクセス可能なデータ、およびデータ内の関連を指定します。
アプリケーション・モジュール定義は、次の2種類の方法で使用できます。
サービス・オブジェクトとして使用: MVCアプリケーションの各インスタンスがアプリケーション・モジュールの1つのインスタンスにアクセスできます。このようなルート・レベルのアプリケーション・モジュール・インスタンスはADF BCトランザクション・オブジェクトを制御し、ADF BCトランザクション・オブジェクトはエンティティ・キャッシュおよびビュー・キャッシュを制御します。
ネストした利用可能オブジェクトとして使用: データ・モデルとサービス・メソッドを作成し、そのインスタンスの1つを他のアプリケーション・モジュール定義にネストできます。このようなアプリケーション・モジュール定義は、ネストされたモジュールのメソッドおよびデータ・モデルにアクセスできます。ネストされたアプリケーション・モジュールは、ルート・レベルのアプリケーション・モジュールのトランザクションを共有します。
アプリケーション・モジュール定義は、次の1つまたは2つのパーツで構成されます。
XMLファイル: アプリケーションのうち、宣言的に開発可能な部分を表します。具体的には、アプリケーション・モジュールに含まれるビュー・オブジェクトおよビュー・リンクのインスタンス、およびそれらの関連方法です。多くのアプリケーション・モジュールでは、XMLファイルのみで十分です。
アプリケーション・モジュール・クラス: カスタム・コード(MVCアプリケーションでバッチ・データ処理用に起動可能なサービス・メソッドなど)を記述できます。アプリケーション・モジュール・クラスはクラスoracle.jbo.server.ApplicationModuleImpl
を拡張します。カスタムのサービス・メソッドを記述する必要がない場合には、アプリケーション・モジュール・クラスを作成する必要はありません。ADFでは、oracle.jbo.server.ApplicationModuleImpl
をそのまま使用できます。
アプリケーション・モジュールの主要な機能の1つが、アプリケーションが特定のタスクを完了するために必要なデータの提供です。このデータはツリー(アプリケーション・モジュールのデータ・モデル)によって表され、ツリーにはビュー・オブジェクトとビュー・リンクのインスタンスが含まれます。
ビュー・オブジェクト・インスタンスは、取得されたデータのキャッシュを1つ管理します。ビュー・オブジェクト・インスタンスは、ビュー・オブジェクト定義で提供されるデータ取得メカニズム(通常はSQL問合せ)を使用します。ただし、このメカニズムはインスタンス・レベルでカスタマイズ可能です。つまり、あるビュー・オブジェクト・インスタンスの問合せで動的に句を追加または変更しても、他のビュー・オブジェクト・インスタンスに同様の変更が自動的に適用されることはありません。これは、インスタンス間で1つの定義を共有している場合も同様です。さらに、あるビュー・オブジェクト・インスタンスに対して問合せを実行しても、他のインスタンスの問合せが自動的に実行されることはありません。
すべてのビュー・オブジェクト・インスタンスは、そのインスタンスがデータ・モデルに最初に追加されたときに名前を割り当てられます。この名前は、クライアントおよびサービス・メソッドが、そのインスタンスおよびインスタンスのキャッシュに格納されたデータへアクセスする際に使用します。この名前は、ビュー・オブジェクト定義名と関連している必要はありません。たとえば、1つのデータ・モデルに、どちらもOrdersViewという名前のビュー・オブジェクト定義に基づく、AllOrdersおよびOrdersForCustomerという名前の2つのビュー・オブジェクト・インスタンスを含むことができます。
ビュー・リンク・インスタンスは、ビュー・オブジェクト・インスタンス間のマスター/ディテール関係を提供します。ビュー・リンク・インスタンスは、関連するビュー・オブジェクト定義を互いに関連付けるビュー・リンク定義に基づきます。
データ・モデルにビュー・リンク・インスタンスを追加すると、2つのビュー・オブジェクト・インスタンスはマスター/ディテール関係となり、ビュー・リンクを削除するとディテールのビュー・オブジェクト・インスタンスは完全に独立したものとなります。この方法では、ビュー・リンクのアクセッサ属性と異なり、マスター/ディテール関係が動的に保持されます。ディテールのビュー・オブジェクト・インスタンスのキャッシュにはマスターのビュー・オブジェクト・インスタンスの現在行のディテールである行のみが格納されます。現在行が変更になると、ディテール・キャッシュも自動的に変更されます。
トランザクション・オブジェクトは、データベース・トランザクションを表すOracle ADF Business Componentsオブジェクトです。トランザクション・オブジェクトはエンティティ・キャッシュやビュー・キャッシュへのポインタおよびデータベース接続を保持し、ポスト、コミットおよびロールバック操作を行います。
データベース・トランザクションと異なり、トランザクション・オブジェクトはコミットおよびロールバック操作後も存在します。このため、1つのトランザクション・オブジェクトは存続期間中いくつものデータベース・トランザクションに対応できます。
一般には、ルート・レベルの(ネストされていない)アプリケーション・モジュール・インスタンスごとに1つのトランザクション・オブジェクトを使用します。ルート・レベルのアプリケーション・モジュール・インスタンス、そのネストされたアプリケーション・モジュール、またはそのキャッシュ内のあらゆるエンティティ・オブジェクト・インスタンスからトランザクション・オブジェクトにアクセスした場合、すべて同じオブジェクトが取得されます。2つのルート・レベルのアプリケーション・モジュール・インスタンスからトランザクション・オブジェクトにアクセスした場合、異なるオブジェクトが取得されます。
サービス・メソッドは、データに対して複雑な操作を実行する、ADFアプリケーション・モジュール定義上のメソッドです。アプリケーションからはこのメソッドを1回のネットワーク・ラウンドトリップでコールできるため、クライアントでの処理を節約してネットワークの混雑を軽減できます。
サービス・メソッドはアプリケーション・モジュールのクラスに実装され、クライアントがアプリケーション・モジュールに(ローカルにデプロイされているかEJB Session Beanとしてデプロイされているかに関係なく)一貫してアクセスできる、層に依存しないインタフェース上で公開されます。サービス・メソッド内では、次の処理を実行できます。
データ・モデルへのビュー・オブジェクト・インスタンスとビュー・リンク・インスタンスの動的な追加
データ・モデルからのビュー・オブジェクト・インスタンスとビュー・リンク・インスタンスの削除
ビュー・オブジェクト・インスタンスの検索とその行セットに対する操作の実行
トランザクション・オブジェクトの取得と操作
アプリケーション・モジュール・プールは、トップレベルのアプリケーション・モジュール・インスタンス用のリソース・マネージャです。トランザクションおよびアプリケーション・モジュール・インスタンスに関連付けられたビュー・オブジェクトとエンティティ・オブジェクトのキャッシュを格納すると負荷がかかります。このためアプリケーション・モジュール・プールでは、一部のインスタンスをメモリーに格納し、他のインスタンスを再利用します。アプリケーション・モジュール定義ごとに1つのアプリケーション・モジュール・プールがあり、そのアプリケーション・モジュールのインスタンスはすべてこのプールに格納されます。
アプリケーションがあるアプリケーション・モジュール・インスタンスを使用している場合(Strutsデータ処理中など)、そのアプリケーション・モジュール・インスタンスは「チェック・アウトされている」と表現されます。アプリケーションがそのインスタンスの使用を終了した時点で、インスタンスはプールに「チェック・イン」されます。再びそのインスタンスが必要になると、アプリケーションは再度チェック・アウトを試みます。
アプリケーションが最初にあるアプリケーション・モジュール・インスタンスをリクエストすると、アプリケーション・モジュール・プールはその時点でプールにあるインスタンスの数をチェックします。この数がリサイクルしきい値と呼ばれるパラメータより少なければ、プールはアプリケーション用に新しいインスタンスを作成します。
アプリケーション・モジュールにリサイクルしきい値以上のインスタンスが含まれる場合、インスタンスの1つがリサイクルされます。リサイクルは次の手順で実行されます。
最も長くチェックインしているアプリケーション・モジュール・インスタンスがプールで検索されます。
プールは、そのインスタンスのトランザクションのREDOログを、データベース表PS_TXNに書き込みます。
インスタンスがそのキャッシュをクリアします。
新たにクリアされたキャッシュが、プールによってアプリケーションに渡されます。
デフォルトのリサイクルしきい値は10です。多くのアプリケーションでは、リサイクルしきい値を高くするとパフォーマンスが向上します。しきい値を設定する際には、メモリーに格納するデータの量(アプリケーション・サーバーのパフォーマンスに影響)とリサイクルの回数(リサイクルは時間がかかるため)とのバランスを検討します。このバランスを判断するには、ロード・テスターを使用して試行と修正を繰り返す方法をお薦めします。
ADF Business Componentsをビジネス・サービスとして使用する開発者には、アプリケーションのビジネス・ルールを実装するコードをどこに配置するか、ビュー・オブジェクト定義をエンティティ・オブジェクト定義ベースまたはSQL専用のどちらにするか、という2つの設計判断が求められます。
ビジネス・ルールは、アプリケーションの複数のレベル(データベース、ビューまたはビジネス・サービス・レイヤー)で提供できます。それぞれの場所には、それに適したケースがあります。
ビジネス・ルールをトリガーまたはストアド・プロシージャの形でデータベースに追加すると、最大の堅牢性を実現できます。このようなビジネス・ルールは常に使用可能で、SQL*Plusプロンプトから直接実行されるSQLコマンドを含む、あらゆるアプリケーションに遵守されます。ただし、データベースにコーディングされたビジネス・ルールの即応性はあまり高くありません。このようなルールはデータがデータベースへポストされるまで起動しないため、明示的なポスト・コマンドを待機するか、変更のたびにデータをポストする必要があります。これらは過剰なJDBCラウンドトリップを必要とするため、パフォーマンスの低下を招きます。また、ビジネス・ルールをデータベースに追加すると、データベースではデータの処理以外の操作を実行しなければならなくなり、データベースの効率およびアプリケーションのモジュール性の低下につながります。最後に、データベースにビジネス・ルールを追加するためには、JavaまたはWebのアプリケーションをPL/SQLコードで書かれたビジネス・ロジックと統合する必要があります。
ビジネス・ルールをJavaクライアント・アプリケーションの場合はJavaで、Webアプリケーションの場合はJavaScriptでビュー・レイヤーに追加すると、最大の即応性を実現できます。たとえばフィールドに文字が入力されるたび、またはマウス・ポインタがグラフィック・イメージの上に置かれたときにトリガーするビジネス・ルールは、ビュー・レベルで実装する必要があります。ただし、ビュー・レイヤーに追加されたビジネス・ルールは堅牢ではありません。ユーザーが他のユーザー・インタフェースからデータにアクセスした場合、ビュー・レイヤーに追加されたビジネス・ロジックは使用できないか、または強制されない結果となります。
ADF BCコンポーネントにビジネス・ルールを追加する方法は、これら2つの折衷案です。ADF BCコンポーネント内のビジネス・ルールは、メモリー内のJavaオブジェクトに変更が加えられた時点で強制されるため、データベース内にコーディングされたビジネス・ルールよりも即応性があります。また、データベースにビジネス・ルールを追加する場合のその他のデメリットも解消されます。一方でこの種のビジネス・ルールは、そのコンポーネントを使用するあらゆるアプリケーションで強制されるため、ビュー・レイヤーにコーディングされたビジネス・ルールよりも堅牢性があります。
最も重要なビジネス・ルールはデータベースに実装するか、またはデータベースとビジネス・サービスの両方に実装してください。後者の場合、即応性が向上し、また多少の作業で簡単にJavaと統合できます。真の即応性が求められるビジネス・ルールは、ビュー・レイヤーに実装する必要があります。残りのビジネス・ルールは、ADF Business Componentsに実装できます。ADF BCでは、この種のビジネス・ルールに対して、他のビジネス・サービスでは提供していない有益な関連付けを提供しています。
ADF BCコンポーネントにビジネス・ルールを実装する場合、通常はエンティティ・オブジェクト定義に実装します。エンティティ・オブジェクト定義はすべてのDML操作を実行するため、データベースに影響を及ぼす変更があった場合、エンティティ・オブジェクト定義内の該当するビジネス・ルールがトリガーされます。ビュー・オブジェクト定義に実装されたビジネス・ルールは、堅牢性が低くなります。他のビュー・オブジェクト定義を介して変更があった場合、たとえ同じエンティティ・オブジェクト定義に基づいていたとしても、ビジネス・ルールはトリガーされません。
この章の前半で説明したように、エンティティ・オブジェクト内にビジネス・ルールを配置する場合、配置場所にはさらに次の選択肢があります。
エンティティ・オブジェクト・クラス
Validator
ドメイン
検証ルール以外のビジネス・ルールの場合には、エンティティ・オブジェクト・クラスに配置する必要があります。検証ルールはこれらのどの場所にも配置できますが、それぞれに特徴があります。
エンティティ・オブジェクト・クラスに配置された検証コードは、他のエンティティ・オブジェクト定義で簡単に再利用できません。ただし、エンティティ・オブジェクト・クラスを編集してメソッドを追加するだけでよく、最初に検証コードを作成する方法としては最も簡単です。また、アソシエーションを横断する検証ロジックを配置できるのは、エンティティ・オブジェクト・クラスのみです。
Validatorは再利用性が非常に高く、複数のエンティティ・オブジェクトで、型の異なる属性に対して再利用でき、宣言的にカスタマイズできます。ただし、他の形式の検証ロジックに比べて最初の設定に手間がかかります。Validatorクラスを作成し、JbiValidator
インタフェースを実装し、プロパティ・エディタを作成し(Validatorのカスタマイズに使用する場合)、作成したValidatorをJDeveloperに登録する必要があります。
ドメインは、この2つの選択肢の折衷案です。検証ドメインを作成するためには、ドメイン・クラスの作成とコーディングが必要ですが、それでもValidatorを作成する場合に比べてはるかに簡単です。同時に、多数のエンティティ・オブジェクト定義内の多数の属性間で再利用可能です。ただし、属性はすべて同じ型に基づいている必要があります。
大規模なプロジェクトの場合、ビジネス・ルールの配置場所を1つにしなければならないケースもあります。ビジネス・ルールをエンティティ・オブジェクト、ドメインおよびValidatorに分散して配置すると、メンテナンスが非常に困難になるためです。
エンティティ・オブジェクト定義がすべてのDML操作を処理するため、あるビュー・オブジェクト定義からデータベースへの変更を可能にするには、そのビュー・オブジェクト定義をエンティティ・オブジェクト定義に基づいて作成する必要があります。ビュー・オブジェクト定義をエンティティ・オブジェクト定義に基づいて作成する場合、他にも次のメリットがあります。
ビュー・オブジェクト定義の問合せが結合問合せの場合(SELECT * FROM DEPARTMENTS, EMPLOYEES WHERE DEPARTMENTS.DEPARTMENT_ID=EMPLOYEES.DEPARTMENT_IDなど)、マスター表から取得された各行が問合せ結果セットのいくつもの行に表示されます。マスター表に対してエンティティ・オブジェクト定義を作成し、それをビュー・オブジェクト定義で使用すれば、マスター表の各行から取得されたデータは、エンティティ・キャッシュに1度だけ格納されます。エンティティ・オブジェクト定義を使用しない場合、データは問合せ結果セットの各行ごとに、ビュー・キャッシュに重複して格納されます。
各ビュー・オブジェクト・インスタンスには専用のビュー・キャッシュがあります。複数のビュー・オブジェクト・インスタンスが同じ表からのデータを問い合せた場合、エンティティ・オブジェクト定義を使用してその表を表していれば、データの格納は1度ですみます。エンティティ・オブジェクト定義を使用して表を表していない場合、データは各ビュー・キャッシュに格納される必要があります。
同じエンティティ属性にマップされた2つのエンティティ導出ビュー属性では、値が同期化されます。エンティティ導出属性に対し、1つのビュー行にsetAttribute()
をコールして属性値を変更し、もう1つのビュー行にgetAttribute()
をコールして同じエンティティ属性にマップされた属性の値を読み込んだ場合、getAttribute()
は変更された値を戻します。属性がSQL専用の場合には、このような同期化は行われません。
ビュー・オブジェクト定義でDML操作を実行する必要がある場合には、必ずエンティティ・オブジェクト定義をベースにしてください。また、ビュー・オブジェクト定義が前述のいずれかに該当する場合には、少なくともエンティティ・オブジェクト定義をベースにすることを検討してください。それ以外の場合、通常はビュー・オブジェクト定義はエンティティ・オブジェクト定義をベースにはしないでください。エンティティ・オブジェクト・インスタンスを作成する必要がないため、時間とリソースの節約になります。