目次 | 前の項目 | 次の項目 | JDBCTM ガイド: 入門 |
したがって、代わりの方法として、7.2 項と 7.1 項で説明した getXXX メソッドと setXXX メソッドを使うことになりました。さまざまなサンプルプログラムを比較した結果、プログラムで使うには getXXX と setXXX の方式の方が簡単であるという結論に達しました。また、JDBC API の一部として多くの Holder 型を定義する必要もなくなります。以上のような結果から、Holder 型は使わず、getXXX と setXXX の方式を使うことに決まりました。
SQL 文にパラメータが渡るように、java.sql.Statement クラスでは Holder オブジェクトを特定のパラメータと関連付けることができます。文が実行されると、IN または INOUT のパラメータの値が対応する Holder オブジェクトから読み込まれて、文の実行が完了すると、OUT または INOUT のパラメータの値が対応する Holder オブジェクトに書き込まれます。
java.sql.Statement stmt = conn.createStatement();
// We pass two parameters. One varies each time around
// the for loop, the other remains constant.
IntHolder ih = new IntHolder();
stmt.bindParameter(1, ih);
StringHolder sh = new StringHolder();
stmt.bindParameter(2, sh);
sh.value ="Hi"
for (int i = 0; i < 10; i++) {
ih.value = i;
stmt.executeUpdate("UPDATE Table2 set a = ? WHERE b = ?");
}
次は、Holder を使う OUT パラメータの例です。
java.sql.Statement stmt = conn.createStatement();
IntHolder ih = new IntHolder();
stmt.bindParameter(1, ih);
StringHolder sh = new StringHolder();
stmt.bindParameter(2, sh);
for (int i = 0; i < 10; i++) {
stmt.executeUpdate("{CALL testProcedure(?, ?)}");
byte x = ih.value;
String s = sh.value;
}
// 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();
IntHolder ih = new IntHolder();
stmt.bindHolder(1, ih);
StringHolder sh = new StringHolder();
stmt.bindHolder(2, sh);
BytesHolder bh = new BytesHolder();
stmt.bindHolder(3, bh);
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table7");
while (r.next()) {
// print the values for the current row.
int i = ih.value;
String s = sh.value;
byte b[] = bh.value;
System.out.println("ROW = " + i + " " + s + " " + b[0]);
}
Holder オブジェクトは、さまざまな Java 型の単一のインスタンスを保持できます。一方、単一要素の配列を代わりのホルダーとして使うこともできます。この方法にはいくつかの欠点がありますが、1 つ大きな利点があります。
最初の欠点は、「foo f[] = new foo[1];」という記述が混乱を招きやすいことです。これに対応する Holder の宣言「fooHolder f = new fooHolder();」は、f が何でなぜそれに割り当てているかが比較的簡単にわかります。
第 2 の欠点は、1 つのメソッド Statement.bindColumn を、配列の型ごとに別のメソッドで置き換える必要があることです。一方、すべての Holder 型は java.sql.Holder を継承しているので、java.sql.Holder 型の引数を受け取る一般的なメソッドには、Holder 型を引数として渡すことができます。ただし、配列を使うと、少なくとも多数のホルダークラスを定義することだけは避けられます。
最後の欠点は、foo[] の場合は Java のままの型情報しか扱えないということです。一方、SQL での使用に合わせた具体的な Holder 型を定義することで、CurrencyHolder 型のように特別なフィールドやセマンティクスを定義できます。
一方、配列方式の大きな利点は、パラメータのコンテナとして foo[1] を使う場合、列単位の結合でテーブルの複数の列を結合する手段として、foo[x] をごく自然に受け入れることができることです。この方法であれば、インタフェースをあらためてモデル化しなくても、列単位の結合のサポートを追加できます。
Holder の代わりに配列を使った場合、bindColumn の機構を利用することで、列単位の結合へのスケールアップが容易になります。
Java でこのような機能を実現するもっとも簡単な機構は、なんらかの方法で列単位の結合をサポートすることです。この方法を使うと、たとえば、プログラムで各列の次の 20 個の値を保持する配列を指定して、一度に 20 行すべてを読み取ることができます。
ただし、JDBC の最初のバージョンではこのような機能は提供されません。ドライバが常に適切なチャンクで行を先取りすることをお勧めします。
int i = ((Integer)r.getObject(1, java.sql.Types.INTEGER)).intValue()
そのためこの場合は、余計なものは残さないという方針を少しだけ曲げて、大多数のアプリケーションプログラマに勧められるインタフェースとして、get および set 系のメソッドを残すことに決定しました。一方で、ツール開発者と高度なアプリケーションのためには、getObject メソッドと setObject メソッドも提供されます。
if (!ResultSet(isNull(3)) {
count += ResultSet.getInt(3);
}
残念ながら、isNull をすべてのデータベースに確実に実装するのは不可能であることが明らかになりました。一部のデータベースでは、列を読み取る以外に列が NULL かどうかを判定する手段がなく、しかも特定の列の読み取りは 1 回しか認められていません。列の値を読み取り、あとで使えるように保存しておく方法を検討しましたが、データの変換が必要な場合に問題があります。
さまざまな解決策を調査したあと、不本意ながら、isNull メソッドを wasNull メソッドに置き換えることに決定しました。wasNull メソッドは、単に、特定の ResultSet (または CallableStatement) から読み取った最後の値が SQL の NULL かどうかだけを返します。
つまり、たとえば getChar は getString になり、setSmallInt は setShort になりました。
新しいメソッドのセマンティクスは、基本的には、古いメソッドと変わりありません。しかし Java 型の名前を使うことで、Java のプログラマにはそれぞれのメソッドの意味がより明確になります。