レベル: 中級 Robert Brunner (rb@ncsa.uiuc.edu), NCSA Research Scientist, Assistant Professor of Astronomy, University of Illinois, Urbana-Champaign
2007年 1月 30日 Apache Derby データベースに対して単純なデータベース・クエリーを実行する方法、そして結果を選んで処理する方法を完全に把握してもらうには、Statement、ResultSet、そして ResultSetMetaData という 3 つの新しい JDBC クラスについて紹介しなければなりません。この記事では、この 3 つのクラスを JDBC データベース接続で使用して、Apache Derby データベースから独自の Java™ アプリケーションに短時間で簡単にデータを抽出する方法を説明します。
JDBC クエリーの実行: 概要
JDBC API は、あるタイプのオブジェクトに別のタイプのオブジェクトが含まれるという階層構造で設計されています。例えば、JDBC の Connection を使ってデータベースに接続し、この Connection オブジェクトから JDBC の Statement オブジェクトを作成して SQL クエリーをデータベースに送信します。これは当然のことのようですが、ある重要な効果があります。その効果とは、データベース接続が何らかの理由でクローズされると、そこに含まれるデータベース・オブジェクトも同じくクローズされるということです。クエリーの結果にアクセスするには該当する Statement オブジェクトに含まれる ResultSet オブジェクトを使用するため、この階層のレベルはもう 1 つ増えます。図 1 に、この階層を図示します。
図 1. JDBC オブジェクト間のコンテナー関係
この記事では実行しませんが、JDBC クエリーを再実行すると、内在する ResultSet オブジェクトが再使用されます。つまり、(例えばデータベース・クエリーを再実行して) JDBC の Statement を再使用する前にクエリー結果を完全に処理しておかないと、前回のクエリーの結果がすべて失われてしまいます。
Apache Derby データベースに対してクエリーを実行するには、データベースが正しく初期化されていなければなりません。シリーズのこれまでの記事の手順に従っていれば、データベースはこの記事のこれからの説明に使用できる状態になっているはずです。そうでない場合、あるいは白紙の状態から始めたい場合は、リスト 1 に示す derby.build.sql スクリプト・ファイルを使用してください。
リスト 1. Apache Derby ワークスペースの初期化
rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby10.zip Archive: ../derby10.zip
inflating: derby.build.sql
inflating: FirstQuery.java
inflating: SecondQuery.java
inflating: ThirdQuery.java
rb$ java org.apache.derby.tools.ij < derby.build.sql > derby.build.out 2> derby.build.err
rb$ javac *.java
rb$ ls
FirstQuery.class ThirdQuery.class derby.build.sql
FirstQuery.java ThirdQuery.java derby.log
SecondQuery.class derby.build.err test
SecondQuery.java derby.build.out
|
リスト 1 に示されているように、まず新しい作業ディレクトリーを作成してから、この記事付属のソース・コード・ファイル (記事の終わりに記載した「ダウンロード」セクションを参照) を解凍します。次のステップで、正しく初期化された Apache Derby データベースを作成します。Apache Derby の ij ツールから SQL スクリプトを実行すれば、Apache Derby データベースを簡単に初期化できます。最後に、付属の 3 つの Java ソース・コード・ファイルをコンパイルします。コンパイル結果は明示していませんが (一部のファイルは非常に長い出力を生成するため)、例えばコマンド・プロンプトで java FirstQuery と入力すると、これらの Java アプリケーションをそれぞれ実行できます。記事ではこの後、3 つの例それぞれのソース・コードを記載します。
クエリーを実行する
前のセクションで説明したように、データベース・クエリーの実行には以下の 3 つの主要コンセプトが関係します。
上記のオブジェクトを使用するには、まずはリスト 2 に示すようにオブジェクトをアプリケーションにインポートします。
リスト 2. JDBC クエリー・アプリケーションの開始
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.ResultSet;
public class FirstQuery {
private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
private static final String url = "jdbc:derby:test" ;
private static final String qry =
"SELECT itemNumber, price, stockDate, description FROM bigdog.products" ;
|
 |
明示的クエリー: ベスト・プラクティス
一般に、データベース・クエリーは常に明示的に作成しなければなりません。SELECT * 節を使用し、列名を指定しないクエリーは、アプリケーションと基礎データベースの間に疎結合を作り出してしまうので、このようなクエリーは作成しないようにしてください。データベースに変更が加えられると (列の名前変更、列のデータ型の変更、列の追加や削除など)、アプリケーションは引き続き機能しますが誤った結果を出す恐れがあります。列を明示的にリストすれば、要求した列が変更されない限り、コードは期待したとおりに機能するので、このようなバグが発生する可能性を最小限に抑えることができます。要求した列が変更されている場合にはコードがエラー・メッセージを生成し、問題を明示的に通知してくれます。 |
|
リスト 2 のコード・スニペットでは、まずすべての必要な Java クラスを明示的にインポートし、それからいくつかの重要な定数を定義しています。これらの定数は、最初のデータベース・クエリー・プログラムの残りの部分で使用することになります。このコードの大部分は前回の記事でも使用しているので、お馴染みのはずです。今回新しく目にするのは、Statement および ResultSet JDBC インターフェースの import 文が明示的に組み込まれている部分、そしてプログラムの後のほうで実行するクエリー・ストリングです。このクエリーは bigdog.products テーブルに含まれる 4 つの列を明示的にリストしていますが、これがベスト・プラクティスです (囲み記事の 「明示的クエリー」を参照)。こうすることによって、基礎データベースが予想外に変更されていた場合に発生する恐れのある多数のバグを排除することができます。
リスト 3 を見るとわかるように、Java プログラム内からデータベース・クエリーを実行するのに必要な操作は比較的単純です。このリストは、FirstQuery.java プログラムの場合の doQuery メソッドを示しています。メソッドのシグニチャーに、throws SQLException 節が含まれていることに注目してください。これは、このメソッド内で生成された SQL 例外は、呼び出したコードの側で処理しなければならないことを示しています (この例外を適切に処理する方法は、リスト 4 に示されています)。
リスト 3. データベース・クエリーの実行
static void doQuery(Connection con) throws SQLException {
SQLWarning swarn = null ;
Statement stmt = con.createStatement() ;
ResultSet rs = stmt.executeQuery(qry) ;
while (rs.next()) {
System.out.println("Item Number: " + rs.getString("itemNumber")) ;
System.out.println("Item Price: " + rs.getString("price")) ;
System.out.println("Stock Date: " + rs.getString("stockDate")) ;
System.out.println("Description: " + rs.getString("description") + '\n') ;
swarn = rs.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}
}
rs.close() ;
stmt.close() ;
}
|
doQuery メソッドでは、最初に Connection オブジェクトの createStatement メソッドを使って JDBC の新しい Statement オブジェクトを作成しています。Statement オブジェクトの executeQuery メソッドは、クエリー・ストリングを Apache Derby データベースに送信して実行するために使われています。Java プログラム内のクエリー結果には、Apache Derby に組み込まれた JDBC ドライバー・パッケージによって提供される ResultSet 実装を使ってアクセスしています。
ResultSet インターフェースは、クエリーから返されるデータにアクセスするための手法を複数提供します。この doQuery メソッドでは、繰り返し手法を使っています。これにより、next メソッドを使って実行中のクエリーの結果生じたデータの各行をループしています。はじめは、このイテレーターは先頭行の前に配置されているため、最初の呼び出しの後にイテレーターが指定するのは先頭行となります。最終行に到達するまで、while ループごとに結果データ・セット内の次の行にアクセスします。最終行が処理されると移動先は最終行の先となるため、イテレーターは null になり、これによってループが終了します。
ループ内では、ResultSet オブジェクトの getString メソッドを使って各行につき 4 つの列にアクセスします。このメソッドは列の値を Java の String オブジェクトとして取得するので、行の値を直接出力することが可能です。getString メソッドでは、クエリーに含まれる列の序数、または列の名前のどちらかを使用して列にアクセスできます。前回のクエリーを例にとると、getString(1) は getString("itemNumber") に相当します。ここでも同じく、明示的にすることがベスト・プラクティスです。明示的にすることによって、ささいなバグがアプリケーションに発生する可能性を抑えられるので、通常は列名を使用する方法が推奨されます。その上、0 からでははなく 1 の列からアクセスするということも問題の種になる可能性があるので、明示的にする理由が一層裏付けられます。
警告が生成される可能性はほとんどありませんが、新しい行がアクセスされるたびに、doQuery メソッドは明示的に ResultSet オブジェクトで新しい警告が生成されているかどうかをチェックします。このチェックが必要なのは、ResultSet の前の警告は新しい行がアクセスされるたびにクリアされるからです。このメソッドでは行っていませんが、Statement オブジェクトでの SQL 警告を同じようにチェックすることも可能です。
doQuery メソッドを使用するには、リスト 4 に示すようにデータベース接続を確立し、try ... catch ブロックからこのメソッドを呼び出す必要があります。
リスト 4. doQuery メソッドの呼び出し
public static void main(String[] args) {
Connection con = null ;
try {
Class.forName(driver) ;
con = DriverManager.getConnection(url);
SQLWarning swarn = con.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}
doQuery(con) ;
} catch (SQLException se) {
printSQLException(se) ;
}
...
|
上記を見るとわかるように、クエリー実行コードは別個のメソッドにカプセル化されているため、前回の記事に記載したデータベース接続コードの main メソッドに必要な変更は、たった 1 つだけとなっています。データベース接続が確立されて SQL 警告のチェックが行われた後、doQuery メソッドを呼び出すと、このメソッドによってすべてのクエリー処理の詳細が処理されます。それでは、ここからは doQuery メソッドのロジックを変更して Java プログラム内から Derby データベースに対して選択クエリーを実行する方法を詳しく説明していきます。
型付きデータを抽出する
前のセクションでは、クエリーを実行し、データを文字列として抽出する方法を説明しました。これはただ単にデータを出力する場合には申し分ありませんが、大抵の場合はビジネス・ロジックをデータに適用することになり、それにはデータを適切なデータ型として抽出する必要があります。例えば、price を数値による通貨データ型として抽出し、stockDate を日付データ型として抽出するという場合を考えてみましょう。
幸いなことに、JDBC API にはデータを適切なデータ型として抽出するためのメソッドがあります。形式上、型マッチングのルールは非常に長いものになりますが、Apache Derby データベースは Java 言語で作成されているため、型マッチングは極めて単純化されます。完全な構文のガイドラインは、「参考文献」セクションにリストした Apache Derby リファレンス・マニュアルに記載されていますが、一般的には型マッチングはあなたの予想通り、リスト 5 のようになります。このリストは、SecondQuery.java プログラムの場合の doQuery メソッドを示しています。
リスト 5. 型マッチング
static void doQuery(Connection con) throws SQLException {
SQLWarning swarn = null ;
Statement stmt = con.createStatement() ;
ResultSet rs = stmt.executeQuery(qry) ;
while (rs.next()) {
System.out.println("Item Number: " + rs.getString("itemNumber")) ;
System.out.println("Item Price: " + rs.getString("price")) ;
System.out.println("Stock Date: " + rs.getString("stockDate")) ;
System.out.println("Description: " + rs.getString("description") + '\n') ;
swarn = rs.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}
}
rs.close() ;
stmt.close() ;
}
|
上記のサンプル・コードに示した doQuery メソッドは、クエリー結果を該当するデータ型として抽出するように変更されています。つまり、price 列は java.math.BigDecimal データ型として、stockDate 列は java.sql.Date データ型として抽出されます。このコード・リストには示されていませんが (ただし、これは SecondQuery.java ファイルの完全なソース・コード・リストの一部です)、プログラムでこの 2 つのクラスを使用するには、適切な import 文を組み込む必要があります。
この doQuery メソッドの新しい実装では、クエリー結果セットに含まれる列を繰り返し処理しますが、それぞれの列は関連するデータ型として抽出されます。結果は以前と同じように表示されますが、ただし表示されるのは itemNumber 列の値が 6 より小さく、price が特定のしきい値 (この例では US$40.00) 未満の行だけです。ResultSet は、複数の重要なメソッドを使用した複合オブジェクトにできます。次のセクションで説明するように、ResultSet が持つ固有のメタデータには、ResultSetMetaData オブジェクトを使用してアクセスできます。
クエリー・メタデータを操作する
これまでに記載した 2 つの Java アプリケーションでは、少数の行しか選択されていないにもかかわらず、生成されるクエリー結果のリストは非常に長いものになりました。このシリーズでは以前、データベースの操作を実行するのに Apache Derby の ij ツールを使用しました (この記事の最初でも、付属 SQL スクリプトを実行する際に使用しています)。ij ツールは整然とフォーマット設定されたクエリー出力を作成しますが、これと同じことを、ResultSetMetaData オブジェクトとフォーマット設定 print 文を使って行うことができます。例えば、リスト 6 は ThirdQuery.java プログラムの doQuery メソッドの場合を表していです。
リスト 6. フォーマット設定クエリーの実行
static void doQuery(Connection con) throws SQLException {
int itemNumber = -1 ;
BigDecimal price = null ;
Date stockDate = null ;
String description = null ;
int numRows = 0 ;
String line = "------------------------------------" ;
BigDecimal threshold = new BigDecimal(40.00) ;
SQLWarning swarn = null ;
Statement stmt = con.createStatement() ;
ResultSet rs = stmt.executeQuery(qry) ;
ResultSetMetaData rsmd = rs.getMetaData() ;
System.out.printf("%-11s|", rsmd.getColumnName(1)) ;
System.out.printf("%-8s|", rsmd.getColumnName(2)) ;
System.out.printf("%-10s|", rsmd.getColumnName(3)) ;
System.out.printf("%-40s\n", rsmd.getColumnName(4)) ;
System.out.println(line + line);
while (rs.next()) {
itemNumber = rs.getInt("itemNumber") ;
price = rs.getBigDecimal("price") ;
stockDate = rs.getDate("stockDate") ;
description = rs.getString("description") ;
swarn = rs.getWarnings() ;
if(swarn != null){
printSQLWarning(swarn) ;
}else{
numRows ++ ;
System.out.printf("%-11s|", itemNumber) ;
System.out.printf("%-8s|", price) ;
System.out.printf("%-10s|", stockDate) ;
System.out.printf("%-40s\n", description) ;
}
}
System.out.println("\n" + numRows + " rows selected") ;
rs.close() ;
stmt.close() ;
}
|
上記の例では ResultSetMetaData オブジェクトを使用して、特定 ResultSet オブジェクトのメタデータを抽出および操作しています。このコード・リストには記載されていませんが、ResultSetMetaData インターフェースをインポートする必要があります (付属の ThirdQuery.java ソース・コード・ファイルには記載されています)。このメタデータに含まれる豊富な情報により、特定のデータベースに依存しない方法で Java アプリケーションを作成できるようになります。通常、このような普遍性は Apache Derby データベースを利用する Java アプリケーションには必要ありません。その理由は、Java アプリケーションと、それ自体が Java アプリケーションである組み込み Apache Derby データベースの間には密結合が存在することが多いためです。その一方、Derby の ij ツールでのように任意の SQL コマンドを処理しなければならない場合は、メタデータへのアクセスが重要になります。
この例で取り入られているもう 1 つの工夫は、フォーマット設定された出力を使用していることです。これをまだ目にしたことがない方のために説明すると、フォーマット設定 print 文はフォーマット・ストリングを後続のデータに適用して、特別にフォーマット設定された出力を作成します。上記の例では、ij ツールの出力を模倣するため、表示される各列の長さを制限し、縦線 (|) を挿入して列を区分しています。例えば「%-11s|」は、文字列を 11 文字に制限し、その後に縦線を表示します。マイナス記号は、文字列を左揃えするという意味です。
まとめ
この記事では、データベース・クエリーを実行し、その結果のデータ・セットを処理する方法を説明しました。説明のために使用したのは Java ソース・コードの 3 つの例です。最初の例では、データベースに接続し、クエリーを実行して結果を画面に出力しました。2 番目の例では、結果を適切な Java データ型として抽出してから、結果のデータをフィルタリングして画面に出力しました。そして最後に、クエリー結果に関するメタデータを使って、Apache Derby の ij ツールと同じような方法で整然とフォーマット設定されたクエリー結果を出力しました。このシリーズでは次回、この記事で紹介したデータ抽出の手法に基づいて Apache Derby データベースのデータを変更する方法を説明する予定です。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Derby SQL script for this article | derby10.zip | 4KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Robert J. Brunnerは、米国立スーパー・コンピューター応用研究所に科学者として勤務するかたわら、イリノイ大学アーバナ・シャンペーン校で天文学の助教授を務めています。何冊かの著作と、さまざまな分野にわたる数多くの記事や解説書を発表しています。連絡先はrb@ncsa.uiuc.eduです。 |
記事の評価
|