PL/SQLとJavaの強力な連携開発手法
JavaからのPL/SQLの呼び出しとシームレス・デバッグ

2004年5月

概要

アプリケーション開発におけるJavaの利用は急速に拡大しています。しかしながら、既に多くのプログラム資産を所有しているユーザーにとって、すべてを一からJavaで作り直すのは無駄なことです。特にデータベース内に蓄えられたPL/SQLプログラムの資産を有効利用したいと考えるのは自然な発想です。このドキュメントでは、JavaからのPL/SQLの呼び出しと効果的なデバッグ方法を紹介します。

JavaからPL/SQLを呼び出すコードの作成

Java から PL/SQLプログラムを呼び出すためには、一般に以下のような手順をとります。

  1. JDBC接続の確立
  2. 呼び出しのためのPL/SQL構文を作成
  3. ステートメント・オブジェクトを準備
  4. (必要に応じて)引き渡すパラメータのバインド
  5. ステートメントの実行
  6. エラー処理

この手順は、ほぼパターン化された定型のスタイルですが、実際にこのようなコードを用意するのは煩雑で手間のかかる作業です。これを簡易化する方法として、Oracle JPublisher ユーティリティを使用できます。このユーティリティは、指定されたPL/SQLプログラムから、それをコールするJavaユーティリティ・クラスのコードを生成します。

JPublisher ユーティリティ

JPublisher ユーティリティは、Oracleデータベースやアプリケーション・サーバーに同梱されており、コマンドラインで実行することができます(JPublisherについての詳細は 該当マニュアルを参照ください)。しかし、より簡単な方法は Oracle JDeveloper から利用することです。この場合、より対話的な方法で PL/SQL プログラムを Java で利用可能な状態にして、そのままコンパイル、実行できます。

注意:

JDeveloper 10gに組み込みの JPublisher ユーティリティは、現在、SQLJ の機能を利用したコードを生成しますが、SQLJ は 2005年末でサポートが終了される予定です。JPublisher ユーティリティは、今後、SQLJ の機能を使わずに直接 JDBC を利用する Javaコードを生成できるように改良されます。
JDeveloper 10gでSQLJファイルをコンパイルすると、ファイルシステム上に <<ファイル名>>.generated.java という名前の Javaファイルが生成されます。このファイルを使用すれば、SQLJ のサポート/未サポートに影響なく利用できます。


JDeveloper を使った、 PL/SQLプログラムを呼び出すJavaアプリケーションの作成例

ここでは一つの例を使って、手順を追って紹介します。

  1. JDeveloper を起動し、新規プロジェクトを作成します。
    本ドキュメントの以降の画面ショットでは、このプロジェクトの名前を、わかりやすいように PLSQL としています。また、プロジェクトの設定の「共通 - 入力パス」「デフォルト・パッケージ」sample と設定して、以後作成されるJavaソースが sample パッケージに含まれるように構成してます。
     
  2. JDeveloper から、データベースへの接続定義を作成します。
    # この方法は、 オンライン・デモで紹介しています。
    本ドキュメントでは、SCOTTユーザーに対する接続を作成したとして以後の話を進めています。
     
  3. 作成したデータベース接続定義のノードを展開して、目的のPL/SQLプログラムが見える状態にします。
    本ドキュメントでは、TEST_EMP_PROC1 および TEST_EMP_PROC2 というプロシージャがある場合を想定して以後の話を進めています。この2つのプロシージャのソースは本ドキュメントの最後に掲載しています。
     
  4. 目的のPL/SQLプログラムを含む上位のツリー(下図では 「プロシージャ」のツリー)を右クリックして、メニューから 「Javaを生成」を選択します。

    JPublisher ダイアログが起動します。
     
  5. ファイル生成先のプロジェクトを指定します。ここの 「使用名」で設定されるファイル名でプロジェクトにファイルが追加されます。

    上記例では、sample\TopLevel.sqljファイルが PLSQL プロジェクトに追加されます。
    ※ 既に同じ名前のファイルがプロジェクトに存在する場合は、何も処理が行われません。
     
  6. 生成されたSQLJファイルをコンパイルしておきます。

作成されたソースを利用するテスト用のJavaプログラムを作成してみましょう。

  1. ナビゲータからプロジェクトを右クリックして、「新規」を選択します。
  2. 新規ダイアログで General カテゴリの Javaクラスを選択し、 OKを押します。
  3. 新規クラスダイアログで適切に 名前および パッケージを指定します。オプションの 「mainメソッドの生成」をチェックして OKを押します。

     
  4. 生成されたJavaソース内で、JDBC接続のためのオブジェクト生成のコードを追加します。JDeveloperでは、以下のようにすると簡単です。
  5. JPublisher で作成されたオブジェクトを利用するコードを記述します。例えば、main メソッド内に以下のようなコードを追加します。
  6.  
    try
    {
      // 設定されたDB接続情報を使ってオブジェクトをインスタンス化 
      Connection conn = getConnection();
      TopLevel tl = new TopLevel( conn );
      // PL/SQL の TEST_EMP_PROC* に対応する Javaメソッドをコール 
      tl.testEmpProc1( new Integer(20) );
      tl.testEmpProc2( new Integer(20) );
      // 接続を閉じる 
      conn.commit();
      conn.close();
    }
    catch (SQLException e)
    {
      e.printStackTrace();
    }
    

この TestMain を実行すると、DBセッションが確立された後、TEST_EMP_PROC1 および TEST_EMP_PROC2 プロシージャが実行されます。

PL/SQLプログラムのデバッグ

JDeveloper を使用すると、データベース内のPL/SQLプログラムのコードを JDeveloper 内から参照して、その実行とデバッグを行うことができます。これについては、次の資料に記述がありますので、そちらを参照してください。


PL/SQLとJavaのシームレス・デバッグ

JavaからPL/SQLの呼び出しを行うアプリケーションを作成して、何か問題に直面した場合、Javaアプリケーションから正しい引数が渡されているか、PL/SQLプログラムで正しく受け取っているか、その後PL/SQLプログラムが期待通りに機能しているか、などのチェックが必要な場合があります。

これを行うためには、Javaアプリケーションのデバッグ、PL/SQLのデバッグを個々に単独で行うだけでは不十分です。Java アプリケーションのデバッグと、そこから呼び出されるPL/SQLのデバッグをシームレスに行うことが必要になります。

JDeveloper の強力なデバッガは、PL/SQLのリモート・デバッグ機能を提供します(前述の資料の「 リモートからのPL/SQLデバッグ」の章を参照)。この機能を利用すると、Java-PL/SQLのシームレス・デバッグが可能になります。

注意:

PL/SQLリモート・デバッグの機能は、対象のデータベースがOracle9i Database Release2 以降の場合でのみサポートされます。
デバッグ対象のPL/SQLプログラムはデバッグ情報を含んだ状態でコンパイルされている必要があります。これに関する詳細は、前述の資料の「 PL/SQLのデバッグに関するセットアップ要件」の章を参照してください。


Java-PL/SQLのシームレス・デバッグ:概要

このデバッグ方法を行うためには、Javaアプリケーションのデバッグを行うプロジェクトと、PL/SQLのリモート・デバッグを制御するプロジェクトの2つが必要になります。

  1. PL/SQLのリモート・デバッグ用のプロジェクトでリモート・デバッガを開始します。このプロジェクトでリモート・デバッグ用リスナーが起動します。
  2. PL/SQL連携コードを持つJavaアプリケーションのデバッグを開始します。
  3. Javaアプリケーションの実行過程で、PL/SQL連携を行うために、DBセッションに接続しPL/SQLプログラムを呼び出すコード部分に到達します。
  4. Javaアプリケーションから呼び出される最初のPL/SQLプログラム内で、JDeveloperのデバッグ・リスナーに接続するよう、プロシージャ・コールを行います。これにより、指定されたホスト名とポート番号で、DBセッションからJDeveloperデバッグ・リスナーへのアタッチが開始されます。
  5. DBセッションとJDeveloperとの間にデバッグ用セッションが確立されます。以後、Javaプログラムから制御がPL/SQLへと移っても、JDeveloperはシームレスに実行位置や変数値を追跡できるようになります。

Java-PL/SQLのシームレス・デバッグ:設定と実行

設定準備

実行手順

  1. PL/SQLプログラムにブレークポイントを設定
    JDeveloper の 接続ナビゲータの適切なデータベース定義ノードを展開して、デバッグ対象のPL/SQLプログラムをダブルクリックしてソースを表示します。適当な位置にブレークポイントを設定します。
       
     
  2. (必要に応じて)Javaプログラムにブレークポイントを設定
    Javaプログラムのソース・コードを開いて、適当な位置にブレークポイントを設定します。例えば以下のようになります。
       
     
  3. Javaプログラムのデバッグを開始
    Javaプログラムのデバッグを開始します。デバッガが起動し、最初のブレークポイントでストップします。
    プログラムの実行位置が PL/SQL 内部に移った後、PL/SQLプログラムに対して設定したブレークポイントでストップします。

    ストップした後は、ステップ実行などを利用して一行ずつ実行を確認したり、デバッガのデータ・ウィンドウ(上図では左下部分の領域)で、その時点のデータ値の確認や、必要に応じて、実行途中で値を変更してその後の処理を行わせることも可能です。

最後に

JDeveloper は、単なるJ2EE開発環境を越えた多くの機能を提供します。本ドキュメントによって、JDeveloper が PL/SQL と Java に対する強力な開発方法を実現可能であることがわかります。ここではシンプルなテスト用JavaプログラムからのPL/SQLの呼び出しを例にあげましたが、同じ仕組みが、Servlet などのサーバーサイドJavaコンポーネントからPL/SQLを呼ぶ場合にも適用できます。

最後に、本ドキュメントで使用したサンプル・コードを紹介しておきます。

TEST_EMP_PROC1、TEST_EMP_PROC2 プロシージャ

 
PROCEDURE TEST_EMP_PROC1 ( dept_no INTEGER ) AS
  vUP      INTEGER := 0;
  vPRE_SAL INTEGER := 100000;
  CURSOR emplist IS SELECT empno, sal FROM emp
    WHERE deptno = dept_no ORDER BY sal DESC;
BEGIN
  FOR empdata IN emplist LOOP
    IF vPRE_SAL > empdata.sal THEN vUP := vUP + 100;
    END IF;
    vPRE_SAL := empdata.sal;

    UPDATE emp SET sal = sal + vUP
    WHERE empno = empdata.empno;
  END LOOP;
END;

PROCEDURE TEST_EMP_PROC2 ( dept_no INTEGER ) AS
  vUP      INTEGER := 0;
  vPRE_SAL INTEGER := 100000;
  CURSOR emplist IS SELECT empno, sal FROM emp
    WHERE deptno = dept_no ORDER BY sal DESC;
BEGIN
  FOR empdata IN emplist LOOP
    IF vPRE_SAL > empdata.sal THEN vUP := vUP + 100;
    END IF;
    vPRE_SAL := empdata.sal;

    UPDATE emp SET sal = sal - vUP
    WHERE empno = empdata.empno;
  END LOOP;
END;

TopLevel.sqlj

JPublisherによって自動で作成されます。

 
package sample;

import java.sql.SQLException;
import sqlj.runtime.ref.DefaultContext;
import sqlj.runtime.ConnectionContext;
import java.sql.Connection;

public class TopLevel
{
  /* connection management */
  // 本ドキュメントでは省略します

  /* constructors */
  // 本ドキュメントでは省略します

  public void testEmpProc1 (
    Integer deptNo)
  throws SQLException
  {
    #sql [getConnectionContext()] { CALL TEST_EMP_PROC1(
      :deptNo) };
  }

  public void testEmpProc2 (
    Integer deptNo)
  throws SQLException
  {
    #sql [getConnectionContext()] { CALL TEST_EMP_PROC2(
      :deptNo) };
  }

TestMain.java

 
package sample;
import java.sql.*;
import oracle.jdbc.OracleDriver;

public class TestMain 
{
  public static void main(String[] args)
  {
    TestMain testMain = new TestMain();
    try
    {
      // 設定されたDB接続情報を使ってオブジェクトをインスタンス化  
      Connection conn = getConnection();
      TopLevel tl = new TopLevel( conn ); 
      // PL/SQL の TEST_EMP_PROC* に対応する Javaメソッドをコール  
      tl.testEmpProc1( new Integer(20) );
      tl.testEmpProc2( new Integer(20) );
      // 接続を閉じる 
      conn.commit();
      conn.close();
    }
    catch (SQLException e)
    {
      e.printStackTrace();
    }
  }
  
  public static Connection getConnection() throws SQLException
  {
    // DB接続情報は環境に合わせて変更してください 
    String username = "scott";
    String password = "tiger";
    String thinConn = "jdbc:oracle:thin:@<<DBHostName>>:1521:<<serviceName>>";
    Driver d = new OracleDriver();
    Connection conn = DriverManager.getConnection(thinConn,username,password);
    conn.setAutoCommit(false);
    return conn;
  }
}