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


付録 A: 採用されなかった仕様

A.1     get/set メソッドに代わる Holder 型の使用

JDBC の初期の原案では、パラメータを渡したり結果を取得したりする手段として、Holder 型の方式が使われていました。この方式は、ポインタの使用とよく似た方法を ODBC の変数に適用しようとしたものです。しかし、テスト用のサンプルを記述してみた結果、Holder 型の作成やバインドが非常にやっかいであることがわかりました。特に、単純な結果行を処理する場合にそのことがいえます。

したがって、代わりの方法として、7.2 項7.1 項で説明した getXXX メソッドと setXXX メソッドを使うことになりました。さまざまなサンプルプログラムを比較した結果、プログラムで使うには getXXX と setXXX の方式の方が簡単であるという結論に達しました。また、JDBC API の一部として多くの Holder 型を定義する必要もなくなります。以上のような結果から、Holder 型は使わず、getXXX と setXXX の方式を使うことに決まりました。

A.1.1 Holder 型を使ったパラメータの引き渡し

java.sql の API の一部として、SQL 文に対するパラメータを保持する一連の Holder 型が定義されています。抽象基底クラスの Holder と、SQL で使われる可能性のある Java 型ごとに個別のサブタイプがあります。たとえば、String 型のパラメータを保持するための StringHolder や、byte 型のパラメータを保持するための ByteHolder などです。

SQL 文にパラメータが渡るように、java.sql.Statement クラスでは Holder オブジェクトを特定のパラメータと関連付けることができます。文が実行されると、IN または INOUT のパラメータの値が対応する Holder オブジェクトから読み込まれて、文の実行が完了すると、OUT または INOUT のパラメータの値が対応する Holder オブジェクトに書き込まれます。

次は、Holder を使う IN パラメータの例です。

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

A.1.2 Holder オブジェクトを使った結果行の取得

SQL 文を実行する前に、アプリケーションの中で、Holder オブジェクトを特定の列に結合できます。SQL 文の実行が完了したあと、アプリケーションプログラムでは、ResultSet.next() メソッドを使って ResultSet に対する繰り返し処理を行い、次の行に移動できます。アプリケーションが次の行に移動すると、その行の値が Holder オブジェクトに取り出されます。これは、ODBC で使われている SQLBindColumn の仕組みと似ています。

次はこの処理の簡単な例です。

// 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]);
}

A.2     代替方式: fooHolder などの型の代わりに foo[] を使用

将来的には、なんらかの形式で列単位の結合をサポートし、複数の行を一度に読み込めるようにすることが予定されています。Holder 方式を試している間、列単位の結合を可能にする方法として次の代替案が検討されました。

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 の機構を利用することで、列単位の結合へのスケールアップが容易になります。

A.3     複数行の一括取得のサポート

現在、個別の行の個別の列から一度に 1 フィールドずつ取得するメソッドが提供されています。目標のデータベースにアクセスする回数を減らすため、ドライバでは、大きなチャンクとしての行の先取りが普通に行われるようになると思われます。一方、大きなチャンクでデータを取り込む処理を、JDBC API を使ってプログラムで行えるようにすることも有効です。

Java でこのような機能を実現するもっとも簡単な機構は、なんらかの方法で列単位の結合をサポートすることです。この方法を使うと、たとえば、プログラムで各列の次の 20 個の値を保持する配列を指定して、一度に 20 行すべてを読み取ることができます。

ただし、JDBC の最初のバージョンではこのような機能は提供されません。ドライバが常に適切なチャンクで行を先取りすることをお勧めします。

A.4     ResultSet.get メソッドでの列番号

旧バージョンの JDBC の仕様では、さまざまな get 系メソッドには引数がなく、左から右に順番に次の列の値を返すだけでした。できあがったサンプルコードがわかりにくいものであったため、列番号の引数が導入されることになりました。サンプルコードでは、SELECT 文で指定されている列と突き合わせるために、get 呼び出しの数を数えなければならない事態が頻繁に発生しました。

A.5     set 系メソッドのオーバーロード

旧バージョンの仕様では、setByte や setBoolean など異なる名前のメソッドを用意するのではなく、これらのメソッドすべてを単に setParameter という名前で呼び出し、引数のデータ型の違いだけでメソッドが区別されるよう、メソッドのオーバーロードが使われていました。Java の場合これは普通の方法ですが、間違いやすく、エラーの原因になりやすいものであるという指摘が多数ありました。特に、SQL のデータ型と Java のデータ型のマッピングが 1 つだけでない場合に問題がありました。検討の結果、これらの意見を受け入れることになりました。

A.6     registerOutParameter メソッド

registerOutParameter メソッドの必要性には疑問がありました。JDBC の開発の過程で、このメソッドの使用をやめて、代わりに、ドライバがデータベースのメタデータを使って OUT パラメータのデータ型を決定するという方法が提案されました。しかし、レビューの意見を検討した結果、パフォーマンスの理由から、registerOutParameter メソッドを使って OUT パラメータの型を指定する方が適切であると判断しました。

A.7     大きな OUT パラメータのサポート

現在、非常に大きな OUT パラメータはサポートされていません。非常に大きな OUT パラメータを扱う機構を提供するとすれば、プログラムで java.io.OutputStreams を登録し、文の実行時に JDBC のランタイムが OUT パラメータのデータをそこに書き込めるようにするという方法が考えられます。しかし、ResultSet の一部として大きな結果を処理する仕組みがすでにあることを考えれば、このような方法に価値を見いだすことは難しいと思われます。

A.8     GetObject と getXXX のサポート

さまざまな get 系および set 系のメソッドと汎用の getObject メソッドおよび setObject メソッドが重複しているため、get および set 系のメソッドを廃止して getObject メソッドと setObject メソッドだけにすることが検討されました。しかし、よくあることですが、プログラマが SQL 型を知っている場合は、汎用メソッドに伴うキャストと抽出の処理が非常に煩雑になります。次はその例です。

int i = ((Integer)r.getObject(1, java.sql.Types.INTEGER)).intValue()

そのためこの場合は、余計なものは残さないという方針を少しだけ曲げて、大多数のアプリケーションプログラマに勧められるインタフェースとして、get および set 系のメソッドを残すことに決定しました。一方で、ツール開発者と高度なアプリケーションのためには、getObject メソッドと setObject メソッドも提供されます。

A.9     isNull と wasNull

SQL の NULL を処理するよい方法の決定までには、若干の困難がありました。しかし、JDBC 0.50 では、十分に使いやすいと思われる ResultSet.isNull メソッドが提供されました。列を読み取る前、または読み取ったあとに isNull メソッドを呼び出して、列が NULL かどうかを判定できます。

if (!ResultSet(isNull(3)) {
	count += ResultSet.getInt(3);
}

残念ながら、isNull をすべてのデータベースに確実に実装するのは不可能であることが明らかになりました。一部のデータベースでは、列を読み取る以外に列が NULL かどうかを判定する手段がなく、しかも特定の列の読み取りは 1 回しか認められていません。列の値を読み取り、あとで使えるように保存しておく方法を検討しましたが、データの変換が必要な場合に問題があります。

さまざまな解決策を調査したあと、不本意ながら、isNull メソッドを wasNull メソッドに置き換えることに決定しました。wasNull メソッドは、単に、特定の ResultSet (または CallableStatement) から読み取った最後の値が SQL の NULL かどうかだけを返します。

A.10     Java 型の名前と SQL 型の名前の使用

旧バージョンの仕様では、結果を取得しパラメータにアクセスするための getXXX メソッドと setXXX メソッドで、XXX の部分は SQL 型の名前でした。JDBC の 0.70 では、XXX の部分に Java 型の名前を使う getXXX メソッドと setXXX メソッドに変更されました。

つまり、たとえば getChar は getString になり、setSmallInt は setShort になりました。

新しいメソッドのセマンティクスは、基本的には、古いメソッドと変わりありません。しかし Java 型の名前を使うことで、Java のプログラマにはそれぞれのメソッドの意味がより明確になります。



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