この章では、互いに関連するPL/SQLコードとデータを1つのパッケージにまとめる方法について説明します。 パッケージをコンパイルしてデータベースに格納し、その内容を複数のアプリケーションで共有できます。
ここでのトピック:
パッケージとは、論理的に関連するPL/SQLの型、変数およびサブプログラムをグループにまとめたスキーマ・オブジェクトのことです。 通常、パッケージは仕様部と本体の2つの部分で構成されますが、本体が不要な場合もあります。
仕様部はパッケージへのインタフェースです。 ここでは、型、変数、定数、例外、カーソル、およびパッケージの外から参照できるサブプログラムを宣言します。 本体は、カーソルの問合せとサブプログラムのコードを定義します。
仕様部はインタフェース、本体はブラック・ボックスと考えることができます。 パッケージの仕様部を変更しなくても、本体をデバッグ、拡張または置換できます。
パッケージ仕様部を作成するには、CREATE PACKAGE文を使用します。 パッケージ本体を作成するには、CREATE PACKAGE BODY文を使用します。
仕様部には、ストアド・サブプログラムやその他のパッケージ外部のコードから見えるパブリックな宣言を入れます。 サブプログラムは、仕様部で他のすべての項目の後で最後に宣言する必要があります(ただし、特定のファンクションの名前を指定するプラグマは、ファンクション仕様部の後に宣言する必要があります)。
本体には、実装の細部と、パッケージ外部のコードからは隠されているプライベートな宣言を入れます。 パッケージ本体の宣言部の後には、オプションの初期化部があります。ここには、パッケージ変数を初期化する文と、その他の一度のみの設定を行う文を入れます。
AUTHID句は、すべてのパッケージ・サブプログラムがその定義者(デフォルト)と実行者のどちらの権限で実行するか、およびスキーマ・オブジェクトへの未修飾の参照が定義者と実行者のどちらのスキーマで解決されるかを決定します。 詳細は、「実行者権限または定義者権限の使用(AUTHID句)」を参照してください。
コール仕様を使用すると、パッケージ・サブプログラムをJavaメソッドまたは外部Cファンクションにマップできます。 コール仕様は、JavaまたはC言語の名前、パラメータ型および戻り型を対応するSQLにマップします。
|
参照:
|
パッケージ変数のgetメソッドとsetメソッド。これらは他のサブプログラムからの直接読取りや書込みを避ける場合に使用します。
SQL問合せテキストのあるカーソル宣言。 同じ問合せテキストを複数の場所で再利用する方が、同じ問合せを少しずつ変更して毎回再入力するより効率的です。 また、多くの場所で使用されている問合せを変更する必要がある場合、メンテナンスも容易になります。
例外の宣言。 これらの宣言は、通常、起動されたサブプログラム内で例外を処理するために、様々なサブプログラムから参照できる必要があります。
相互に起動し合うサブプログラムの宣言。 パッケージ化されたサブプログラムのコンパイルの順序にとらわれる必要はありません。このため、スタンドアロン・ストアド・サブプログラムが互いに起動し合う場合よりも便利です。
オーバーロードされたサブプログラムの宣言 名前は同じだが異なるパラメータ・セットを使用する、複数のバリエーションのサブプログラムを作成できます。
同じセッション内のサブプログラム・コール間で使用可能な状態に保つ変数。 パッケージ内でグローバル変数のように扱うことができます。
PL/SQLコレクション型の型宣言。 ストアド・サブプログラム間でコレクションをパラメータとして渡すには、起動元のサブプログラムと起動されたサブプログラムの両方でコレクションを参照できるように、パッケージ内で型を宣言する必要があります。
詳細は、「CREATE PACKAGE文」を参照してください。 PL/SQLパッケージの例は、例1-19および例10-3を参照してください。 アプリケーションから参照およびアクセスできるのは、パッケージ仕様部の宣言のみです。 パッケージ本体の実装の詳細は隠ぺいされ、アクセスできません。 そのため、起動元のプログラムを再コンパイルしなくても、本体(実装)を変更できます。
ソフトウェア・エンジニアリングではパッケージに長い歴史があります。大規模なシステムのチーム開発において、パッケージは信頼性のある、メンテナンスが容易で再利用可能なコードに重要な機能を提供しています。
モジュール性
パッケージを使用すると、論理的に関連した型、項目およびサブプログラムを、名前付きのPL/SQLモジュールにカプセル化できます。 個々のパッケージは理解しやすく、パッケージ間のインタフェースは単純かつ明快で、明確に定義されています。 これはアプリケーション開発に役立ちます。
アプリケーションの設計の容易さ
アプリケーション設計の最初の段階では、パッケージの仕様部に含まれるインタフェース情報のみが必要です。 仕様部は本体がなくてもコーディングし、コンパイルできます。 仕様部のコンパイルが終了すると、そのパッケージを参照するストアド・サブプログラムもコンパイルできます。 アプリケーション作成の最終段階になるまで、パッケージ本体を完全に定義する必要はありません。
情報の隠ぺい
パッケージを使用すると、個々の型、項目およびサブプログラムについて、それがパブリック(可視でアクセス可能)なのか、またはプライベート(隠されていてアクセス不可)なのかを指定できます。 たとえば、パッケージに含まれる4つのサブプログラムのうち、3つをパブリック、1つをプライベートにすることもできます。 パッケージはプライベートなサブプログラムの実装を隠ぺいするため、実装が変更された場合も(アプリケーションではなく)パッケージのみが影響を受けます。 このため、メンテナンスや機能拡張が簡単に実施できます。 また、実装上の細部をユーザーから隠ぺいすることで、パッケージの整合性を維持できます。
機能の追加
パッケージ化されたパブリック変数およびカーソルは、セッションを通じて存続します。 このため、同じ環境の中で実行するすべてのサブプログラムで共有できます。 これによって、データベースに格納することなくトランザクション間でデータをメンテナンスできます。
高いパフォーマンス
パッケージ・サブプログラムを初めて起動すると、パッケージ全体がメモリーにロードされます。 パッケージ内の関連するサブプログラムに対する2度目以降のコールでは、ディスクI/Oは必要ありません。
パッケージ化すると互いに依存することがなくなるため、不要な再コンパイルを避けることができます。 たとえば、パッケージ・ファンクションの本体を変更した場合、そのファンクションを起動する他のサブプログラムはデータベースによって再コンパイルされません。これらのサブプログラムは仕様部で宣言されたパラメータと戻り値にのみ依存するためです。再コンパイルされるのは、仕様部が変更された場合のみです。
パッケージ仕様部にはパブリック宣言が入っています。 宣言された項目は、パッケージ内のどの場所からでも、同じスキーマ内の別のサブプログラムからでもアクセス可能です。 図10-1に有効範囲を示します。
仕様部には、アプリケーションが利用できるパッケージ・リソースのリストがあります。 アプリケーションがリソースを使用するために必要な情報は、すべて仕様部の中にあります。 たとえば、次の宣言は、factorialという名前のファンクションがINTEGER型の引数を1つ取り、INTEGER型の値を戻すことを示しています。
FUNCTION factorial (n INTEGER) RETURN INTEGER; -- returns n!
ファンクションの起動に必要な情報はこれのみです。 ユーザーはfacの下位の実装のこと(それが反復を利用しているのか、再帰を利用しているのかなど)を考える必要がありません。
仕様部で宣言されているのが型、定数、変数、例外およびコール仕様のみであれば、パッケージ本体は不要です。 下位の実装を持つのは、サブプログラムとカーソルのみです。 例10-1では、パッケージは型、例外および変数を宣言していますが、サブプログラムまたはカーソルを持たないため、パッケージ本体は不要です。 このようなパッケージを使用すると、セッションを通じて存続するグローバル変数(ストアド・サブプログラムおよびトリガーで使用できる)を定義できます。
例10-1 単純なパッケージ仕様部(本体なし)
CREATE PACKAGE trans_data AS -- bodiless package
TYPE TimeRec IS RECORD (
minutes SMALLINT,
hours SMALLINT);
TYPE TransRec IS RECORD (
category VARCHAR2(10),
account INT,
amount REAL,
time_of TimeRec);
minimum_balance CONSTANT REAL := 10.00;
number_processed INT;
insufficient_funds EXCEPTION;
END trans_data;
/
パッケージの仕様部で宣言された型、項目、サブプログラムおよびコール仕様部を参照するときには、次のようにドット表記法を使用します。
package_name.type_name package_name.item_name package_name.subprogram_name package_name.call_spec_name
パッケージ内容は、データベース・トリガー、ストアド・サブプログラム、3GLアプリケーション・プログラムおよび様々なOracleのツール製品から参照できます。 たとえば、例1-20または例10-3で示すように、パッケージ・サブプログラムを起動できます。
次の例では、Pro*Cプログラムの無名ブロックからhire_employeeプロシージャを起動しています。 実パラメータemp_id、emp_lnameおよびemp_fnameはホスト変数です。
EXEC SQL EXECUTE
BEGIN
emp_actions.hire_employee(:emp_id,:emp_lname,:emp_fname, ...);
制限
リモート・パッケージ変数は、直接的にも間接的にも参照できません。 たとえば、サブプログラムがパッケージ変数を参照する場合、データベース・リンクを通じてサブプログラムを起動することはできません。
また、パッケージ内ではホスト変数を参照できません。
パッケージ本体には、パッケージ仕様部で宣言されているすべてのカーソルとサブプログラムの実装が含まれています。 パッケージ本体で定義されたサブプログラムにパッケージの外側からアクセスするには、その指定がパッケージ仕様部に存在している必要があります。 サブプログラムの仕様部がパッケージ仕様部に含まれていない場合、サブプログラムは同じパッケージ内の他のサブプログラムからのみ起動できます。 パッケージ本体は、パッケージ仕様部と同じスキーマ内にある必要があります。
サブプログラムの仕様部と本体を一致させるために、PL/SQLは、それらのヘッダーをトークンごとに比較します。 このため、空白を除いて、ヘッダーは一語一語が一致している必要があります。 一致していない場合、PL/SQLは、例10-2に示すように、例外を呼び出します。
例10-2 パッケージの仕様部と本体の一致
CREATE PACKAGE emp_bonus AS PROCEDURE calc_bonus (date_hired employees.hire_date%TYPE); END emp_bonus; / CREATE PACKAGE BODY emp_bonus AS -- the following parameter declaration raises an exception -- because 'DATE' does not match employees.hire_date%TYPE -- PROCEDURE calc_bonus (date_hired DATE) IS -- the following is correct because there is an exact match PROCEDURE calc_bonus (date_hired employees.hire_date%TYPE) IS BEGIN DBMS_OUTPUT.PUT_LINE ('Employees hired on ' || date_hired || ' get bonus.'); END; END emp_bonus; /
パッケージ本体には、パッケージの内部動作に必要な型や項目を定義するプライベート宣言を入れることもできます。 これらの宣言の有効範囲は、パッケージ本体に対してローカルです。 このため、宣言された型と項目はパッケージ本体の中からでなければアクセスできません。 パッケージ仕様部とは異なり、パッケージ本体の宣言部にはサブプログラムの本体を置くことができます。
パッケージ本体の宣言部の後には、オプションの初期化部があります。ここには、一般にパッケージの中で宣言済の変数を初期化する文がいくつか置かれています。
サブプログラムとは異なり、パッケージを起動することもパッケージにパラメータを渡すこともできないため、パッケージの初期化部にはあまり意味がありません。 このため、パッケージの初期化部は、パッケージが初めて参照されたときに一度のみ実行されます。
すでに説明したように、仕様部で宣言されているのが型、定数、変数、例外およびコール仕様部のみであればパッケージ本体は不要です。 ただしその場合でも、パッケージ本体を使用して、パッケージ仕様部で宣言した項目を初期化できます。
次に示すemp_adminという名前のパッケージの例を考えます。 パッケージ仕様部では、次のような型、項目およびサブプログラムを宣言します。
EmpRecTyp型
カーソルdesc_salary
例外invalid_salary
ファンクションhire_employeeおよびnth_highest_salary
プロシージャfire_employeeおよびraise_salary
パッケージを作成すると、そのパッケージの型の参照、サブプログラムの起動、カーソルの使用、例外の呼出しなどを行うアプリケーションを開発できます。 パッケージを作成すると、そのパッケージはデータベースに格納され、そのパッケージの実行権限を持つアプリケーションから使用されます。
例10-3 emp_adminパッケージの作成
-- create the audit table to track changes
CREATE TABLE emp_audit(date_of_action DATE, user_id VARCHAR2(20),
package_name VARCHAR2(30));
CREATE OR REPLACE PACKAGE emp_admin AS
-- Declare externally visible types, cursor, exception
TYPE EmpRecTyp IS RECORD (emp_id NUMBER, sal NUMBER);
CURSOR desc_salary RETURN EmpRecTyp;
invalid_salary EXCEPTION;
-- Declare externally callable subprograms
FUNCTION hire_employee (last_name VARCHAR2,
first_name VARCHAR2,
email VARCHAR2,
phone_number VARCHAR2,
job_id VARCHAR2,
salary NUMBER,
commission_pct NUMBER,
manager_id NUMBER,
department_id NUMBER)
RETURN NUMBER;
PROCEDURE fire_employee
(emp_id NUMBER); -- overloaded subprogram
PROCEDURE fire_employee
(emp_email VARCHAR2); -- overloaded subprogram
PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);
FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp;
END emp_admin;
/
CREATE OR REPLACE PACKAGE BODY emp_admin AS
number_hired NUMBER; -- visible only in this package
-- Fully define cursor specified in package
CURSOR desc_salary RETURN EmpRecTyp IS
SELECT employee_id, salary
FROM employees
ORDER BY salary DESC;
-- Fully define subprograms specified in package
FUNCTION hire_employee (last_name VARCHAR2,
first_name VARCHAR2,
email VARCHAR2,
phone_number VARCHAR2,
job_id VARCHAR2,
salary NUMBER,
commission_pct NUMBER,
manager_id NUMBER,
department_id NUMBER)
RETURN NUMBER IS new_emp_id NUMBER;
BEGIN
new_emp_id := employees_seq.NEXTVAL;
INSERT INTO employees VALUES (new_emp_id,
last_name,
first_name,
email,
phone_number,
SYSDATE,
job_id,
salary,
commission_pct,
manager_id,
department_id);
number_hired := number_hired + 1;
DBMS_OUTPUT.PUT_LINE('The number of employees hired is '
|| TO_CHAR(number_hired) );
RETURN new_emp_id;
END hire_employee;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM employees WHERE employee_id = emp_id;
END fire_employee;
PROCEDURE fire_employee (emp_email VARCHAR2) IS
BEGIN
DELETE FROM employees WHERE email = emp_email;
END fire_employee;
-- Define local function, available only inside package
FUNCTION sal_ok (jobid VARCHAR2, sal NUMBER) RETURN BOOLEAN IS
min_sal NUMBER;
max_sal NUMBER;
BEGIN
SELECT MIN(salary), MAX(salary)
INTO min_sal, max_sal
FROM employees
WHERE job_id = jobid;
RETURN (sal >= min_sal) AND (sal <= max_sal);
END sal_ok;
PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS
sal NUMBER(8,2);
jobid VARCHAR2(10);
BEGIN
SELECT job_id, salary INTO jobid, sal
FROM employees
WHERE employee_id = emp_id;
IF sal_ok(jobid, sal + amount) THEN
UPDATE employees SET salary =
salary + amount WHERE employee_id = emp_id;
ELSE
RAISE invalid_salary;
END IF;
EXCEPTION -- exception-handling part starts here
WHEN invalid_salary THEN
DBMS_OUTPUT.PUT_LINE
('The salary is out of the specified range.');
END raise_salary;
FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp IS
emp_rec EmpRecTyp;
BEGIN
OPEN desc_salary;
FOR i IN 1..n LOOP
FETCH desc_salary INTO emp_rec;
END LOOP;
CLOSE desc_salary;
RETURN emp_rec;
END nth_highest_salary;
BEGIN -- initialization part starts here
INSERT INTO emp_audit VALUES (SYSDATE, USER, 'EMP_ADMIN');
number_hired := 0;
END emp_admin;
/
-- invoking the package procedures
DECLARE
new_emp_id NUMBER(6);
BEGIN
new_emp_id := emp_admin.hire_employee ('Belden',
'Enrique',
'EBELDEN',
'555.111.2222',
'ST_CLERK',
2500,
.1,
101,
110);
DBMS_OUTPUT.PUT_LINE
('The new employee id is ' || TO_CHAR(new_emp_id));
EMP_ADMIN.raise_salary(new_emp_id, 100);
DBMS_OUTPUT.PUT_LINE('The 10th highest salary is '||
TO_CHAR(emp_admin.nth_highest_salary(10).sal) || ',
belonging to employee: ' ||
TO_CHAR(emp_admin.nth_highest_salary(10).emp_id));
emp_admin.fire_employee(new_emp_id);
-- you can also delete the newly added employee as follows:
-- emp_admin.fire_employee('EBELDEN');
END;
/
パッケージの初期化部は、パッケージが初めて参照されたときに一度のみ実行されることに注意してください。 このため、上の例のINSERT文では、データベース表emp_auditに挿入される行は1行のみです。また、変数number_hiredは一度しか初期化されません。
プロシージャhire_employeeが起動されるたびに、変数number_hiredが更新されます。 ただし、number_hiredが保持しているカウントは各セッションによって異なります。 つまり、カウントは全ユーザーが処理した数ではなく、1人のユーザーが処理した新しい従業員の数を反映します。
PL/SQLでは、パッケージ化された複数のサブプログラムに同じ名前を付けることができます。 サブプログラムで、データ型が異なるパラメータからなる類似したパラメータのセットを受け取れるようにする場合は、この方法が便利です。 たとえば、例10-3のemp_adminパッケージではfire_employeeという名前の2つのプロシージャを定義しています。 1番目のプロシージャは数値を受け取り、2番目のプロシージャは文字列を受け取ります。 ただし、どちらのプロシージャもデータを適切に処理します。 オーバーロードされたサブプログラムに適用される規則は、「PL/SQLサブプログラム名のオーバーロード」を参照してください。
パッケージemp_adminのパッケージ本体では、0(ゼロ)に初期化される変数number_hiredが宣言されています。 本体で宣言される項目は、パッケージ内でしか使用できません。 このため、パッケージの外側のPL/SQLコードからは変数number_hiredを参照できません。 このような項目はプライベートと呼ばれます。
例外emp_adminなど、invalid_salaryの仕様部で宣言された項目は、パッケージの外からも見えます。 このため、例外invalid_salaryはどのPL/SQLコードからも参照できます。 このような項目はパブリックと呼ばれます。
セッションを通じて、または複数のトランザクションの間で維持する必要がある項目は、パッケージ本体の宣言部に置くようにしてください。 たとえば、number_hiredの値はhire_employeeへの複数のコールの間も保持されています。 セッションが終了すると、値が失われます。
パブリックにする必要がある項目は、パッケージ仕様部の中に置いてください。 たとえば、パッケージ仕様部で宣言されたemp_recは、パブリックで使用可能です。
STANDARDという名前のパッケージではPL/SQL環境を定義しています。 このパッケージの仕様部では、型、例外およびサブプログラムをグローバルに宣言します。それらは、自動的にPL/SQLプログラムで使用可能になります。 たとえば、パッケージSTANDARDでは、引数の絶対値を戻すファンクションABSを次のように宣言します。
FUNCTION ABS (n NUMBER) RETURN NUMBER;
パッケージSTANDARDの内容は、アプリケーションから直接見ることができます。 その内容を参照する場合もパッケージ名に接頭辞を付けて修飾名にする必要はありません。 たとえば、ABSはデータベース・トリガー、ストアド・サブプログラム、Oracleのツール製品または3GLアプリケーションから次のように起動できます。
abs_diff := ABS(x - y);
ユーザー独自のABSを宣言すると、ローカル宣言がグローバル宣言をオーバーライドします。 ただし、次に示すように、完全な名前を指定して、組込みファンクションを起動できます。
abs_diff := STANDARD.ABS(x - y);
ほとんどの組込みファンクションはオーバーロードされています。 たとえば、パッケージSTANDARDには次のような宣言があります。
FUNCTION TO_CHAR (right DATE) RETURN VARCHAR2; FUNCTION TO_CHAR (left NUMBER) RETURN VARCHAR2; FUNCTION TO_CHAR (left DATE, right VARCHAR2) RETURN VARCHAR2; FUNCTION TO_CHAR (left NUMBER, right VARCHAR2) RETURN VARCHAR2;
PL/SQLは、仮パラメータと実パラメータの数とデータ型を比較して、どのTO_CHARのコールかを判定します。
Oracleの様々なツール製品には、PL/SQL、SQL、Javaまたはその他のプログラミング環境から起動できるApplication Program Interface(API)を定義した製品固有のパッケージが用意されています。 この項では、広く使用されている次の製品固有のパッケージについて簡単に説明します。
これらの製品固有のパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。
DBMS_ALERTパッケージでは、データベース内の特定の値が変更されたときに、データベース・トリガーを使用してアプリケーションに警告できます。 その警告は、トランザクション・ベースで、非同期です(つまり、警告はタイミング・メカニズムとは無関係に作動します)。 たとえば、会社ではこのパッケージを使用して、株や債券の取り引き価格が更新されたときに、投資ポートフォリオの値を更新できます。
DBMS_OUTPUTパッケージを使用すると、PL/SQLブロック、サブプログラム、パッケージおよびトリガーからの出力を表示できます。 このパッケージは、PL/SQLデバッグ情報を表示する場合に特に役立ちます。 PUT_LINEプロシージャは、別のトリガー、サブプログラムまたはパッケージで読み取ることができるバッファに情報を出力します。 この情報は、プロシージャGET_LINEを起動するか、SQL*PlusにSERVEROUTPUT ONを設定することによって表示します。 例10-4に、PL/SQLブロックからの出力の表示方法を示します。
例10-4 DBMS_OUTPUTパッケージでのPUT_LINEの使用
REM set server output to ON to display output from DBMS_OUTPUT
SET SERVEROUTPUT ON
BEGIN
DBMS_OUTPUT.PUT_LINE
('These are the tables that ' || USER || ' owns:');
FOR item IN (SELECT table_name FROM user_tables)
LOOP
DBMS_OUTPUT.PUT_LINE(item.table_name);
END LOOP;
END;
/
DBMS_PIPEパッケージを使用すると、名前付きパイプを介して異なるセッション間で通信できます。 (パイプとは、あるプロセスから他のプロセスに情報を渡すために使用するメモリーの領域のことです。) PACK_MESSAGEプロシージャとSEND_MESSAGEプロシージャを使用してパイプの中にメッセージをパックし、同じインスタンス内の別のセッションまたは待機中のアプリケーション(Linux、UNIXプログラムなど)に送信できます。
パイプのもう一端では、RECEIVE_MESSAGEプロシージャとUNPACK_MESSAGEプロシージャを使用して、メッセージを受信し、アンパック(読取り)できます。 名前付きパイプは、あらゆる点で便利です。 たとえば、データを収集するCプログラムを作成し、次にそれをパイプを介してデータベース内のストアド・サブプログラムに送信できます。
DBMS_CONNECTION_POOLパッケージは、複数の中間層プロセスによって共有されるデータベース常駐接続プールの管理に使用されます。 データベース管理者は、DBMS_CONNECTION_POOLのプロシージャを使用して、データベース常駐接続プールの開始と停止、およびサイズや時間制限などのプール・パラメータの構成を行います。
|
参照:
|
UTL_FILEパッケージを使用すると、PL/SQLプログラムでオペレーティング・システム(OS)のテキスト・ファイルに対して読取りおよび書込みを実行できます。 このパッケージは、オペレーティング・システムの標準ストリーム・ファイルI/Oの制限されたバージョン(OPEN、PUT、GET、CLOSEの操作を含む)を提供します。
テキスト・ファイルに対して読取りまたは書込みを実行する場合は、ファンクションFOPENを起動します。このファンクションは、それ以降のサブプログラム・コールで使用するためのファイル・ハンドルを戻します。 たとえば、プロシージャPUT_LINEは、テキスト文字列と行終了文字をオープン・ファイルに書き込みます。また、プロシージャGET_LINEは、オープン・ファイルから出力バッファにテキストの行を読み込みます。
UTL_HTTPパッケージを使用すると、PL/SQLプログラムでHTTP(Hypertext Transfer Protocol)のコールアウトを実行できます。 これによって、データをインターネットから取り出すことも、Oracle Web Serverカートリッジを起動することもできます。 このパッケージには複数のエントリ・ポイントがあり、各ポイントでURL(Uniform Resource Locator)を受け取り、指定されたサイトに接続し、要求されたデータを戻します。通常このデータはHTML(Hypertext Markup Language)形式のものです。
パッケージを作成する場合は、別のアプリケーションで再利用できるように、汎用性を持たせるようにしてください。 オラクル社が提供するパッケージをよく理解して、すでにOracleが提供している機能と重複する機能を持つパッケージを作成しないように注意してください。
パッケージ本体の前にパッケージ仕様部を設計および定義してください。 仕様部に入れるのは、起動元のプログラムから見える必要のあるもののみにします。 こうすることで、他の開発者が実装の細部に不適切に依存しないようにできます。
コードの変更時に必要な再コンパイルを削減するために、パッケージ仕様部に置く項目はできるかぎり少なくしておきます。 そうすれば、パッケージ本体を変更しても、起動元のサブプログラムを再コンパイルする必要はありません。 ただし、パッケージ仕様部を変更すると、データベースでそのパッケージを参照するすべてのストアド・サブプログラムを再コンパイルする必要があります。
パッケージの中で、カーソルの仕様部を本体と切り離して別の位置に配置できます。 これによって、カーソルの仕様部を変更せずに、本体のみを変更できます。 カーソルの構文は、「明示カーソル」を参照してください。
例10-5では、%ROWTYPE属性を使用して、データベース表employeesの中の行を表すレコード型を指定しています。
例10-5 パッケージでのカーソル仕様部の分離
CREATE PACKAGE emp_stuff AS
-- Declare cursor spec
CURSOR c1 RETURN employees%ROWTYPE;
END emp_stuff;
/
CREATE PACKAGE BODY emp_stuff AS
CURSOR c1 RETURN employees%ROWTYPE IS
-- Define cursor body
SELECT * FROM employees WHERE salary > 2500;
END emp_stuff;
/
戻り値のデータ型をSELECT句で指定しているため、カーソル仕様部にはRETURN文がありません。 ただしカーソル本体には、SELECT文と、カーソル仕様部と同じRETURN句が必要です。 また、SELECTリスト中の項目の数とデータ型は、RETURN句と一致する必要があります。
パッケージ・カーソルを使用すると柔軟性が向上します。 たとえば、上の例では、カーソル仕様部を変更することなく、カーソル本体を変更できます。
次の例に示すように、PL/SQLブロックまたはサブプログラムからパッケージ・カーソルを参照するドット表記法を使用します。
DECLARE emp_rec employees%ROWTYPE;BEGIN
OPEN emp_stuff.c1;
LOOP
FETCH emp_stuff.c1 INTO emp_rec;
-- do processing here ...
EXIT WHEN emp_stuff.c1%NOTFOUND;
END LOOP;
CLOSE emp_stuff.c1;
END;
/
パッケージ・カーソルの有効範囲はPL/SQLブロックに制限されません。 したがって、パッケージ・カーソルをオープンすると、クローズするか、セッションから切断するまでオープンしたままになります。