注: この章の内容は、Addison Wesley 社より Java シリーズの 1 巻として出版された『JDBCTM API Tutorial and Reference, Second Edition: Universal Data Access for the JavaTM 2 Platform』(ISBN 0-201-43328-1) に基づいています。
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
は、 Statement
のメソッド ( Statement
のメソッドは一般的に SQL 文を扱います ) を継承し、また IN パラメータを処理する PreparedStatement
のメソッドを継承します。 CallableStatement
に定義されているすべてのメソッドは、 OUT パラメータまたは INOUT パラメータの出力部分の処理 (OUT パラメータの JDBC の型の登録、それらの値の取り出し、または戻り値が JDBC NULL
であったかどうかの確認) を行います。 ResultSet
に定義された getXXX
メソッドが結果セットからの値を取り出すのに対して、CallableStatement
に定義された getXXX
メソッドは OUT パラメータから値を取り出すか、ストアドプロシージャの値を返すか、あるいはその両方を行います。
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」の章を参照してください。
IN パラメータの値の CallableStatement
オブジェクトへの引き渡しは、PreparedStatement
から継承された setXXX
メソッドを使用して行われます。 渡される値の型は、どの setXXX
(float
の値を渡すためには setFloat
、boolean
の値を渡すためには setBoolean
など) を使用するかによって決まります。 パラメータを使用するプログラムでは、多くの場合、IN パラメータだけが使用されます。
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
としての使用が許されません。
ストアドプロシージャが OUT パラメータを返す場合、CallableStatement
を使用する前に、各 OUT パラメータの JDBC の型を登録する必要があります。 この登録が必要なのは、一部の DBMS では、 SQL 型 ( JDBC 型が表しています ) が必要なためです。JDBC が必要としているわけではありません。 JDBC のデータ型、使用頻度の高い SQL のデータ型を表す汎用的な SQL の型識別子についての詳細は、「SQL と Java の型のマッピング」を参照してください。
JDBC の型の登録は、メソッド registerOutParameter
によって行います。 文の実行が完了してから、CallableStatement
の getXXX
メソッドを使用して 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
メソッドが組み込まれており、バイナリラージオブジェクトおよびキャラクタラージオブジェクトを取得することができます。
メソッド (setXXX
、getXXX
、および 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
を参照しているわけではありません。
入力を渡し、出力を受け取るパラメータ (INOUT パラメータ) は、( PreparedStatement
から継承された ) 適切な setXXX
を呼び出す必要があります。 さらに registerOutParameter
メソッドも呼び出す必要があります。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);
一部の DBMS に課せられている制限のため、移植性を大きくするために以下を推奨します。OUT パラメータを取り出す前に、CallableStatement
オブジェクトの実行によって生成された ResultSet
オブジェクトのすべての結果を取り出してください。 結果セットの値がすべて取り出されると、 ResultSet.next
メソッドは false
を返します。
CallableStatement
オブジェクトが複数の ResultSet
オブジェクトを返す場合 (execute
メソッドの呼び出しによって実行された場合のみ起り得る)、OUT パラメータを取り出す前に、すべての結果セットにアクセスする必要があります。 この場合、すべての結果に確実にアクセスするように、結果がなくなるまで Statement
のメソッド getResultSet
、getUpdateCount
、および getMoreResults
を呼び出す必要があります。 すべての結果がなくなったあとで、getMoreResults
メソッドを呼び出すと false
が返され、getUpdateCount
メソッドを呼び出すと -1
が返されます。
ResultSet.getXXX
メソッドを使用して ResultSet
オブジェクトからすべての値が取り出され、更新カウントが返されなくなったら、CallableStatement
.getXXX
メソッドを使用して OUT パラメータの値を取り出すことができます。
OUT パラメータに返される値は JDBC NULL
である場合があります。 JDBC NULL
値は、getXXX
メソッドが返す値が getXXX
メソッドの型によって null
、0
、false
のどれかになるように変換されます。ResultSet
オブジェクトについて、0
または false
の値がもともと JDBC NULL
であったかどうかを知る唯一の方法は、 wasNull
メソッドを使用して確認する方法です。 wasNull
は、getXXX
メソッドが読み取った最後の値が JDBC NULL
の場合 true
を返し、そうでない場合 false
を返します。