目次 | 前の項目 | 次の項目 JDBCTM ガイド: 入門


7 パラメータの引き渡しと結果の受け取り

インタフェースの完全な説明については、JDBC API の別のドキュメントを参照してください。

採用されなかった Holder 機構については、付録 A を参照してください。

7.1     クエリーの結果

クエリー文の実行結果はデータ行のセットで、java.sql.ResultSet オブジェクトを通じてアクセスできます。ResultSet オブジェクトには get メソッドのセットがあり、現在の行のさまざまな列にアクセスできます。ResultSet.next メソッドを使うと、ResultSet の行の間を移動できます。

// 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 回だけにします。これは、基礎を成す一部のデータベースプロトコルの実装制限を反映するものです。

7.1.1 クエリー結果のデータ変換

ResultSet.getXXX メソッドは、データベースから返される SQL のデータ型を getXXX メソッドから返される Java のデータ型に変換しようとします。

表 1 は、getXXX メソッドを通じて行われる、SQL のデータ型から Java のデータ型への変換の一覧です。たとえば、SQL VARCHAR の値を getInt を使って整数として読むことはできますが、SQL FLOAT を java.sql.Date として読むことはできません。

不正な変換を試みた場合、あるいはデータ変換に失敗した場合 (SQL VARCHAR の値 foo に getInt を行なったなど) は、SQLException が発行されます。

7.1.2 NULL 結果値

結果の値が SQL の NULL かどうかを判断するには、まず列データを読み、次に読み取り処理が SQL の NULL を返したかどうかを ResultSet.wasNull メソッドを使って調べます (付録 A.9 を参照)。

ResultSet.getXXX メソッドの 1 つを使って SQL の NULL を読み取った場合は、次のどれかを受け取ります。

7.1.3 非常に大きな列値の取得

JDBC では getBytes と getString を使って、Statement.getMaxFieldSize 値によって課される上限値までの任意の大きさの LONGVARBINARY または LONGVARCHAR データを取得できます。しかし多くの場合、アプリケーションのプログラマにとっては、非常に大きなデータは固定サイズの小さなブロックに分けて取得する方が便利です。

この方法は、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);
	}
}

7.1.4 選択可能な ResultSet または複数の ResultSet

通常、SQL 文は executeQuery (単一の ResultSet を返す) と executeUpdate (データベース修正文に使用でき、更新された行数を返す) のどちらかを使って実行されます。

しかし、文が ResultSet を返すかどうかが、その文が実行されるまでアプリケーションにわからない場合があります。また一部のストアドプロシージャは、複数の異なる ResultSet か更新カウント、またはその両方を返します。

これらの必要に応えるため、アプリケーションが文を実行したあとに ResultSet と更新カウントの任意のコレクションを処理できるような機構を提供します。この機構は、完全に汎用の execute メソッドに基づき、getResultSet、getUpdateCount、getMoreResults の 3 つのメソッドでサポートされます。これらのメソッドを使うと、アプリケーションは文の結果を 1 つずつ調べて、受け取った結果が ResultSet なのか更新カウントなのかを判断できます。

7.2     IN パラメータの引き渡し

パラメータを SQL 文に引き渡すために、java.sql.PreparedStatement クラスには一連の setXXX メソッドがあります。これらのメソッドを使用して、各文を実行する前にパラメータフィールドにパラメータを書き込みます。ある文のパラメータ値は一度定義すれば、PreparedStatement.clearParameters の呼び出しによってクリアされるまで、その文の実行に何度も使用できます。

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();
}

7.2.1 IN パラメータのデータ型の一致

PreparedStatement.setXXX メソッドは一般的なデータ型変換を行いません。Java の値は単に対応する SQL のデータ型にマッピングされ (表 3 に指定されたマッピングに従う)、その値がデータベースに送られます。

各引数の Java のデータ型が、目的のデータベースで期待されている SQL のデータ型に確実にマッピングされるようにすることは、プログラマの責任です。最大限の移植性を実現するためには、プログラマは目的のデータベースで期待されている SQL のデータ型に正確に対応する Java のデータ型を使用するべきです。

IN パラメータにデータ型の変換が必要な場合は、PreparedStatement.setObject メソッドを使って Java オブジェクトを指定の SQL データ型に変換してからデータベースに値を送ることができます。

7.2.2 IN パラメータとして SQL NULL を送る

PreparedStatement.setNull メソッドを使うと、SQL NULL 値を IN パラメータとしてデータベースに送ることができます。ただし、パラメータの SQL データ型を指定する必要があります。

また、Java オブジェクトを引数にとる setXXX メソッドでは、Java の null 値が setXXX メソッドに渡されると、 SQL NULL がデータベースに送られます。

7.2.3 非常に大きなパラメータを送る

JDBC そのものには、setBytes や setString の呼び出しで送られるデータの量に制限はありません。しかし、大量のデータを扱う際は、データを小さなブロックに分けて渡す方がアプリケーションのプログラマにとって便利です。

このために、パラメータとして 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.3     OUT パラメータの受け取り

ストアドプロシージャ呼び出しを実行するときは、CallableStatement クラスを使用します。 CallableStatement は PreparedStatement のサブタイプです。

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);

7.3.1 OUT パラメータのデータ型の一致

CallableStatement.getXXX メソッドは、一般的なデータ型変換を行いません。データベースから返される SQL データ型を registerOutParameter の呼び出しによって指定してから、その SQL データ型に対応する java データ型の getXXX メソッドを呼び出す必要があります。SQL と Java のデータ型の対応は表 2 に示されています。

7.3.2 OUT パラメータとして NULL 値を受け取る

ResultSet の場合と同様に、OUT パラメータの値が SQL の NULL かどうか判断するために、パラメータを読んだあとに CallableStatement.wasNull メソッドを使用して、その読み込み処理が SQL の NULL を返したかどうか調べる必要があります。

CallableStatement.getXXX メソッドを使って SQL の NULL 値を読んだ場合は、7.1.2 項の ResultSet.getXXX メソッドの場合と同じ規則に従って null、ゼロ、FALSE のどれかの値を受け取ります。

7.3.3 非常に大きな OUT パラメータの受け取り

OUT パラメータをストリームとして取得するための機構は提供しません。

大きなサイズの値は、ResultSet を通じて取得することをお勧めします。

7.3.4 結果のあとに OUT パラメータを受け取る

ストアドプロシージャが結果と OUT パラメータの両方を返す場合は、最大限の移植性を実現するためには、結果を OUT パラメータより前に受け取ります。

7.4     データの切り捨て

データベースに対するデータの読み書きの際、データの切り捨てが起こる場合があります。この処理の取り扱いは状況に依存しますが、一般に、データベースの読み出し時にデータの切り捨てが起こると警告が発行され、データベースへの書き込み時に切り捨てが起こると SQLException が発行されます。

7.4.1 接続最大フィールドサイズ制限の超過

アプリケーションが Connection.setMaxFieldSize を使ってフィールドの最大サイズに制限を課している場合、このサイズを超えてフィールドの読み出しや書き込みを行おうとすると、データは maxFieldSize で指定されたサイズに自動的に切り捨てられますが、SQLException や SQLWarning は発行されません。

7.4.2 読み出し時のデータの切り捨て

一般に JDBC では、データの読み出し時の切り捨てエラーはまれです。その理由は JDBC API では固定サイズのバッファの受け渡しは不要で、必要に応じて適切なデータ空間を割り当てるからです。しかし、状況によってはドライバが内部の実装制限に遭遇する場合があるので、読み出し時にデータが切り捨てられる可能性はあります。

ResultSet からの読み出し時にデータの切り捨てが発生すると、ResultSet の警告リストに DataTruncation オブジェクト (SQLWarning のサブタイプ) が追加され、メソッドは読み出せるだけのデータを返します。同様に、データベースからの OUT パラメータの受け取り時にデータの切り捨てが発生すると、CallableStatement の警告リストに DataTruncation オブジェクトが追加され、メソッドは読み出せるだけのデータを返します。

7.4.3 書き込み時のデータの切り捨て

データベースへの書き込み時には、ドライバやデータベースが受け取れるより多くのデータをアプリケーションが送る可能性があります。この場合は、失敗したメソッドが SQLException として DataTruncation 例外を発行するものとします。



目次 | 前の項目 | 次の項目
jdbc@wombat.eng.sun.com または jdbc-odbc@wombat.eng.sun.com
Copyright © 1996, 1997 Sun Microsystems, Inc. All rights reserved.