本記事は2001年に執筆されたものです。JDBC全般の最新情報は@IT Java Solutuionのカテゴリ「DB連携」をご参照ください。
この連載では、Javaのデータベース・アクセスAPIである「JDBC」の機能を、サンプルコードを交えて解説していきます。また、J2EEにおけるJDBCの位置付けや、JDBCを利用するさまざまなテクノロジについても解説していく予定です。前提知識としては、Javaとリレーショナル・データベースに関するベーシックな知識があれば十分です
前回は、JDBCの概要とそのアーキテクチャについて解説しました。今回は、さっそくJDBCを使った簡単なサンプルアプリケーションを紹介しながら、JDBCによるデータベース接続と、データ型のマッピング、検索処理について解説します。
簡単なJDBCアプリケーション
それでは、簡単なJDBCアプリケーションのコードを見てみましょう。これは、同じマシンで動作しているOracle8iデータベースにユーザー名“SCOTT”で接続し、EMP表に対して問い合わせを実行し、その結果を標準出力に出力します。
// Javaデータアクセスの基礎 サンプルコード(1) // EMP表への問合せを実行するJavaアプリケーション // JDBC APIをインポート import java.sql.*;class JavaDataAccess01 { public static void main (String args[]) throws SQLException, ClassNotFoundException { // Oracle JDBC Driverのロード Class.forName("oracle.jdbc.driver.OracleDriver"); // Oracle8iに接続 Connection conn = DriverManager.getConnection ("jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger"); // ステートメントを作成 Statement stmt = conn.createStatement(); // 問合せの実行 ResultSet rset = stmt.executeQuery("select EMPNO, ENAME from EMP"); // 問合せ結果の表示 while ( rset.next() ) { // 列番号による指定 System.out.println(rset.getInt(1) + "\t" + rset.getString(2)); } // 結果セットをクローズ rset.close(); // ステートメントをクローズ stmt.close(); // 接続をクローズ conn.close(); } }
そして、このコードの実行結果は、次のようになります。
C:\JDBC>java JavaDataAccess01 7369 SMITH 7499 ALLEN 7521 WARD 7566 JONES 7654 MARTIN 7698 BLAKE 7782 CLARK 7788 SCOTT 7839 KING 7844 TURNER 7876 ADAMS 7900 JAMES 7902 FORD 7934 MILLER
サンプルコード実行のための環境構築
ここで、本連載で紹介していくサンプルコードを実行するための環境を構築しておきましょう。サンプルコードの実行のためには、J2SEおよびデータベースを利用できる環境を準備する必要があります。本連載では、データベースにOracle8iを使用します。Oracle8iが用意できない方は、サイトからトライアル版をダウンロードして使用してください。
- J2SE 1.3(http://java.sun.com/j2se/1.3/ja/)
- Oracle8i Database
(http://otn.oracle.co.jp/software/iserver/oracle8i/oracle8i.html) - Oracle JDBC Driver
(http://otn.oracle.co.jp/software/db_connect/jdbc/jdbc.html)
※Oracle8iにも同梱されています
CLASSPATHに、Oracle JDBC Driverのファイルclasses12.zipを追加しておきましょう。このファイルは、次の位置にあります。
<ORACLE_HOME>\jdbc\lib\classes12.zip
サンプルコードでは、データベースのSID、リスナの構成などにデフォルトの値を使用しているので、環境に合わせて変更しましょう。また、データベースの動作するマシンに合わせて、ホスト名も変更しましょう。
また、SCOTTスキーマが作成されていない場合や再作成したい場合には、次のSQLスクリプトを実行しましょう。
<ORACLE_HOME>\RDBMS\ADMIN\utlsampl.sql
なお、本連載では、サンプルコードの実行結果やファイル・パス表記として、Windows NTのものを使いますが、UNIXプラットフォームでも、当然実行可能です(WORAですね!)。
Oracle8i以外でも、JDBC 2.0 API対応のJDBCドライバを持つデータベースであれば、サンプルコードを多少修正するだけで実行可能になるので、ぜひチャレンジしてみてください。
JDBCドライバのロードとデータベース接続の確立
では、サンプルコードをもう一度見てみましょう。JDBCドライバのロードとデータベースの接続をどう行っているかを見ていきます。
// Oracle JDBC Driverのロード Class.forName("oracle.jdbc.driver.OracleDriver");
まず、現行のクラス・ローダを使って、JDBCドライバのクラスをロードします。ここでは、Oracle JDBC Driverのクラスoracle.jdbc.driver.OracleDriverを指定しています。
// Oracle8iに接続 Connection conn = DriverManager.getConnection ("jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");
次に、java.sql.DriverManagerクラスのgetConnection()メソッドを用いて、データベースへ接続します。getConnection()メソッドの第1パラメータは、接続先のデータベース情報を表しており、「データベースURL」と呼ばれます。第2、第3パラメータには、それぞれデータベース・ユーザーとそのパスワードを指定します。
データベースURLは、次のフォーマットで指定します。
jdbc:<サブプロトコル>:<サブネーム>
Oracle JDBC Driverのサブプロトコルは、サンプルコードを見れば分かるようにoracleです。ところで、JDBC-ODBCブリッジ・ドライバのサブプロトコルは、odbcです。このように、それぞれのJDBCドライバは固有のサブプロトコルを持っています。
サブネーム部分の構文は、JDBCドライバが独自に定義できるためドライバによって異なります。
例えば、JDBC-ODBCブリッジ・ドライバの場合には、次のように、ODBCデータ・ソース名を指定します(“xyz”は、ODBCデータ・ソース名)。
jdbc:odbc:xyz
Oracle JDBC Thin Driverの場合には、次のようなフォーマットとなります。
jdbc:oracle:thin:@:<ホスト名>:<リスナのポート番号>:<Oracle SID>
詳細は、使用するJDBCドライバのドキュメントを参考にしてください。
データ型のマッピング
ここで、SQLのデータ型とJavaのデータ型の間のマッピングについて、簡単に見ておきましょう。
JDBCを利用して、データベースからデータを取得する場合、SQLデータ型で表現されているデータを、特定のJavaデータ型で表現する必要があります。逆に、データベース内のデータを更新する場合、Javaデータ型で表現されているデータを、特定のSQLデータ型のデータとして扱う必要があります。
JDBCでは、SQL-92で定義されている標準的なSQLデータ型を、JDBCデータ型として定義しています。そして、このJDBCデータ型とJavaデータ型の間のデフォルト・マッピングも定義しています。このマッピングを表1に示します。
これらの標準的なSQLデータ型に加えて、独自に定義したSQLデータ型を使用しているデータベースは、少なくありません。例えば、Oracle8iでは、254バイト以下の可変長文字列を表すデータ型 “VARCHAR”を拡張して、4000バイト以下の可変長文字列を表すデータ型 “VARCHAR2” を定義しています。
データベース独自のSQLデータ型とJDBCデータ型の間のマッピングは、JDBCドライバが暗黙的に行います。そのため、Java開発者は、データベース独自のSQLデータ型を利用するデータベースにアクセスする場合でも、Javaデータ型とJDBCデータ型だけを考慮するだけでよくなるわけです。
Oracle JDBC Driverで定義されている、Oracle8i SQLデータ型とJDBCデータ型の間のデフォルト・マッピングも、表1に合わせて示します。
利用可能なSQLデータ型と対応するJavaデータ型の詳細は、使用するデータベース、およびJDBCドライバのドキュメントを参考にしてください。
Javaデータ型 | JDBCデータ型 | Oracle8i SQLデータ型 |
---|---|---|
String | CHAR | CHAR |
String | VARCHAR | VARCHAR2 |
String | LONGVARCHAR | LONG |
java.math.BigDecimal | NUMERIC | NUMBER |
java.math.BigDecimal | DECIMAL | NUMBER |
boolean | BIT | NUMBER |
byte | TINYINT | NUMBER |
short | SMALLINT | NUMBER |
int | INTEGER | NUMBER |
long | BIGINT | NUMBER |
float | REAL | NUMBER |
double | FLOAT | NUMBER |
double | DOUBLE | NUMBER |
byte[] | BINARY | RAW |
byte[] | VARBINARY | RAW |
byte[] | LONGVARBINARY | LONGRAW |
java.sql.Date | DATE | DATE |
java.sql.Time | TIME | DATE |
javal.sql.Timestamp | TIMESTAMP | DATE |
表1 |
JDBCでの検索処理
ここで再び、サンプルコードに戻りましょう。JDBCでの検索処理の部分を見ていきます。
// ステートメントを作成 Statement stmt = conn.createStatement();
データベース接続を表すjava.sql.ConnectionオブジェクトのcreateStatement()メソッドを用いて、SQL文を表すjava.sql.Statementオブジェクトを作成します。
// 問合せの実行 ResultSet rset = stmt.executeQuery("select EMPNO, ENAME from EMP");
問い合わせ(SELECT文)を実行する場合には、java.sql.StatementオブジェクトのexecuteQuery()メソッドを使用します。パラメータとして、SELECT文を指定します。このメソッドの戻り値は、問い合わせの結果セットを表すjava.sql.ResultSetオブジェクトになります。
// 問合せ結果の表示 while ( rset.next() ) { // 列番号による指定 System.out.println(rset.getInt(1) + "\t" + rset.getString(2)); }
結果セットは、問い合わせ結果の複数行を表しています。結果セットでは、処理対象の行を表すためにカーソルを用います。カーソルは、結果セット作成直後は1行目の前に位置しています。next()メソッドにより、カーソルを1行ずつ下に移動して、問い合わせ結果を1行ずつ処理していきます。カーソルが最終行を超えると、next()メソッドがfalseを返すため、サンプルコードのように、whileループを利用すれば、結果セットのすべての行を処理できます。
カーソルのある処理対象行のデータを取り出すためには、getXXX()メソッドを用います。SQLデータ型がNUMBER(4)、つまり4けたの整数であるEMP表のEMPNO列に対して、getInt()メソッドを用いています。また、SQLデータ型がVARCHAR2(10)であるEMP表のENAME列に対しては、getString()メソッドを用いています。マッピング・テーブルを見直してみれば、これらが、SQLデータ型に対応するデフォルトのJavaデータ型のgetXXX()メソッドになっていることが分かるでしょう。マッピング・テーブルは、サンのサイト(http://java.sun.com/docs/books/tutorial/jdbc/basics/retrieving.html)の表を参考にしてください。
デフォルト・マッピングのJavaデータ型以外のデータ型でデータを取り出すことも可能ですが、特に理由がなければ、デフォルトのデータ型で取り出しましょう。
getXXX()メソッドで列を指定するには、2つの方法があります。(1から始まる)列番号を用いて指定することもできるし、あるいは、列名を用いて以下のように指定することも可能です。
System.out.println(rset.getInt("EMPNO") + "\t" + rset.getString("ENAME"));
“select * from EMP” のように、列名を指定しないSELECT文の場合には、列番号を用いる方がコーディングが簡単になります。
これらのオブジェクトは、ガベージ・コレクションされる際に、自動的にクローズされ、データベースやJDBCのリソースを解放します。ですが、以下のリストのように、オブジェクトが不要になった時点で、明示的にクローズしリソースを直ちに解放する、という習慣を身に付けておく方がいいでしょう。
// 結果セットをクローズ rset.close(); // ステートメントをクローズ stmt.close(); // 接続をクローズ conn.close();
第3回は、JDBCによる更新処理を取り上げる予定です。
参考リファレンス
▼Java Solution FAQ(@IT)
▼J2EEの基礎(@IT)
▼JDBC APIドキュメント(Sun)
▼JDBC Data Access API(Sun、英語)
▼OTN Japan - Java(日本オラクル)
▼OTN - Java (米オラクル、英語)
Copyright © ITmedia, Inc. All Rights Reserved.