連載
» 2001年10月19日 00時00分 公開

Javaデータアクセスの基礎(4):ステートメントの高速化 (2/2)

[佐藤直生,日本オラクル]
前のページへ 1|2       

ストアド・プロシージャを利用する

 ストアド・プロシージャとは、ご存知のとおりデータベース・カーネル上で実行される、何らかのロジックを含んだスキーマ・オブジェクトです。Oracle Databaseでは、PL/SQL(オラクルによるSQL拡張言語)、またはJavaを用いてストアド・プロシージャを実装することができます。

 ストアド・プロシージャには、戻り値を持つものと持たないものがあります。戻り値を持つストアド・プロシージャを、特に「ストアド・ファンクション」と呼ぶこともあります。

 ストアド・プロシージャは、パラメータをとる場合があります。パラメータには、入力パラメータである「INパラメータ」、出力パラメータである「OUTパラメータ」、両者を兼ねた「INOUTパラメータ」の3種類があります。

ストアド・プロシージャの実行

 ここでは、SCOTTスキーマのPL/SQLストアド・プロシージャGIVE_RAISEを実行してみましょう。次のソース・コードを見れば分かるように、GIVE_RAISEプロシージャは、INパラメータとして部門番号(DEPTNO列)と昇給率(%)をとり、(EMP表のSAL列を更新して)その部門に所属する社員の給与を増加させるプロシージャです。

SQL> select line, text from user_source where name='GIVE_RAISE';

LINE TEXT
---- ------------------------------------------------------------
1 procedure give_raise (
2 p_deptno in number,
3 p_raise_percent in number )
4 as
5 begin
6 update emp set sal = sal + (sal * p_raise_percent * .01)
7 where deptno = p_deptno;
8
9 commit;
10 end give_raise;

10行が選択されました。

 ストアド・プロシージャは、あらかじめコンパイルされてデータベースに格納されており、パラメータを設定するだけで実行が可能です。この意味で、ストアド・プロシージャの実行は、プリペアド・ステートメントと非常に似た特徴を持っています。ストアド・プロシージャを実行するために利用するjava.sql.CallableStatementインターフェイスが、java.sql.PreparedStatement インターフェイスをextendsしているのも、このためです。

ステートメントとストアド・プロシージャの両方を実行してみる

 まず、次のサンプル・コードを見てみましょう。これは、部門番号(DEPTNO)が10である社員の給与を2倍に増額(+100%)した後、1/2に減額(?50%)するものです。

(※赤字部分はコメントです。コードの一部ではありませんのでご注意ください)

// Javaデータアクセスの基礎 サンプル・コード(5)
// ストアド・プロシージャを実行するJavaアプリケーション

// JDBC APIをインポート
import java.sql.*;


class JavaDataAccess05 {
 public static void main (String args[]) {


  Connection conn = null;
  Statement stmt = null;
  CallableStatement cstmt = null;
  ResultSet rset = null;
  String sql_str1 = null;
  String sql_str2 = null;


   try{


    Class.forName("oracle.jdbc.driver.OracleDriver");
    System.out.println("JDBCドライバをロードしました...");


    conn = DriverManager.getConnection
    ("jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
    System.out.println("データベースに接続しました...");


(↑ここまでで、データベースに接続するための初期処理を行います)

(↓以下は、通常のステートメントの実行と結果の表示を行います)


    // ステートメントを作成
    stmt = conn.createStatement();


    // 問合せを実行
    sql_str1 = "select DEPTNO, ENAME, SAL from EMP "
    +"where DEPTNO='10'";
    rset = stmt.executeQuery(sql_str1);


    // 問合せ結果を表示
    while ( rset.next() ) {
     System.out.println(rset.getInt("DEPTNO") + "\t"
     + rset.getString("ENAME") + "\t"
     + rset.getInt("SAL"));
    }


(↓ここからストアド・プロシージャの実行と結果の表示を行います)

   // CallableStatementを作成
    sql_str2 = "{call GIVE_RAISE(?, ?)}";
    cstmt = conn.prepareCall(sql_str2);


    // INパラメータを設定 (給与を2倍に増額するよう設定します)
    cstmt.setInt(1, 10);
    cstmt.setInt(2, 100); // +100%


    cstmt.executeUpdate();
    System.out.println("GIVE_RAISEを実行しました...");


    // 問合せを実行
    rset = stmt.executeQuery(sql_str1);


    // 問合せ結果を表示
    while ( rset.next() ) {
     System.out.println(rset.getInt("DEPTNO") + "\t"
     + rset.getString("ENAME") + "\t"
     + rset.getInt("SAL"));
    }


    // INパラメータを設定
    cstmt.setInt(1, 10);(給与を1/2に減額するよう設定します)
    cstmt.setInt(2, -50); // -50%


    cstmt.executeUpdate();
    System.out.println("GIVE_RAISEを実行しました...");


    // CallableStatementをクローズ
    cstmt.close();


    // 問合せを実行
    rset = stmt.executeQuery(sql_str1);


    // 問合せ結果を表示
    while ( rset.next() ) {
     System.out.println(rset.getInt("DEPTNO") + "\t"
     + rset.getString("ENAME") + "\t" + rset.getInt("SAL"));
    }


(↓終了処理、例外処理を行います)

    // 結果セット、ステートメントをクローズ
    rset.close();
    stmt.close();


    conn.close();
    System.out.println("接続をクローズしました...");


    // 例外を処理
   } catch (Exception ex) {
   System.out.println("例外が発生しました...");


   // エラー・メッセージを出力
   System.out.print(ex.toString());


   try {
    if ( rset != null ) { rset.close(); }
    if ( stmt != null ) { stmt.close(); }
    if ( cstmt != null ) { cstmt.close(); }
    if ( conn != null ) { conn.close(); }
   } catch (SQLException se) {}
  } // catch
 } // main
}
リスト JavaDataAccess05.java

実行結果

 このサンプル・コードの実行結果は、次のとおりです。

C:\JDBC>java JavaDataAccess05
JDBCドライバをロードしました...
データベースに接続しました...
10 CLARK 2450
10 KING 5000
10 MILLER 1300
GIVE_RAISEを実行しました...
10 CLARK 4900
10 KING 10000
10 MILLER 2600
GIVE_RAISEを実行しました...
10 CLARK 2450
10 KING 5000
10 MILLER 1300
接続をクローズしました...

 では、順を追ってコードを見てみましょう。CallableStatementオブジェクトを作成するには、ConnectionオブジェクトのprepareCall()メソッドを利用します。プリペアド・ステートメントを作成するprepareStatement()メソッドと同様に、パラメータとしてSQL文を渡している点に注意しましょう。

 prepareCall()メソッドに渡すSQL文では、エスケープ構文を使います。2つのパラメータをとる(戻り値を持たない)ストアド・プロシージャの構文は次のとおりです。

{call my_procedure(?, ?)}

 (戻り値を持つ)ストアド・ファンクションであれば、次のような構文となります。

{? = call my_procedure(?, ?)}

 “?”は、プリペアド・ステートメントの場合と同様に、(1から始まる)パラメータのプレース・ホルダを表します。

// CallableStatementを作成
sql_str2 = "{call GIVE_RAISE(?, ?)}";
cstmt = conn.prepareCall(sql_str2);

 次に、パラメータの設定を行います。INパラメータ、INOUTパラメータに対しては、プリペアド・ステートメントの場合と同様に、適切なsetXXX()メソッドを用いて、パラメータの値をセットします。

// INパラメータを設定
cstmt.setInt(1, 10);
cstmt.setInt(2, 100);

 プロシージャ実行後に値を取り出す戻り値、OUTパラメータ、INOUTパラメータに対しては、registerOutParameter()メソッドで、対応するJDBCデータ型を指定する必要があります。仮に、GIVE_RAISEプロシージャが第3パラメータとして整数値のOUTパラメータを持っているとすれば、次のようにします。

// OUTパラメータを設定
cstmt.registerOutParameter(3, java.sql.Types.INTEGER);

 すべてのプレース・ホルダの設定が完了したら、executeUpdate()メソッド(あるいはexecute()メソッド)を用いて、ストアド・プロシージャを実行します。

cstmt.executeUpdate();
System.out.println("GIVE_RAISEを実行しました...");

 戻り値、OUTパラメータ、INOUTパラメータに対しては、適切なgetXXX()メソッドを用いて、実行後に値を取り出す必要があります。仮に、GIVE_RAISEプロシージャが第3パラメータとして整数値のOUTパラメータを持っているとすれば、次のようにします。

int third_parameter = cstmt.getInt(3);

 JDBC 1.2 APIに含まれる、JDBCの基本機能の紹介は、今回で終わりになります。次回からは、JDBC 2.0 APIの新機能を紹介していきます。

前のページへ 1|2       

Copyright© 2018 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。