JDBCによるDBへの接続と検索の実行Javaデータアクセスの基礎(2)

» 2001年07月11日 00時00分 公開
[佐藤直生日本オラクル]

本記事は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();
  }
} 
リスト JavaDataAccess01.java

 そして、このコードの実行結果は、次のようになります。

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が用意できない方は、サイトからトライアル版をダウンロードして使用してください。

 CLASSPATHに、Oracle JDBC Driverのファイルclasses12.zipを追加しておきましょう。このファイルは、次の位置にあります。

<ORACLE_HOME>\jdbc\lib\classes12.zip

<ORACLE_HOME>は、Oracle8iをインストールしたホーム・ディレクトリ

 サンプルコードでは、データベースの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ループを利用すれば、結果セットのすべての行を処理できます。

図1 結果セットとカーソルのイメージ 図1 結果セットとカーソルのイメージ

 カーソルのある処理対象行のデータを取り出すためには、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.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。