Oracle E-Business Suite開発者ガイド リリース12.2 E53035-01 | 目次 | 前へ | 次へ |
Oracle E-Business Suiteで構築するアプリケーションの一部としてPL/SQLプロシージャを使用できます。コーディング標準に従うことによって、アプリケーションおよびOracle E-Business Suiteとシームレスに統合されるPL/SQLプロシージャを作成できます。
PL/SQLの使用目的は次のとおりです。
フォームおよびレポートへの迅速かつ容易なプロシージャ拡張の開発
開発を高速化し保守性を向上させるためのアプリケーション・コードのモジュール化
ネットワーク通信量を減少させ全体的なパフォーマンスを向上させるためのアプリケーション・コードの最適化
SQLへのOracleの手続き型言語拡張であるPL/SQLを使用して、Oracleのツールで作成したカスタム・フォームおよびレポートへのプロシージャ拡張を開発できます。
たとえば、Oracle E-Business Suiteの標準に従ったフォームを開発するには、フォーム・コードをPL/SQLビジネス・ルール・プロシージャ、項目ハンドラ、イベント・ハンドラおよび表ハンドラに編成します。非常に小さなPL/SQLコードをフォーム・トリガーに直接に入れるのは、それらのトリガーが論理モデルを表していないためです。それらのトリガーは、プロシージャ・コードをコールするためにOracle Formsが提供する、単なるイベント・ポイントです。パッケージされたPL/SQLプロシージャでコードを活用し、さらにトリガーからそれらのプロシージャをコールする場合には、開発と保守が容易なモジュール化フォーム・コードを作成します。
フォーム・コードのモジュール化に役立つPL/SQLプロシージャを作成することが可能です。たとえば、項目ハンドラ、イベント・ハンドラまたはビジネス・ルール・プロシージャは、実際にはより小さな複数のプロシージャから構成されます。これらのより小さなプロシージャを論理的なパッケージにグループ化すると、その目的が明確になります。(これらのより小さなプロシージャには特別な名称はありません。単にPL/SQLプロシージャと呼ばれます。)
また、PL/SQLを使用して、コンカレント・プログラム、あるいはコンカレント・プログラムからコールされるストアド・プロシージャを開発できます。一般に、Oracle E-Business Suiteの過去のリリースで即時コンカレント・プログラムとして開発したコンカレント・プログラムなら、PL/SQLコンカレント・プログラムとして開発することが可能です。または、コンカレント・プログラムの本体をC言語で開発することは可能ですが、コンカレント・プログラムにより発行されたSQL文はすべてPL/SQLストアド・プロシージャにカプセル化されます。
関連項目: PL/SQLストアド・プロシージャ
この章で使用される2つの用語の定義を次に示します。
「サーバー側」は、(データベース・サーバー上の) Oracleデータベースに格納されているPL/SQLプロシージャを記述するのに使用する用語です。データベースに格納されているプロシージャおよびファンクションはストアド・プロシージャおよびファンクションとも呼ばれ、データベース・サーバー側プロシージャとも呼ばれることがあります。
「クライアント側」は、Oracle Forms、Oracle Reportsおよびライブラリなど、データベースのクライアントであるプログラムで実行されるPL/SQLプロシージャを記述するのに使用する用語です。
このマニュアルの「クライアント側」という用語は、通常は(フォームが存在する)フォーム・サーバーを指します。このマニュアルの「クライアント側」は、Webブラウザを実行中のパーソナル・コンピュータまたはその他のデスクトップ・マシンを通常意味する「デスクトップ・クライアント」は指していません。
準拠する必要がある一般的な標準を次に示します。
PL/SQLプロシージャは、パッケージ内で常に定義されている必要があります。フォームまたはその他の論理的にグループ化されたコードのブロックごとにパッケージを作成します。
クライアント側の(Oracle Forms) PL/SQLプログラム・ユニットのソース・コードおよびコンパイル済コードは、あわせて64Kより小さくする必要があります。(プログラム・ユニットは、パッケージ仕様または本体またはスタンドアローン・プロシージャです。)つまり、プログラム・ユニットのソース・コードは10Kを超えることができないことを意味します。
パッケージが10Kの制限を超える場合、1つ以上の「プライベート・パッケージ」にプライベート変数およびプロシージャを置くことによってパッケージのサイズを減らすことができます。標準では、プライベート・パッケージにある変数およびプロシージャにアクセスするのは、元のパッケージのみです。個別のプロシージャがサイズ制限を超える場合は、コードを2つ以上のプロシージャに分割する必要があります。
Oracle Forms PL/SQLプロシージャが64Kの制限を超える場合は、生成時にOracle Formsがエラーを示します。
サーバー側のパッケージおよびプロシージャにはサイズ制限はありませんが、Oracle Formsがサーバー側のパッケージまたはプロシージャを参照する際に、サイズ制限のあるローカル・スタブが作成されます。パッケージ・スタブのサイズは、パッケージにあるプロシージャの数およびそれぞれのプロシージャが持つ引数の数とタイプに依存します。10Kの制限を超えないようにするためには、パッケージ内のプロシージャ数が25より小さくなるようにしてください。
既存のパッケージ(データベースまたはOracle Formsライブラリのいずれかに格納)に新規プロシージャまたはファンクションを追加する場合は、通常、パッケージ(およびパッケージ仕様)の最後に追加する必要があります。パッケージ仕様およびパッケージの途中に新規プロシージャを追加するには、パッケージを参照するすべてのフォームを再生成する必要があり、そうしない場合にはそれらのフォームにはORA-4062エラーが発生します。
必ずブロック名を含む完全なフィールド名を指定してください(つまり、FIELD_NAMEではなくBLOCK.FIELD_NAMEとする)。フィールド名のみを指定すると、Oracle Formsはフィールドを見つけるためにフォーム内の各ブロックに対してフィールドの全リストをスキャンし、その名前が曖昧で潜在的にフォームのパフォーマンスを低下させないかどうかをチェックする必要があります。ブロック名を含めると、Oracle Formsはそのブロック内のフィールドのみを検索し、一致するものを見つけると停止します。その上、ブロックをさらに追加したりすると、フィールド名を明確に指定したため既存のコードが機能し続けます。
フィールド名をプロシージャに渡し、IN OUTまたはOUTパラメータを使用するかわりにCOPYを使用してフィールド値を更新します。この方法により、プロシージャで実際に修正したかどうかに関係なくフィールドが変更済とマークされることがなくなります。OUTと宣言されたパラメータはすべて、プロシージャが正常に終了したときに書き出されます。
たとえば、test(my_var VARCHAR2 IN OUT)としてプロシージャを宣言しそれをtest(:block.field)としてコールするのでなく、プロシージャをtest(my_var VARCHAR2 IN)と宣言しそれをtest('block.field')としてコールします。
読みやすさを向上するためにパラメータ・リストが長い場合にはパラメータ名および値を「=>」で明示的に関連付けて、パラメータによって「切断」されることのないようにします。
パラメータのデフォルト値の宣言時には「:=」ではなくDEFAULTを使用します。値をデフォルト設定中のため、DEFAULTがより正確となります。コールするプロシージャは値を上書きできます。
逆に、不変の定数に対して値を宣言する際にはDEFAULTではなく「:=」を使用します。値をデフォルト設定ではなく割当中のため、「:=」を使用するとより正確になります。値は上書きできません。
SET_<OBJECT>_PROPERTYビルトイン(またはOracle Application Object Libraryと同等のもの)を使用してオブジェクトの複数のプロパティを変更するコードには、オブジェクトIDを使用する必要があります。まず適切なFIND_<OBJECT>ビルトインを使用してIDを取得してから、そのIDをSET_<OBJECT>_PROPERTYビルトインに渡します。
また、フォームの実行中に一度のみ取り出せばよいように、パッケージ・グローバルにIDを格納することも考慮する必要があります。
PL/SQLでNULL値を処理する際は注意を要します。たとえば、a := NULLかつb := NULLである場合、式(a = b)はFALSEと同値となります。片方の項がNULLである「=」式では、その式全体の値はFALSEとなります。
この理由から、値がNULLに等しいかどうかをチェックするには、かわりに演算子「is」を使用する必要があります。片方がNULLに等しい可能性のある2つの値を比較する場合は、次のように式を作成する必要があります:((a = b) or ((a is null) and (b is null))
Oracle Forms DeveloperとPL/SQLは、様々なタイプのグローバル変数をサポートしています。
Oracle Formsグローバル:フォームの「グローバル」疑似ブロックでの変数
PL/SQLパッケージ・グローバル: パッケージの仕様に定義されているグローバル
Oracle Formsパラメータ: Oracle Forms Designer内でパラメータとして作成された変数
これらの変数タイプの詳細説明は『Oracle Forms Reference Manual』を参照してください。次の表は各タイプの変数の特性をリストしており、作成するコードに最も適切なタイプの選択が可能です。
動作 | Oracle Formsグローバル | PL/SQLパッケージ・グローバル | Oracle Formsパラメータ |
---|---|---|---|
設計時に作成可能 | Y | Y | |
実行時に作成可能 | Y | ||
すべてのフォームからアクセス可能 | Y | ||
添付ライブラリからアクセス可能 | Y | (1) | Y |
特定のデータ型をサポート | (2) | Y | Y |
宣言デフォルトあり | Y | ||
間接的に参照可能 | Y | Y | |
コマンド行にて指定可能 | Y | ||
メモリーを回復するには削除する必要あり | Y | ||
すべてのOracle Formsコードで使用可能 | Y | Y |
(1) フォームで定義されたパッケージ変数は添付ライブラリからは参照できません。添付ライブラリで定義された変数はフォームから参照できます。(Oracle Formsグローバルは添付ライブラリから参照できます)
(2) 常にCHAR(255)になります。
パフォーマンスは、どのようなアプリケーションにおいても重要な側面です。典型的なクライアント/サーバー環境ではネットワークのラウンド・トリップは非常にコストがかかるものであるため、ラウンド・トリップの回数を最小化することが良好なパフォーマンスを確保する上でのかぎとなります。
PL/SQLプロシージャをサーバー側に置くかクライアント側に置くかは、ネットワークのラウンド・トリップの数が結果的に最も少なくなるように決定する必要があります。次のとおり、いくつかのガイドラインがあります。
Oracle Formsビルトイン(より一般的にはクライアント・ビルトイン)をコールするプロシージャは、クライアントに置く必要があります。
:block.fieldとして、またはNAME_IN/COPYによってのいずれかでフィールドを直接参照するプロシージャは、クライアントに置く必要があります。PL/SQLプロシージャへのパラメータとしてフィールド値または名前を直接受け取ることによってフィールドの参照を避けることができ、それによってコードのモジュール化も向上されます。
プロシージャに3つ以上のSQL文が含まれる場合、あるいは非常に複雑になる場合には、プロシージャは通常はサーバーに属します。
SQLを実行せずデータベースへのアクセスを必要としないプロシージャは、必要とされるどの場所にでも置くことができます。
プロシージャがサーバーからコールされる場合、プロシージャはサーバーに置く必要があります。プロシージャがクライアントとサーバーの両方からコールされる場合、プロシージャが非常に複雑で両方で保守するコストが大きくなりすぎないかぎり、両方の場所で定義する必要があります。両方で定義しない場合は、プロシージャはサーバーに置きます。
この項では、PL/SQLコードの書式についての推奨事項を説明します。
パッケージ内で、プライベート変数を先に定義してから、プライベート・プロシージャを定義し、最後にパブリック・プロシージャを定義します。
プロシージャまたはパッケージ名を「end」文に続けることでプロシージャを区別できるようにして、必ずプロシージャおよびパッケージを終了します。
コードを論理的にインデントします。空白2文字の増分値でインデントしていくことによって、ネストしている場合でも容易に追跡が可能です。
SQL文を次のようにインデントします。
例
DECLARE
CURSOR employees IS
SELECT empno
FROM emp
WHERE deptno = 10
AND ename IN ('WASHINGTON', 'MONROE')
AND mgr = 2701;
デバッグ中に「/* ... */」で広範囲のコードを簡単にコメント・アウトできるよう、「- -」を使用してコメントを開始します。
コメントされているコードに揃えてコメントをインデントします。
コードをコメントアウトするときは、一番左の列からコメント・デリミタを開始します。コードがもう必要でないことがはっきりとした時点で、そのコード全体を削除します。
大文字と小文字を使用して、コードの読みやすさを向上させます(PL/SQLは大文字小文字を区別しません)。ガイドラインとしては、予約語には大文字を使用し、その他すべてに小文字を使用することをお薦めします。
深くネストしたIF-THEN-ELSE条件制御は避けます。そのかわりにIF-THEN-ELSIFを使用します。
悪いスタイルの例
IF ... THEN ... ELSE
IF ... THEN ... ELSE
IF ... THEN ... ELSE
END IF
END IF
END IF;
良いスタイルの例
IF ... THEN ...
ELSIF ... THEN ...
ELSIF ... THEN ...
ELSIF ... THEN ...
ELSE ...
END IF;
トラップする必要のある特定の例外処理がある場合にのみ、ネストしたPL/SQLブロック(BEGIN/ENDのペア)をプロシージャ内に作成します。
例外処理では次に示すヒントを参照してください。
Oracle Forms PL/SQLで失敗が発生し、後続処理を停止する場合は、FND_MESSAGEを使用してエラー・メッセージを表示してから、RAISE FORM_TRIGGER_FAILUREで処理を停止します。
IF (error_condition) THEN
fnd_message.set_name(appl_short_name,
message_name);
fnd_message.error;
RAISE FORM_TRIGGER_FAILURE;
END IF;
RAISE FORM_TRIGGER_FAILUREはメッセージ等を表示することなく処理を停止することに注意してください。エラー通知がないため、メッセージを表示する場合は例外を実行する前に自分でFND_MESSAGEを使用して表示する必要があります。
関連項目: PL/SQLプロシージャのメッセージ・ディクショナリAPI
ストアド・プロシージャで失敗が発生し、後続処理を停止する場合は、パッケージ・プロシージャFND_MESSAGE.SET_NAMEを使用してメッセージを設定し、APP_EXCEPTION.RAISE_EXCEPTIONで処理を停止します。
IF (error_condition) THEN
fnd_message.set_name(appl_short_name,
message_name);
APP_EXCEPTION.RAISE_EXCEPTION;
END IF;
フォームでのプロシージャのコールは、このストアド・プロシージャ・エラーを処理するためには何もする必要はありません。フォームのON-ERRORトリガーのコードは、自動的にストアド・プロシージャを検出し、メッセージを取り出して表示します。
重要: パフォーマンス上の理由から、サーバー側パッケージは、no_rowsなど、予期しないすべてのリターンに対してreturn_codeを返すことが必要です。例外処理で処理する必要があるのは、予期しない例外のみです。
関連項目: PL/SQLプロシージャのメッセージ・ディクショナリAPI、TEMPLATEフォームでの特殊トリガーおよびAPP_EXCEPTION: 例外処理API
FORM_SUCCESS、FORM_FAILUREまたはFORM_FATALをテストする際は、その値が、ビルトインの結果として起動される別のトリガーのビルトインによって変更される場合があることに注意してください。たとえば、次のコードを考えてみます。
GO_ITEM('emp.empno');
IF FORM_FAILURE THEN
RAISE FORM_TRIGGER_FAILURE;
END IF;
GO_ITEMによって、WHEN-NEW-ITEM-INSTANCEなど、その他のトリガーが起動されます。GO_ITEMが失敗しても、起動する最後のトリガーが成功する、つまりFORM_FAILUREがFALSEとなる可能性があります。この問題を回避する例を次に示します。
GO_ITEM('EMP.EMPNO');
IF :SYSTEM.CURSOR_ITEM != 'EMP.EMPNO' THEN
-- No need to show an error, because Oracle Forms
-- must have already reported an error due to
-- some other condition that caused the GO_ITEM
-- to fail.
RAISE FORM_TRIGGER_FAILURE;
END IF;
各ビルトインの失敗を検出するその他の技法は、『Oracle Forms Reference Manual』を参照してください。
RAISE_APPLICATION_ERRORは使用しないでください。これは、サーバー側の例外を処理するスキームと衝突します。
関連項目: PL/SQLプロシージャのメッセージ・ディクショナリAPI
コードを作成するすべてのSQLで次のガイドラインに従ってください。
「select from SYS.DUAL」ではなく「select from DUAL」を使用します。SYSTEM.DUALは使用しないでください。
すべてのSELECT文には、明示的カーソルを使用する必要があります。暗黙的SELECT文は、実際には、データの取得およびTOO_MANY_ROWS例外のチェックの2つのフェッチを実行させます。これは、明示的カーソルから単一のレコードのみをフェッチすることで回避できます。
プロシージャ・パラメータでSELECTを行う場合は、パラメータがフィールドでないかぎり、パラメータ値を参照するかどうかに関係なく、IN OUTとしてパラメータを宣言します。
行を返さない単一行のSELECTは、例外NO_DATA_FOUNDを実行します。行に影響を与えないINSERT、UPDATEまたはDELETEは、例外を実行しません。エラーの行がない場合は、SQL%NOTFOUNDの値を明示的にチェックする必要があります。
NO_DATA_FOUND例外を処理するには、例外ハンドラを作成します。COUNT文をコーティングして行の有無を検出することは、それのみが目的でないかぎりは実行しないでください。
リテラルに対してフィールドまたはPL/SQL変数の値をチェックする際は、WHERE句ではなく、PL/SQLコードでチェックを行ってください。SQL実行をまとめて回避できる場合もあります。
データベースの整合性の問題ではエラーチェックは行わないでください。たとえば、正しいデータベースに表SYS.DUALがあり、その表に1行のみが存在している場合、SYS.DUALに1行のみがあるのか複数の行があるのか、あるいはSYS.DUALが存在するかどうかをチェックする必要はありません。
フォームでのトリガーでは次のとおりの一般的ルールに従います。
ブロックまたはフィールド・レベルのすべてのトリガーの実行スタイルは、「上書き」または「前」のいずれかである必要があります。トリガーのフォーム・レベルのバージョンも通常は開始する必要があるため、一般にはスタイルに「前」を使用します。例外は、フォーム・レベルのPOST-QUERYトリガーでフレックスフィールド・コールがあるものの、ブロック・レベルPOST-QUERYでブロックの問合せステータスをリセットしている場合です。その場合には、ブロック・レベルPOST-QUERYは、実行スタイルに「後」を使用する必要があります。
関連項目: TEMPLATEフォームでの特殊トリガー
すべてのKEYに対して「キーの表示」プロパティはTRUEに設定 - 無効にするコード(「キーの表示」をFALSEに設定)を除いたコードがトリガーされます。「キー説明の表示」プロパティは常にNULLに設定します。
WHEN-CREATE-RECORDトリガーは、ブロックが挿入を許していない場合でも開始します。このトリガーにロジックがあり、ブロックが挿入可能なFALSEを動的に持つ場合には、ブロックが挿入を許容するかどうかをチェックする必要がある場合があります。
IF GET_ITEM_PROPERTY('<BLOCK>', INSERT_ALLOWED) = FALSE THEN
null;
ELSE
<your logic here>;
END IF;
パーソナル・コンピュータ上では、同時に利用可能なリアルのウィジェットの数に制限があります(テキスト項目と表示項目は、Oracle Formsが作成するものであるので、リアルのWindowsウィジェットではありません)。
フォームにあるすべてのチェック・ボックス、リスト・項目およびオブジェクト・グループは、これらのリソースを消費します。リアルのウィジェットが非表示のキャンバス上にある場合、それが消費するリソースは解放されます。画面に表示されていないキャンバスを明示的に非表示にすることによって、リソースを解放できます。また、Oracle Forms DesignerにおいてFALSEの表示プロパティで設定されたキャンバスは、キャンバスを訪れるかキャンバスがプログラムから表示されないかぎり、それ自身またはそのウィジェットのためのリソースを消費しません。
Oracle Formsはスタートアップ時に最初の入力可能項目にナビゲートし、First Navigation Blockに対してキャンバスとそのすべてのウィジェットを作成することに注意してください。
なんらかのアクションを実行する前にMS Windowsリソースの可用性をチェックするには、次のユーティリティを使用します。
if get_application_property(USER_INTERFACE) =
'MSWINDOWS' then
if (FND_UTILITIES.RESOURCES_LOW) then
FND_MESSAGE.SET_NAME('FND', 'RESOURCES_LOW');
if (FND_MESSAGE.QUESTION('Do Not Open', 'Open',
'', 1) =1) then
raise FORM_TRIGGER_FAILURE;
end if;
end if;
end if;
これらの標準は、一定のビルトインを完全に避けたり、その場所に「ラッパー」ルーチンをコールすることを必要とします。多くのビルトインに、複数の起動方法があります。ビルトインを直接コールして、標準フォーム動作を提供できます。ビルトインによっては、APP_STANDARD.EVENTをコールして起動する標準のOracle E-Business Suite動作があります。
これらのビルトインの多くに、キーおよびそれに関連付けられたKEY-トリガーがあります。利用対象のKEY-トリガーに追加ロジックがある場合には、DO_KEYビルトインを使用してトリガーを起動できます。これは、関連付けられたキーをユーザーが押した場合と同じ結果となります。
DO_KEYビルトインは定期的に使用する必要があります。KEY-トリガーをバイパスする理由があるとすれば、開始する追加のコードを開始する必要がある場合です。
次に示すOracle Formsビルトインは使用しないでください。
変数 | 説明 |
---|---|
CALL_FORM | このビルトインは、Oracle E-Business Suiteルーチンで使用されるOPEN_FORMとは互換性がありません。 フォームをプログラムからオープンする必要があるときは、CALL_FORMまたはOPEN_FORMのいずれかではなくFND_FUNCTION.EXECUTEを必ず使用します。FND_FUNCTION.EXECUTEを使用すると、Oracle E-Business Suiteセキュリティをバイパスすることなくフォームをオープンすることが可能になり、フォームの正しいディレクトリ・パスの検索も可能になります。 関連項目: PL/SQLプロシージャの機能セキュリティAPI |
これらのOracle Formsビルトインには、追加機能を提供する同等のAPPCOREルーチンがあります。
変数 | 説明 |
---|---|
EXIT_FORM | Oracle E-Business Suiteフォームには、特別の終了処理があります。EXIT_FORMを直接コールしないでください。必ずdo_key('EXIT_FORM')をコールしてください。 Oracle E-Business Suiteを完全に終了するには、まず次をコールします。
続いて次をコールします。
|
SET_ITEM_ PROPERTY | APP_ITEM_PROPERTY.SET_ PROPERTYおよびAPP_ITEM_PROPERTY.SET_ VISUAL_ATTRIBUTEで置換します。これらのAPPCOREルーチンは、Oracle E-Business Suiteの標準的な方法でプロパティを設定し、伝播の動作を変更します。プロパティによっては、Oracle Formsシステム固有のSET_ITEM_PROPERTYを使用するものもあります。APP_ITEM_PROPERTY.SET_ PROPERTYがカバーしているプロパティの網羅的なリストは、そのルーチンのドキュメントを参照ください。 関連項目: APP_ITEM_PROPERTY: 個別プロパティ・ユーティリティ |
GET_ITEM_ PROPERTY | Oracle E-Business Suiteに固有のプロパティを取得する際は、APP_ITEM_PROPERTY.GET_PROPERTYを使用します。その他のプロパティの設定または取得にはOracle Formsビルトインを使用します。 |
OPEN_FORM | FND_FUNCTION.EXECUTEを使用します。このルーチンは、機能セキュリティのために必要です。 OPEN_FORMとFND_ FUNCTION.EXECUTEの両方とも、POST-RECORDおよびPOST-BLOCKトリガーを開始させます。 |
CLEAR_FORM | do_key('clear_form')を使用します。無効なレコードがある場合には、このルーチンが例外FORM_TRIGGER_FAILUREを発生させます。 「do_key」抜きでこのビルトインを使用することによって、トリガーの実行から生じる追加機能を避けることが可能になります。 |
COMMIT | do_key('commit_form')を使用します。無効なレコードがある場合には、このルーチンが例外FORM_TRIGGER_FAILUREを発生させます。 「do_key」抜きでこのビルトインを使用することによって、トリガーの実行から生じる追加機能を避けることが可能になります。 |
EDIT_FIELD/ EDIT_ TEXTITEM | do_key('edit_field')を使用します。現在の項目が日付である場合に、このルーチンがカレンダを起動します。 「do_key」抜きでこのビルトインを使用することによって、トリガーの実行から生じる追加機能を避けることが可能になります。 |
VALIDATE | かわりにAPP_STANDARD.APP_VALIDATEを使用します。このルーチンはナビゲーションの失敗をもたらすすべての項目にナビゲートします。 「do_key」抜きでこのビルトインを使用することによって、トリガーの実行から生じる追加機能を避けることが可能になります。 警告: APP_STANDARD.APP_VALIDATEを使用するためには、ボタンのコーディング標準に準拠する必要があります。 関連項目: APP_STANDARDパッケージおよびボタン |
開発者は、項目を検証するために必要なすべてのコードを実行するため、または具体的な状況における正しい動作を確保するために、トリガーからハンドラをコールします。
ハンドラは、読込みおよび作業が容易になるように、コードを集中するのに使用されます。典型的なフォームは、ブロックごとのパッケージおよびフォームそのもののパッケージを持っています。これらのパッケージ内にプロシージャのコードを置き、関連付けられたトリガーからプロシージャ(ハンドラ)を呼びます。ハンドルが複数のブロック、またはフォーム・レベル・トリガーへの応答を伴う場合には、ハンドラをフォーム・パッケージに置きます。
項目ハンドラ、イベント・ハンドラ、表ハンドラなど様々な種類のコードに対して、様々な種類のプロシージャが存在します。ほとんどのコードはこれらのプロシージャに存在しており、プロシージャへのコール以外では、トリガーにあるコードは最小限にする必要があります。
項目ハンドラは、特定の項目の検証に使用されるすべてのロジックを含んでいるプロシージャです。項目ハンドラ・パッケージには、ブロックまたはフォームにおいて項目を検証するためのすべてのプロシージャが含まれています。
このパッケージには通常はブロックやフォームに対応する名前が付けられており、プロシージャには個別の項目名に対応する名前が付けられています。たとえばEMPブロックは、EMPNO、ENAME、JOBという項目を含みますが、対応するEMPパッケージのプロシージャにもEMPNO、ENAME、JOBの名前がそれぞれ付けられており、特定の項目に関連付けられたコードを容易に検出できます。
項目ハンドラは常にEVENTと命名された1つのパラメータを取り、タイプVARCHAR2は通常は項目ハンドラをコールするトリガーの名前となります。
共通のイベント・ポイントと関連ロジックは次に示すとおりです。
変数 | 説明 |
---|---|
PRE-RECORD | 新しいレコードの項目属性をリセットします。通常は依存フィールドの有効と無効を切り替えるAPPCOREルーチンで使用されます。制限されたOracle Formsビルトイン・ルーチンを使用する必要があるか、またはナビゲーションやコミットを実行する必要がある場合は、WHEN-NEW-RECORD-INSTANCEが使用できます。 |
INIT | 項目を初期化します。 |
VALIDATE | 項目を検証し、動的な項目属性を設定します。 |
INITは「初期化(Initialize)」の短縮名であり、項目ハンドラが項目を初期化するディレクティブです。INITは、項目ハンドラにフォームの現在の状態を検査するように指示し、必要に応じてその項目のデフォルト値および動的属性をリセットします。このイベントはその他のハンドラによって渡され、多くのAPPCOREルーチンが待ち受けています。
最も一般的なケースは、別の項目に依存している項目の場合です。マスターの項目ハンドラのWHEN-VALIDATE-ITEMで、マスター項目が変更されるたびに、従属する項目ハンドラがINITイベントでコールされます。
マスター項目の条件が変更される場合、通常はイベントをその他の従属項目にカスケードする必要があります。
この疑似イベントは、項目を検証する必要のある、多くのAPPCOREルーチンで使用されます。WHEN-VALIDATE-ITEM、WHEN-CHECKBOX-CHANGED、WHEN-LIST-CHANGEDまたはWHEN-RADIO- CHANGED (このいずれかを使用することも可能)のかわりに、このイベントを使用します。VALIDATEイベントまたはトリガー名のいずれかを予期する、独自の項目ハンドラ・ルーチンを作成できます。
典型的な項目ハンドラは、次のような内容です。
procedure ITEM_NAME(event VARCHAR2) IS
IF (event = 'WHEN-VALIDATE-ITEM') THEN
-- validate the item
ELSIF (event = 'INIT') THEN
-- initialize this dependent item
ELSIF (event in ('PRE-RECORD', 'POST-QUERY')) THEN
-- etc.
ELSE fnd_message.debug('Invalid event passed to item_name: ' || EVENT);
END IF;
END ITEM_NAME;
ヒント: 項目ハンドラを作成することがプロセスの全体ではないことに注意してください。また、プロシージャが処理するイベントごとにトリガーをコーディングし、項目ハンドラをコールする必要があります。コーディングした内容で動作しない場合、まずチェックするのは、新規の項目ハンドラをコールするトリガーをコーディングしたかどうかということです。
イベント・ハンドラは、個別の項目の動作にでなくイベントに関してコードを集中化する方が容易である場合、複数項目に付随するロジックをカプセル化します。開発者は、どの場合に項目ハンドラのセットより、イベント・ハンドラが読み込みやすくなるかを決定します。
非常に複雑な項目間の動作がイベント・ハンドラに属し、一方、非常にシンプルな単位の項目の動作は項目ハンドラに属します。イベント・ハンドラから項目ハンドラをコールできます。
たとえば、POST-QUERYで多くの項目に値を移入するように、イベント・ハンドラのコードを記述することが可能です。項目ごとに項目ハンドラを作成するより、すべてのロジックを単一のイベント・ハンドラにカプセル化できます。
イベント・ハンドラはイベントを1つのみ処理するものであるため、EVENTパラメータは必要ありません。実際に、イベント・ハンドラはパラメータを一切受け付けません。
イベント・ハンドラは、トリガーから命名され、ダッシュをアンダースコアに置換します(たとえば、PRE-QUERYイベント・ハンドラはPRE_QUERYとなります)。
変数 | 説明 |
---|---|
PRE_QUERY | 適切なレコードを検索するのに必要な値を項目に移入します。 |
POST_QUERY | 基礎表でない項目に値を移入します。 |
WHEN_CREATE _RECORD | デフォルト値を移入します(デフォルト値プロパティの使用が不十分な場合) |
WHEN_ VALIDATE_ RECORD | 複雑な項目間の関連を検証します |
表ハンドラは、APIを表に与えるサーバー側またはクライアント側のパッケージです。表ハンドラは、レコードを挿入、更新、削除またはロックするため、あるいは別の表にあるレコードがこの表にあるレコードを参照しているかどうかをチェックするために使用されます。
Oracle E-Business Suiteでのフォームのほとんどはビューに基づいているため、これらの表ハンドラはビューの下で表との相互作用を処理するのに必要となります。
警告: ブロックが複数表のビューに基づいている場合、ブロック「キー・モード」をデフォルト値の「一意キー」から「更新不可キー」に変更します。項目のプロパティ・シートで「主キー」をTRUEに設定して、主キー項目を指定します。
表ハンドラには、次のプロシージャのいくつかまたはすべてが含まれます。
変数 | 説明 |
---|---|
CHECK_ UNIQUE | 一意の列で値が重複していないかチェックします。 |
CHECK_ REFERENCES | 参照整合性をチェックします。 |
INSERT_ROW | 表に行を挿入します。 |
UPDATE_ROW | 表にある行を更新します。 |
DELETE_ROW | 表から行を削除します。 |
LOCK_ROW | 表にある行をロックします。 |
INSERT_ROW、UPDATE_ROW、DELETE_ROWおよびLOCK_ROWは、通常、ON-INSERT、ON-UPDATE、ON-DELETEおよびON-LOCKトリガーにおいてデフォルトのOracle Formsトランザクション処理を置換するのに使用されます。
INSERT_ROW表ハンドラ・プロシージャにおいて、主キー列でNULLが許可されている場合には、SELECT ROWID文のWHERE句に「OR (primary_key IS NULL AND X_col IS NULL)」を必ず追加してください。
LOCK_ROW表ハンドラ・プロシージャで、列にNULLが許されていない場合は、IF文から「OR (RECINFO.col IS NULL AND X_col IS NULL」条件を削除してください。
また、Oracle Formsは問合せを受けたフィールドから後続ブランクを取り去るため、通常の行ロックは比較の前にデータベース値から後続ブランクは取り去られています。例でのLOCK_ROWストアド・プロシージャは後続ブランクを取り去らないため、この(まれな)場合の比較は常に失敗します。必要に応じRTRIMを使用して後続ブランクを取り去ることが可能です。
別の表でアクションを実行するには、アクションを直接に実行するのでなく、その表の適切なハンドラ・プロシージャをコールします。
たとえば、カスケードDELETEを実行するには、マスター表のDELETE_ROW表ハンドラにおいて直接DELETEを実行するかわりに、詳細表の(マスター主キーをパラメータとして受け取る) DELETE_ROWS表ハンドラをコールします。
次に示すのは、EMP表に対してINSERT_ROW、UPDATE_ROW、DELETE_ROWおよびLOCK_ROWプロシージャを提供するクライアント側の表ハンドラの例です。フォームに直接にクライアント側の表ハンドラをコーディングします。
PACKAGE EMP IS
PROCEDURE Insert_Row;
PROCEDURE Lock_Row;
PROCEDURE Update_Row;
PROCEDURE Delete_Row;
END EMP;
PACKAGE BODY EMP IS
PROCEDURE Insert_Row IS
CURSOR C IS SELECT rowid FROM EMP
WHERE empno = :EMP.Empno;
BEGIN
INSERT INTO EMP(
empno,
ename,
job,
mgr,
hiredate,
sal,
comm,
deptno
) VALUES (
:EMP.Empno,
:EMP.Ename,
:EMP.Job,
:EMP.Mgr,
:EMP.Hiredate,
:EMP.Sal,
:EMP.Comm,
:EMP.Deptno
);
OPEN C;
FETCH C INTO :EMP.Row_Id;
if (C%NOTFOUND) then
CLOSE C;
Raise NO_DATA_FOUND;
end if;
CLOSE C;
END Insert_Row;
PROCEDURE Lock_Row IS
Counter NUMBER;
CURSOR C IS
SELECT empno,
ename,
job,
mgr,
hiredate,
sal,
comm,
deptno
FROM EMP
WHERE rowid = :EMP.Row_Id
FOR UPDATE of Empno NOWAIT;
Recinfo C%ROWTYPE;
BEGIN
Counter := 0;
LOOP
BEGIN
Counter := Counter + 1;
OPEN C;
FETCH C INTO Recinfo;
if (C%NOTFOUND) then
CLOSE C;
FND_MESSAGE.Set_Name('FND',
'FORM_RECORD_DELETED');
FND_MESSAGE.Error;
Raise FORM_TRIGGER_FAILURE;
end if;
CLOSE C;
if (
(Recinfo.empno = :EMP.Empno)
AND ( (Recinfo.ename = :EMP.Ename)
OR ( (Recinfo.ename IS NULL)
AND (:EMP.Ename IS NULL)))
AND ( (Recinfo.job = :EMP.Job)
OR ( (Recinfo.job IS NULL)
AND (:EMP.Job IS NULL)))
AND ( (Recinfo.mgr = :EMP.Mgr)
OR ( (Recinfo.mgr IS NULL)
AND (:EMP.Mgr IS NULL)))
AND ( (Recinfo.hiredate = :EMP.Hiredate)
OR ( (Recinfo.hiredate IS NULL)
AND (:EMP.Hiredate IS NULL)))
AND ( (Recinfo.sal = :EMP.Sal)
OR ( (Recinfo.sal IS NULL)
AND (:EMP.Sal IS NULL)))
AND ( (Recinfo.comm = :EMP.Comm)
OR ( (Recinfo.comm IS NULL)
AND (:EMP.Comm IS NULL)))
AND (Recinfo.deptno = :EMP.Deptno)
) then
return;
else
FND_MESSAGE.Set_Name('FND',
'FORM_RECORD_CHANGED');
FND_MESSAGE.Error;
Raise FORM_TRIGGER_FAILURE;
end if;
EXCEPTION
When APP_EXCEPTIONS.RECORD_LOCK_EXCEPTION then
IF (C% ISOPEN) THEN
close C;
END IF;
APP_EXCEPTION.Record_Lock_Error(Counter);
END;
end LOOP;
END Lock_Row;
PROCEDURE Update_Row IS
BEGIN
UPDATE EMP
SET
empno = :EMP.Empno,
ename = :EMP.Ename,
job = :EMP.Job,
mgr = :EMP.Mgr,
hiredate = :EMP.Hiredate,
sal = :EMP.Sal,
comm = :EMP.Comm,
deptno = :EMP.Deptno
WHERE rowid = :EMP.Row_Id;
if (SQL%NOTFOUND) then
Raise NO_DATA_FOUND;
end if;
END Update_Row;
PROCEDURE Delete_Row IS
BEGIN
DELETE FROM EMP
WHERE rowid = :EMP.Row_Id;
if (SQL%NOTFOUND) then
Raise NO_DATA_FOUND;
end if;
END Delete_Row;
END EMP;
次に示すのは、EMP表に対してINSERT_ROW、UPDATE_ROW、DELETE_ROWおよびLOCK_ROWプロシージャを提供するサーバー側の表ハンドラの例です。ハンドラは、フォームのパッケージおよびデータベースのサーバー側パッケージから構成されます。フォームのパッケージは、サーバー側パッケージをコールし、引数としてすべてのフィールド値を渡します。
PACKAGE EMP IS
PROCEDURE Insert_Row;
PROCEDURE Update_Row;
PROCEDURE Lock_Row;
PROCEDURE Delete_Row;
END EMP;
PACKAGE BODY EMP IS
PROCEDURE Insert_Row IS
BEGIN
EMP_PKG.Insert_Row(
X_Rowid => :EMP.Row_Id,
X_Empno => :EMP.Empno,
X_Ename => :EMP.Ename,
X_Job => :EMP.Job,
X_Mgr => :EMP.Mgr,
X_Hiredate => :EMP.Hiredate,
X_Sal => :EMP.Sal,
X_Comm => :EMP.Comm,
X_Deptno => :EMP.Deptno);
END Insert_Row;
PROCEDURE Update_Row IS
BEGIN
EMP_PKG.Update_Row(
X_Rowid => :EMP.Row_Id,
X_Empno => :EMP.Empno,
X_Ename => :EMP.Ename,
X_Job => :EMP.Job,
X_Mgr => :EMP.Mgr,
X_Hiredate => :EMP.Hiredate,
X_Sal => :EMP.Sal,
X_Comm => :EMP.Comm,
X_Deptno => :EMP.Deptno);
END Update_Row;
PROCEDURE Delete_Row IS
BEGIN
EMP_PKG.Delete_Row(:EMP.Row_Id);
END Delete_Row;
PROCEDURE Lock_Row IS
Counter Number;
BEGIN
Counter := 0;
LOOP
BEGIN
Counter := Counter + 1;
EMP_PKG.Lock_Row(
X_Rowid => :EMP.Row_Id,
X_Empno => :EMP.Empno,
X_Ename => :EMP.Ename,
X_Job => :EMP.Job,
X_Mgr => :EMP.Mgr,
X_Hiredate => :EMP.Hiredate,
X_Sal => :EMP.Sal,
X_Comm => :EMP.Comm,
X_Deptno => :EMP.Deptno);
return;
EXCEPTION
When APP_EXCEPTIONS.RECORD_LOCK_EXCEPTION then
APP_EXCEPTION.Record_Lock_Error(Counter);
END;
end LOOP;
END Lock_Row;
END EMP;
SET VERIFY OFF
DEFINE PACKAGE_NAME="EMP_PKG"
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
CREATE or REPLACE PACKAGE &PACKAGE_NAME as
/* Put any header information (such as $Header$) here.
It must be written within the package definition so that the
header information will be available in the package itself.
This makes it easier to identify package versions during
upgrades. */
PROCEDURE Insert_Row(X_Rowid IN OUT VARCHAR2,
X_Empno NUMBER,
X_Ename VARCHAR2,
X_Job VARCHAR2,
X_Mgr NUMBER,
X_Hiredate DATE,
X_Sal NUMBER,
X_Comm NUMBER,
X_Deptno NUMBER
);
PROCEDURE Lock_Row(X_Rowid VARCHAR2,
X_Empno NUMBER,
X_Ename VARCHAR2,
X_Job VARCHAR2,
X_Mgr NUMBER,
X_Hiredate DATE,
X_Sal NUMBER,
X_Comm NUMBER,
X_Deptno NUMBER
);
PROCEDURE Update_Row(X_Rowid VARCHAR2,
X_Empno NUMBER,
X_Ename VARCHAR2,
X_Job VARCHAR2,
X_Mgr NUMBER,
X_Hiredate DATE,
X_Sal NUMBER,
X_Comm NUMBER,
X_Deptno NUMBER
);
PROCEDURE Delete_Row(X_Rowid VARCHAR2);
END &PACKAGE_NAME;
/
show errors package &PACKAGE_NAME
SELECT to_date('SQLERROR') FROM user_errors
WHERE name = '&PACKAGE_NAME'
AND type = 'PACKAGE'
/
commit;
exit;
SET VERIFY OFF
DEFINE PACKAGE_NAME="EMP_PKG"
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
CREATE or REPLACE PACKAGE BODY &PACKAGE_NAME as
/* Put any header information (such as $Header$) here.
It must be written within the package definition so the
header information is available in the package itself.
This makes it easier to identify package versions during
upgrades. */
PROCEDURE Insert_Row(X_Rowid IN OUT VARCHAR2,
X_Empno NUMBER,
X_Ename VARCHAR2,
X_Job VARCHAR2,
X_Mgr NUMBER,
X_Hiredate DATE,
X_Sal NUMBER,
X_Comm NUMBER,
X_Deptno NUMBER
) IS
CURSOR C IS SELECT rowid FROM emp
WHERE empno = X_Empno;
BEGIN
INSERT INTO emp(
empno,
ename,
job,
mgr,
hiredate,
sal,
comm,
deptno
) VALUES (
X_Empno,
X_Ename,
X_Job,
X_Mgr,
X_Hiredate,
X_Sal,
X_Comm,
X_Deptno
);
OPEN C;
FETCH C INTO X_Rowid;
if (C%NOTFOUND) then
CLOSE C;
Raise NO_DATA_FOUND;
end if;
CLOSE C;
END Insert_Row;
PROCEDURE Lock_Row(X_Rowid VARCHAR2,
X_Empno NUMBER,
X_Ename VARCHAR2,
X_Job VARCHAR2,
X_Mgr NUMBER,
X_Hiredate DATE,
X_Sal NUMBER,
X_Comm NUMBER,
X_Deptno NUMBER
) IS
CURSOR C IS
SELECT *
FROM emp
WHERE rowid = X_Rowid
FOR UPDATE of Empno NOWAIT;
Recinfo C%ROWTYPE;
BEGIN
OPEN C;
FETCH C INTO Recinfo;
if (C%NOTFOUND) then
CLOSE C;
FND_MESSAGE.Set_Name('FND', 'FORM_RECORD_DELETED');
APP_EXCEPTION.Raise_Exception;
end if;
CLOSE C;
if (
(Recinfo.empno = X_Empno)
AND ( (Recinfo.ename = X_Ename)
OR ( (Recinfo.ename IS NULL)
AND (X_Ename IS NULL)))
AND ( (Recinfo.job = X_Job)
OR ( (Recinfo.job IS NULL)
AND (X_Job IS NULL)))
AND ( (Recinfo.mgr = X_Mgr)
OR ( (Recinfo.mgr IS NULL)
AND (X_Mgr IS NULL)))
AND ( (Recinfo.hiredate = X_Hiredate)
OR ( (Recinfo.hiredate IS NULL)
AND (X_Hiredate IS NULL)))
AND ( (Recinfo.sal = X_Sal)
OR ( (Recinfo.sal IS NULL)
AND (X_Sal IS NULL)))
AND ( (Recinfo.comm = X_Comm)
OR ( (Recinfo.comm IS NULL)
AND (X_Comm IS NULL)))
AND (Recinfo.deptno = X_Deptno)
) then
return;
else
FND_MESSAGE.Set_Name('FND', 'FORM_RECORD_CHANGED');
APP_EXCEPTION.Raise_Exception;
end if;
END Lock_Row;
PROCEDURE Update_Row(X_Rowid VARCHAR2,
X_Empno NUMBER,
X_Ename VARCHAR2,
X_Job VARCHAR2,
X_Mgr NUMBER,
X_Hiredate DATE,
X_Sal NUMBER,
X_Comm NUMBER,
X_Deptno NUMBER
) IS
BEGIN
UPDATE emp
SET
empno = X_Empno,
ename = X_Ename,
job = X_Job,
mgr = X_Mgr,
hiredate = X_Hiredate,
sal = X_Sal,
comm = X_Comm,
deptno = X_Deptno
WHERE rowid = X_Rowid;
if (SQL%NOTFOUND) then
Raise NO_DATA_FOUND;
end if;
END Update_Row;
PROCEDURE Delete_Row(X_Rowid VARCHAR2) IS
BEGIN
DELETE FROM emp
WHERE rowid = X_Rowid;
if (SQL%NOTFOUND) then
Raise NO_DATA_FOUND;
end if;
END Delete_Row;
END &PACKAGE_NAME;
/
show errors package body &PACKAGE_NAME
SELECT to_date('SQLERROR') FROM user_errors
WHERE name = '&PACKAGE_NAME'
AND type = 'PACKAGE BODY'
/
commit;
exit;
Copyright © 1995, 2013, Oracle and/or its affiliates. All rights reserved.