e-learning、オラクル研修、LMS(学習管理システム)のiStudy

e-learning、オラクル研修、LMS(学習管理システム)のiStudy

第116回「WEBアプリ作成(14)(パッケージ化する)」

2015.01.22

こんにちは。インストラクターの蓑島です。今回が「PL/SQLを使ってみよう」の今年最初の回ですね。
昨年1年間、本メルマガを読んでいただき、どうもありがとうございました。年末・年始で中断しましたが、今回からまた再開いたします。PL/SQLについてなるべく簡単でわかりやすい解説を心掛けますので、今年もよろしくお願いします。

さて、今回はタイトルにありますように、WEBアプリケーション(それはつまり、PL/SQLプロシージャです)をパッケージ化することについて解説します。

今まで、本シリーズ(WEBアプリ作成)で解説し作成したPL/SQLプロシージャはすべて、パッケージ化していないものでした。理由は解説のためにソースコードをなるべく短くしたかったからです。しかし実際にはパッケージ化することが推奨です。パッケージ化することで様々なメリットがあります。例えば複数のプロシージャをまとめて一つのパッケージにすることで、個々のプロシージャの権限管理を行う必要はありません。パッケージという大きな単位で管理をするだけでよくなります。このことにより、例えば複数のユーザで同じWEBアプリを共有する場合に権限の管理がたいへん簡素になります。(複数のユーザでWEBアプリを共有する方法については、次回、解説予定です)

また、パッケージ化することで初めて可能となる高度な機能もいくつかあります。(変数の永続性やオーバーロードなど)バックナンバーの第27回から第35回まですべてパッケージをテーマにして基礎から詳細まで解説していますので、興味のある方はぜひ参照してみてください。

バックナンバーの復習となりますが、パッケージは一般に関連するプロシージャやファンクション、変数、定数、例外、カーソルなど(宣言可能なものほとんどすべてがパッケージ化可能)をまとめたものです。そして、プロシージャ、ファンクションをパッケージ化する場合は、必ず2種類のパッケージを組み合わせて論理的に一つのパッケージとする必要があります。その2種類が仕様部と本体部です。仕様部ではプロシージャやファンクションの名前、仮パラメータ、RETURNする型(ファンクションの場合)を記述します。
つまり、CREATE PROCEDURE文などのキーワード「IS(または AS)」よりも前の部分だけを記述するので完全な定義ではありません。プロシージャ・ファンクションの完全な定義は本体部に記述します。
よってプロシージャ・ファンクションがパッケージに含まれる場合は必ず仕様部だけでなく本体部も必要となります。本体部では、IS(またはAS)よりも後ろの部分も含んだ完全なプロシージャ(またはファンクション)の定義を記述します。仕様部に記述したものはすべてパブリックな属性であり、ユーザやアプリケーションが直接アクセスできます。
しかし本体に記述したものはすべてプライベートであり、ユーザやアプリケーションが直接参照することはできません。
つまり、ユーザやアプリケーションが直接アクセス可能なものを仕様部に集め、ユーザが直接知る必要のない処理の部分等は本体に記述することが基本です。作成順番としては、必ず仕様部を先に作成し、次に本体部を作成します。詳細は上述のバックナンバーを参照してください。

では早速、具体的にパッケージ化してみましょう。
バックナンバー第107回第108回で解説したWEBアプリケーションのプロシージャをパッケージにまとめてみます。第107回は、EMP2表へのデータ登録のためのフォーム画面を生成するプロシージャ(EMP2_INSERT_FORM)であり、第108回では、そのフォーム画面からのデータをもとにEMP2表へINSERTを行うプロシージャ(EMP2_INSERT_EXE)です。個々のソースコードはそれぞれのバックナンバーを参照してください。

では、今回作成するパッケージの名前を「PAC_EMP」として、実際にこれらのプロシージャをパッケージ化してみます。

SQL> SHOW USER
ユーザーは"SCOTT"です。

まず、パッケージ仕様部から作成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE OR REPLACE PACKAGE  PAC_EMP
IS
/*****************************************************************/
/** 仕様部なので、各プロシージャの名前と仮パラメータのみ記述する */
/*****************************************************************/
-- ★入力フォーム用のプロシージャ
PROCEDURE EMP2_INSERT_FORM;
-- ★データ登録用のプロシージャ
PROCEDURE EMP2_INSERT_EXE
( P_ENAME      IN VARCHAR2,
   P_HIREDATE   IN VARCHAR2,
   P_DEPTNO     IN VARCHAR2,
   P_NOTE       IN VARCHAR2);
END PAC_EMP;
/
 
パッケージが作成されました。

パッケージ仕様部がエラーなくできたら、続いて、パッケージ本体部を記述します。

(注意)以下のソースコードはブラウザ表示のために、山括弧(「<」と「>」)を全角にしています。
コピーして実行する方は、必ず、山括弧(「<」と「>」)を半角に変換してください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
CREATE OR REPLACE PACKAGE BODY  PAC_EMP  -- BODYがあると本体
IS
/*****************************************************************/
/** 本体部なので、各プロシージャの完全な定義を記述する           */
/*****************************************************************/
-- ★★★★★★★★★★★★★★★★★
-- ★入力フォーム用のプロシージャ ★
-- ★★★★★★★★★★★★★★★★★
PROCEDURE EMP2_INSERT_FORM
IS
    V_DEPT  VARCHAR2(1000);
BEGIN
-- 部門のリストを作成する
   V_DEPT  := '<SELECT NAME="P_DEPTNO">' ||
               '<OPTION VALUE="NULL">-</OPTION>' ;
   FOR REC IN ( SELECT * FROM DEPT ORDER BY DEPTNO) LOOP
       V_DEPT := V_DEPT || '<OPTION VALUE="'
                        ||TO_CHAR(REC.DEPTNO)
                        || '">' ||REC.DNAME || '</OPTION>' ;
   END LOOP;
   V_DEPT := V_DEPT || '</SELECT>' ;
-- ここから画面表示処理
   HTP.P( '<HTML>' );
   HTP.P( '<HEAD><TITLE>社員登録</TITLE></HEAD>' );
   HTP.P( '<BODY>' );
   HTP.P( '<H1>社員登録</H1>' );
   HTP.P( '<HR>' );
   -- ★注意!★以下のACTION属性の記述に注意 パッケージ名(pac_emp)で修飾する
   HTP.P( '<FORM ACTION="pac_emp.emp2_insert_exe" METHOD="POST">' );
   HTP.P( '<TABLE BORDER>' );
   HTP.P( '<TR><TD BGCOLOR="SILVER">社員番号</TD>' ||
         '<TD><FONT COLOR="GRAY">自動生成</FONT></TD></TR>' );
   HTP.P( '<TR><TD BGCOLOR="SILVER">社員名</TD>' ||
         '<TD><INPUT TYPE="TEXT" NAME="P_ENAME"></TD></TR>' );
   HTP.P( '<TR><TD BGCOLOR="SILVER">入社日(YYMMDD)</TD>' ||
         '<TD><INPUT TYPE="TEXT" NAME="P_HIREDATE"></TD></TR>' );
   HTP.P( '<TR><TD BGCOLOR="SILVER">部門</TD>' ||
         '<TD>' ||V_DEPT || '</TD></TR>' );
   HTP.P( '<TR><TD BGCOLOR="SILVER" COLSPAN="2">備考</TD></TR>' );
   HTP.P( '<TR><TD COLSPAN="2">' ||
         '<TEXTAREA NAME="P_NOTE" rows="10" cols="70"></TEXTAREA>' ||
         '</TD><TR>' );
   HTP.P( '</TABLE>' );
   HTP.P( '<INPUT TYPE="SUBMIT" VALUE="送信">' );
   HTP.P( '</FORM>' );
   HTP.P( '</BODY>' );
   HTP.P( '</HTML>' );
END EMP2_INSERT_FORM;
-- ★★★★★★★★★★★★★★★★★
-- ★データ登録用のプロシージャ  ★
-- ★★★★★★★★★★★★★★★★★
PROCEDURE EMP2_INSERT_EXE
( P_ENAME      IN VARCHAR2,
   P_HIREDATE   IN VARCHAR2,
   P_DEPTNO     IN VARCHAR2,
   P_NOTE       IN VARCHAR2)
IS
   V_EMPNO     EMP2.EMPNO%TYPE;   -- 社員番号
   V_HIREDATE  EMP2.HIREDATE%TYPE;  -- 入社日
   V_DEPTNO    EMP2.DEPTNO%TYPE;    -- 部門番号
   V_SQLERRM   VARCHAR2(500);
BEGIN
   -- 新しい社員番号の取得
      SELECT NVL( MAX (EMPNO),0)  + 1 INTO V_EMPNO  FROM EMP2;
   -- P_HIREDATEパラメータをDATE型に変換して入社日を取得
      V_HIREDATE := TO_DATE(P_HIREDATE, 'YYMMDD' );
   -- 部門番号の取得
      IF P_DEPTNO = 'NULL'  THEN   -- 文字列'NULL'のときは、NULL値とする
         V_DEPTNO := NULL ;
      ELSE
         V_DEPTNO := TO_NUMBER(P_DEPTNO);   --それ以外の部門番号は数字に変換
      END IF;
   -- EMP2(社員表)へのINSERT処理
      INSERT INTO EMP2(EMPNO, ENAME, HIREDATE, DEPTNO, NOTE)
      VALUES (V_EMPNO, P_ENAME, V_HIREDATE, V_DEPTNO, P_NOTE);
      IF SQL%ROWCOUNT = 1 THEN
         COMMIT ;
         HTP.P( '正常に1件登録できました:社員番号 = ' ||TO_CHAR(V_EMPNO));
      ELSE
         ROLLBACK ;
         HTP.P( '異常です。社員登録処理を取り消しました' );
      END IF;
EXCEPTION
   WHEN OTHERS THEN
        V_SQLERRM := SQLERRM;   -- エラーメッセージの取得
        HTP.P(V_SQLERRM);       -- それを画面表示
END  EMP2_INSERT_EXE;
END PAC_EMP;
/
 
パッケージ本体が作成されました。

やはりパッケージ化すると、当然ですがソースコードが長くなります。

途中、見やすさのために、改行をしていますが、基本的にはバックナンバーのプロシージャのソースコードをほとんどそのまま貼り付けています。

しかし、1点だけ、実質的に記述の違う部分があります。それは29行目のFORMタグのACTION属性です。
ACTION属性はフォームデータの送信先ですが、PL/SQLプロシージャを示します。そのPL/SQLプロシージャがパッケージ化されている場合は、パッケージ名を付ける必要がありますので、注意してください。つまり、パッケージ化していなければ、以下の記述であり、
ACTION="emp2_insert_exe"
パッケージ化していれば、パッケージ名(PAC_EMP)を先頭につけてpac_emp.emp2_insert_exeとします。
ACTION="pac_emp.emp2_insert_exe"

しかしEMP2_INSERT_FORMプロシージャとEMP2_INSERT_EXEプロシージャは同一パッケージ内にあるので、パッケージ名を付けなくてもいいのでは?と思う方が多いのではないでしょうか?
確かに同一パッケージ内のプロシージャコールはパッケージ名で修飾する必要はありません。
しかしこの場合は、EMP2_INSERT_FORMが、HTTPプロトコルを介して、EMP2_INSERT_EXEにフォームデータを送信しているので、直接プロシージャをコールしているわけではありません。ですから、パッケージ名で修飾する必要があります。パッケージ名で修飾していないと、パッケージ化されていないプロシージャに送信することになりますので、そのようなプロシージャが存在しない場合は、リクエストが失敗します。
たいへん間違えやすいので注意してください。

さて、これで、パッケージ化できました。

それでは早速、ブラウザで入力フォームを要求します。すなわち、パッケージ名で修飾して
pac_emp.emp2_insert_form
ですね。

バックナンバー「第103回 WEBアプリ作成(1)(Oracle DBとPL/SQLだけで、即、WEBアプリ)」と同様の設定ができていれば、以下のURLで可能です。

http://localhost:8080/dad/pac_emp.emp2_insert_form

ログイン画面が表示されるので、SCOTTユーザとそのパスワードでログインしてください。
そうすると、バックナンバー第108回と同様の、社員登録画面が表示され、社員登録が可能です。
簡単ですので、ぜひ試してみてください。

それでは今回はここまでにいたします。
また次回、ご期待ください。

先頭へ戻る