[目次] [前の項目] [次の項目]

CallableStatement

注: この章の内容は、Addison Wesley 社より Java シリーズの 1 巻として出版された『JDBCTM API Tutorial and Reference, Second Edition:Universal Data Access for the JavaTM 2 Platform』(ISBN 0-201-43328-1) に基づいて作成したものです。

7.1 CallableStatement の概要

CallableStatement オブジェクトは、すべての RDBMS について標準的な方法でストアドプロシージャーを呼び出す方法を提供します。ストアドプロシージャーは、データベースに格納され、ストアドプロシージャーへの呼び出しは、CallableStatement オブジェクトが保持します。この呼び出しは、エスケープ構文で書かれます。この構文には、結果パラメータを伴う書式と、伴わない書式の 2 つの書式があります。結果パラメータは OUT パラメータの一種で、ストアドプロシージャーの戻り値です。どちらの形式も、入力 (IN パラメータ)、出力 (OUT パラメータ)、またはその両方 (INOUT パラメータ) として使用する変数を持つことができます。疑問符はパラメータのプレースホルダとして使用できます。

JDBC API を使用してストアドプロシージャーを呼び出す構文は、次のとおりです。角括弧は、それに囲まれた部分がオプションであることを示します。 これらのかっこは、それ自体構文の一部ではありません。

{call procedure_name[(?, ?, ...)]}

以下は、結果パラメータを返すプロシージャーの構文です。

{? = call procedure_name[(?, ?, ...)]}

パラメータが付かないストアドプロシージャーの構文は、以下のようになります。

{call procedure_name}

通常、CallableStatement オブジェクトの作成者は、使用されている DBMS がストアドプロシージャーをサポートしていること、およびそれらのプロシージャーが何かということを知っています。しかし、確認が必要な場合は、さまざまな DatabaseMetaData メソッドがこのような情報を提供します。たとえば、メソッド supportsStoredProcedures は、DBMS がストアドプロシージャー呼び出しをサポートする場合、true を返し、メソッド getProcedures は、使用可能なストアドプロシージャーの説明を返します。

CallableStatement は、SQL 文一般を処理する Statement のメソッドを継承し、また IN パラメータを処理する PreparedStatement のメソッドを継承します。CallableStatement に定義されているすべてのメソッドは、OUT パラメータまたは INOUT パラメータの OUT 部分の処理 (OUT パラメータの JDBC の型の登録、それらの値の取り出し、または戻り値が JDBC NULL であったかどうかの確認) します。ResultSet に定義された getXXX メソッドが結果セットからの値を取り出すのに対して、CallableStatement に定義された getXXX メソッドは OUT パラメータから値を取り出すか、ストアドプロシージャーの値を返すか、あるいはその両方を行います。

7.1.1 CallableStatement オブジェクトの生成

CallableStatement オブジェクトは、Connection メソッドの prepareCall によって生成されます。次の例では、CallableStatement のインスタンスが生成されます。 con はアクティブな JDBC の Connection オブジェクトです。

CallableStatement cstmt = con.prepareCall(
	"{call getTestData(?, ?)}");

変数 cstmt には、ストアドプロシージャー getTestData の呼び出しが含まれています。 このストアドプロシージャーには、2 つの入力パラメータが指定され、結果パラメータは指定されていません。? プレースホルダが、IN、OUT、または INOUT パラメータのどれになるかは、ストアドプロシージャー getTestData に依存します。CallableStatement オブジェクトのこのインスタンスは、JDBC 1.0 API を使用して生成されているため、cstmt によって呼び出されるストアドプロシージャー内のクエリーは、すべてデフォルトの ResultSet オブジェクト (スクロールおよび更新を行うことができないオブジェクト) を生成します。

JDBC 2.0 API では、CallableStatement オブジェクトを生成し、そのオブジェクトから、スクロールおよび更新を行うことができる ResultSet オブジェクトを生成できます。次のコードを参照してください。

String sql = "{call getTestData(?, ?)}";
CallableStatement cstmt2 = con.prepareCall(sql,
	ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);

変数 cstmt2 には、cstmt と同様に、ストアドプロシージャー getTestData の呼び出しが含まれています。 ただし cstmt2 の場合、getTestData によって生成された resultSet オブジェクトはどれでも、すべて更新およびスクロールを行うことができます ( しかしながら、開かれている間に行われた変更は反映されません )。スクロールおよび更新を行うことができるかどうかを識別するときに使用する定数については、「ResultSet」の章を参照してください。

7.1.2 IN パラメータ

IN パラメータの値の CallableStatement オブジェクトへの引き渡しは、PreparedStatement から継承された setXXX メソッドを使用して行われます。渡される値の型は、どの setXXX (float の値を渡すためには setFloatboolean の値を渡すためには setBoolean など) を使用するかによって決まります。パラメータを使用するプログラムでは、多くの場合、IN パラメータだけが使用されます。

7.1.3 バッチ更新の実行

CallableStatement オブジェクトのバッチ更新は、PreparedStatement オブジェクトと同様に行われます。実際、CallableStatement オブジェクトの機能は、PreparedStatement オブジェクトの機能と同じ制限を受けます。つまり、バッチ更新機能を使用するときは、CallableStatement オブジェクトは、入力パラメータを指定するもしくはパラメータをまったく指定しない、ストアドプロシージャーしか呼び出せません。さらに、そのストアドプロシージャーは、更新カウントを返さなければなりません。ストアドプロシージャーが更新カウント以外の結果を返したり、ストアドプロシージャーが OUT または INPUT パラメータを取る場合は、CallableStatement.executeBatch メソッド (PreparedStatement から継承) は、BatchUpdateException をスローします。

次のコードでは、バッチ更新機能を使用して、2 つのパラメータを CallableStatement オブジェクトに関連付けています。

CallableStatement cstmt = con.prepareCall(
			"{call updatePrices(?, ?)}");
cstmt.setString(1, "Colombian");
cstmt.setFloat(2, 8.49f);
cstmt.addBatch();

cstmt.setString(1, "Colombian_Decaf");
cstmt.setFloat(2, 9.49f);
cstmt.addBatch();

int [] updateCounts = cstmt.executeBatch();

変数 cstmt には、ストアドプロシージャー updatePrices への呼び出しが含まれています。 ストアドプロシージャーには、2 つのパラメータが関連付けられています。cstmt を実行すると、2 つの更新文が、バッチとしてまとめて実行されます。一方の更新文には、Colombian および 8.49f がパラメータとして指定されています。 もう一方の更新文には、Colombian_Decaf および 9.49f がパラメータとして指定されています。8.49f のように、数値のあとに f が指定されている場合は、その数値が float であることを Java コンパイラに伝えています。 f が指定されていない場合、コンパイラは 10 進数の数値を double と見なすので、float としての使用が許されません。

7.1.4 OUT パラメータ

ストアドプロシージャーが OUT パラメータを返す場合、各 OUT パラメータの JDBC の型は CallableStatement を使用する前に登録する必要があります。この登録が必要なのは、一部の DBMS では、 SQL 型 ( JDBC 型が表しています ) が必要なためです。 JDBC が必要としているわけではありません。JDBC のデータ型、使用頻度の高い SQL のデータ型を表す総称 SQL の型識別子についての詳細は、「SQL と Java の型のマッピング」を参照してください。

JDBC の型の登録は、メソッド registerOutParameter によって行います。文の実行が完了してから、CallableStatementgetXXX メソッドを使用して OUT パラメータの値を取得します。使用に適した CallableStatement.getXXX メソッドは、その OUT パラメータに登録されている JDBC の型に対応する Java プログラミング言語の型のメソッドです(JDBC の型から Java の型への標準マッピングについては、表 8.1 を参照)。つまり、registerOutParameter は、(それがデータベースが返すデータの型に一致するように) JDBC の型を使用し、getXXX がそれを Java の型にキャストします。

例を挙げて説明すると、次のコードは OUT パラメータを登録し、cstmt が呼び出すストアドプロシージャーを実行してから、OUT パラメータに返された値を取り出します。メソッド getByte は、最初の OUT パラメータから Java の byte を取り出し、getBigDecimal は、2 番目の OUT パラメータから java.math.BigDecimal オブジェクト (小数点以下第 3 位まで) を取り出します。cstmt によって呼び出されたストアドプロシージャーが結果セットを返すので、cstmt の実行には executeQuery メソッドを使用しています。

CallableStatement cstmt = con.prepareCall(
	"{call getTestData(?, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
ResultSet rs = cstmt.executeQuery();
// . . . retrieve result set values with rs.getXXX methods
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2);

ResultSet とは異なり、CallableStatement では、大きな OUT 値を少しずつ次々と取得するための特別な機構は提供されていません。つまり、getAsciiStream または getBinary-Stream などのデータストリームを取得するような getXXX メソッドを持っていません。ただし、JDBC 2.0 API の CallableStatement のメソッドでは、OUT パラメータまたは INOUT パラメータとして SQL3 のデータ型を取得することができます。 CallableStatement には、getBlob メソッドおよび getClob メソッドが組み込まれており、バイナリラージオブジェクトおよびキャラクタラージオブジェクトを取得することができます。

7.1.5 パラメータの番号付け

対象パラメータを指定する int を引数に取るメソッド (setXXXgetXXX、および registerOutParameter) の場合、その int は、? プレースホルダパラメータのみを参照します。その int は 1 から数えられます。このパラメータの番号は、ストアドプロシージャー呼び出しに指定されるリテラルパラメータを参照しません。たとえば、次のコードは、1 つのリテラルパラメータ ( 25 ) と 1 つの ? パラメータを持つストアドプロシージャー呼び出しの例です。

CallableStatement cstmt = con.prepareCall(
		"{call getTestData(25, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);

このコードでは、registerOutParameter の第 1 引数である int 1 は、最初の ? パラメータ (ここでは ? パラメータは 1 つしかありません) を参照しています。この int 1 は、ストアドプロシージャーの第 1 パラメータであるリテラルの 25 を参照しているわけではありません。

7.1.6 INOUT パラメータ

入力を供給し、出力を受け取るパラメータは、メソッド registerOutParameter を呼び出す以外に、 (PreparedStatementから継承された) 適切な setXXXを呼び出す必要があります。setXXX メソッドは、パラメータの値を入力パラメータに設定し、メソッド registerOutParameter がその JDBC の型を出力パラメータとして登録します。setXXX メソッドは、Java の値を提供します。 ドライバはその値をデータベースへ送信する前に JDBC の値に変換します。この IN の値の JDBC の型と、メソッド registerOutParameter に供給する JDBC の型は同一でなければなりません。そして、出力値を取り出すためには、対応する getXXX メソッドを使用します。たとえば、パラメータの Java の型が byte の場合は、setByte メソッドを使用して入力値を代入し、TINYINT を JDBC の型として registerOutParameter に渡し、getByte を使用して出力値を取り出す必要があります (型のマッピングの一覧については、「SQL と Java の型のマッピング」を参照)。

以下の例では、ストアドプロシージャー reviseTotal があり、その唯一のパラメータが 1 つの INOUT パラメータである場合を想定しています。メソッド setByte はこのパラメータを 25 に設定し、ドライバはこれを JDBC の TINYINT として後にデータベースへ送信します。次の registerOutParameter は、このパラメータを JDBC の TINYINT として登録します。ストアドプロシージャーが実行されると、新しい JDBC TINYINT の値が返され、メソッド getByte がこの新しい値を Java の byte として取り出します。この例で呼び出されているストアドプロシージャーは更新カウントを返すため、executeUpdate メソッドが使用されています。

CallableStatement cstmt = con.prepareCall(
		"{call reviseTotal(?)}");
cstmt.setByte(1, (byte)25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);

7.1.7 結果セットの後に OUT パラメータを取り出す

一部の DBMS に課せられている制限のため、移植性を大きくするために次を推奨します。 OUT パラメータを取り出す前に、CallableStatement オブジェクトの実行によって生成された ResultSet オブジェクトのすべての結果を取り出してください。結果セットの値がすべて取り出されると、 ResultSet.next メソッドは false を返します。

CallableStatement オブジェクトが複数の ResultSet オブジェクトを返す場合 (execute メソッドの呼び出しによって実行された場合のみ起こり得る)、OUT パラメータを取り出す前に、すべての結果を取り出す必要があります。この場合、すべての結果に確実にアクセスするように、結果がなくなるまで Statement のメソッド getResultSetgetUpdateCount、および getMoreResults を呼び出す必要があります。すべての結果がなくなったあとで、getMoreResults メソッドを呼び出すと false が返され、getUpdateCount メソッドを呼び出すと -1 が返されます。

ResultSet.getXXX メソッドを使用して ResultSet オブジェクトからすべての値が取り出され、更新カウントが返されなくなったら、CallableStatement.getXXX メソッドを使用して OUT パラメータの値を取り出すことができます。

7.1.8 NULL 値を OUT パラメータとして取り出す

OUT パラメータに返される値は JDBC NULL である場合があります。JDBC NULL 値は、getXXX メソッドが返す値が getXXX メソッドの型によって null0false のどれかになるように変換されます。ResultSet オブジェクトについて、0 または false の値がもともと JDBC NULL であったかどうかを知る唯一の方法は、 wasNull メソッドを使用して確認する方法です。 wasNull は、getXXX メソッドが読み取った最後の値が JDBC NULL の場合 true を返し、そうでない場合 false を返します。



[目次] [前の項目] [次の項目]

Copyright © 1999, Sun Microsystems, Inc. All rights reserved.