目次 | 前の項目 | 次の項目 | JDBCTM ガイド: 入門 |
採用されなかった Holder 機構については、付録 A を参照してください。
// We're going to execute a SQL statement that will return a
// collection of rows, with column 1 as an int, column 2 as
// a String, and column 3 as an array of bytes.
java.sql.Statement stmt = conn.createStatement();
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (r.next()) {
// print the values for the current row.
int i = r.getInt("a");
String s = r.getString("b");
byte b[] = r.getBytes("c");
System.out.println("ROW = " + i + " " + s + " " + b[0]);
}
列を指定する方法は 2 つあります。列インデックス (効率を重視する場合) と列名 (利便性を重視する場合) のどちらかを使用します。たとえば、列インデックスを引数にとる getString メソッドと、列名を引数にとる getString メソッドの両方があります。
寄せられた意見を参考にして、列インデックスと列名の両方のサポートが必要だとの結論に達しました。効率の高いデータベースアクセスが必要なので列インデックスを使用したいとの強い要望がある一方で、列名使用の便利さへの要望もありました (一部の SQL クエリーでは、列名のないテーブルや同名の列を複数持つテーブルが返される。これらの場合、プログラマは列インデックスを使わなければならない)。
最大限の移植性を実現するには、行の中の列は左から右の順で読み出し、各列の読み出しは 1 回だけにします。これは、基礎を成す一部のデータベースプロトコルの実装制限を反映するものです。
表 1 は、getXXX メソッドを通じて行われる、SQL のデータ型から Java のデータ型への変換の一覧です。たとえば、SQL VARCHAR の値を getInt を使って整数として読むことはできますが、SQL FLOAT を java.sql.Date として読むことはできません。
不正な変換を試みた場合、あるいはデータ変換に失敗した場合 (SQL VARCHAR の値 foo に getInt を行なったなど) は、SQLException が発行されます。
ResultSet.getXXX メソッドの 1 つを使って SQL の NULL を読み取った場合は、次のどれかを受け取ります。
この方法は、ResultSet クラスが java.io.Input ストリームを返し、このストリームからデータをブロックに分けて受け取るという方法で実現できます。しかしこれらのストリームは、ResultSet に次の get 呼び出しが行われると自動的に閉じられるので、ただちにアクセスする必要があります。 この動作は、大量のデータのアクセスに関する根本的な実装の制限を意味するものです。
Java ストリームは型なしのバイトデータを返すので、(たとえば) ASCII と Unicode の両方に使用できます。ストリームの取得には 3 種類のメソッドが定義されています。GetBinaryStream は、データベースから未変換のバイトデータのストリームを返します。GetAsciiStream は、1 バイトの ASCII 文字のストリームを返します。GetUnicodeStream は、2 バイトの Unicode 文字のストリームを返します。
java.sql.Statement stmt = conn.createStatement();
ResultSet r = stmt.executeQuery("SELECT x FROM Table2");
// Now retrieve the column 1 results in 4 K chunks:
byte[] buff = new byte[4096];
while (r.next()) {
java.io.InputStream fin = r.getAsciiStream("x");
for (;;) {
int size = fin.read(buff);
if (size == -1) {
break;
}
// Send the newly filled buffer to some ASCII output stream:
output.write(buff, 0, size);
}
}
しかし、文が ResultSet を返すかどうかが、その文が実行されるまでアプリケーションにわからない場合があります。また一部のストアドプロシージャは、複数の異なる ResultSet か更新カウント、またはその両方を返します。
これらの必要に応えるため、アプリケーションが文を実行したあとに ResultSet と更新カウントの任意のコレクションを処理できるような機構を提供します。この機構は、完全に汎用の execute メソッドに基づき、getResultSet、getUpdateCount、getMoreResults の 3 つのメソッドでサポートされます。これらのメソッドを使うと、アプリケーションは文の結果を 1 つずつ調べて、受け取った結果が ResultSet なのか更新カウントなのかを判断できます。
java.sql.PreparedStatement stmt = conn.prepareStatement(
"UPDATE table3 SET m = ? WHERE x = ?");
// We pass two parameters. One varies each time around the for loop,
// the other remains constant.
stmt.setString(1, "Hi");
for (int i = 0; i < 10; i++) {
stmt.setInt(2, i);
int rows = stmt.executeUpdate();
}
各引数の Java のデータ型が、目的のデータベースで期待されている SQL のデータ型に確実にマッピングされるようにすることは、プログラマの責任です。最大限の移植性を実現するためには、プログラマは目的のデータベースで期待されている SQL のデータ型に正確に対応する Java のデータ型を使用するべきです。
IN パラメータにデータ型の変換が必要な場合は、PreparedStatement.setObject メソッドを使って Java オブジェクトを指定の SQL データ型に変換してからデータベースに値を送ることができます。
また、Java オブジェクトを引数にとる setXXX メソッドでは、Java の null 値が setXXX メソッドに渡されると、 SQL NULL がデータベースに送られます。
このために、パラメータとして Java 入出力ストリームを使用できます。文が実行されると、JDBC ドライバはこれらの入出力ストリームに対して呼び出しを繰り返し、これらの内容を読み出してから実際のパラメータデータとして送信します。
未解釈のバイトを含むストリーム、ASCII 文字を含むストリーム、および Unicode 文字を含むストリームのために、それぞれ別の setXXX メソッドが用意されています。
入力パラメータとしてストリームを設定する場合、アプリケーションのプログラマは、ストリームから読み出すバイト数を指定してデータベースに送る必要があります。
データの送信サイズを先に指定する方法は避けたかったのですが、一部のデータベースではデータを送信する前に転送サイズを知る必要があるので、この方法はやむを得ませんでした。
ストリームを使い、ファイルの内容を IN パラメータとして送る例を次に示します。
java.io.File file = new java.io.File("/tmp/foo");
int fileLength = file.length();
java.io.InputStream fin = new java.io.FileInputStream(file);
java.sql.PreparedStatement stmt = conn.prepareStatement(
"UPDATE Table5 SET stuff = ? WHERE index = 4");
stmt.setBinaryStream(1, fin, fileLength);
// When the statement executes, the "fin" object will get called
// repeatedly to deliver up its data.
stmt.executeUpdate();
7.2 で説明したように、IN パラメータの引き渡しには、PreparedStatement に定義されている setXXX メソッドを使用できます。
しかし、ストアドプロシージャが OUT パラメータを返す場合には、OUT パラメータのそれぞれに CallableStatememt.registerOutParameter メソッドを使用して、文を実行する前にその OUT パラメータの SQL データ型を登録する必要があります (付録 A.6 参照)。文の実行後は、対応する CallableStatement.getXXX メソッドを使用してパラメータ値を受け取る必要があります。
java.sql.CallableStatement stmt = conn.prepareCall(
"{call getTestData(?, ?)}");
stmt.registerOutParameter(1,java.sql.Types.TINYINT);
stmt.registerOutParameter(2,java.sql.types.DECIMAL, 2);
stmt.executeUpdate();
byte x = stmt.getByte(1);
BigDecimal n = stmt.getBigDecimal(2,2);
CallableStatement.getXXX メソッドを使って SQL の NULL 値を読んだ場合は、7.1.2 項の ResultSet.getXXX メソッドの場合と同じ規則に従って null、ゼロ、FALSE のどれかの値を受け取ります。
大きなサイズの値は、ResultSet を通じて取得することをお勧めします。
ResultSet からの読み出し時にデータの切り捨てが発生すると、ResultSet の警告リストに DataTruncation オブジェクト (SQLWarning のサブタイプ) が追加され、メソッドは読み出せるだけのデータを返します。同様に、データベースからの OUT パラメータの受け取り時にデータの切り捨てが発生すると、CallableStatement の警告リストに DataTruncation オブジェクトが追加され、メソッドは読み出せるだけのデータを返します。