サーブレット&JSPの基本|JDBCでデータを取得する

以下が、「JDBCでデータを取得する」として整えた記事本文です。EMPLOYEESテーブルのレコード例は、山田 太郎 30歳、佐藤 花子 27歳に差し替えています。

JavaからSQLを送り、データベースの結果を受け取る。JDBCでEMPLOYEESテーブルの従業員情報を取得しよう

前回までに、H2 Databaseを導入し、studyデータベースを作成しました。

さらに、studyデータベースの中にEMPLOYEESテーブルを作成し、従業員データを追加しました。

ここまでの作業で、H2コンソールからSQLを実行すれば、EMPLOYEESテーブルの中身を確認できる状態になっています。

次に行うのは、Javaプログラムからデータベースに接続し、EMPLOYEESテーブルのデータを取得することです。

Javaプログラムからデータベースを利用するには、JDBCを使います。

JDBCでは、JavaプログラムがDBMSへSQL文を送り、その結果をJava側で受け取ります。

たとえば、EMPLOYEESテーブルからすべての従業員情報を取得したい場合、JavaプログラムはSELECT文を用意し、データベースへ送信します。

DBMSはEMPLOYEESテーブルを検索し、検索結果をJavaプログラムへ返します。

Javaプログラムは、その結果をResultSetとして受け取り、1行ずつデータを取り出して利用します。

この記事では、JDBCプログラムの全体像を確認しながら、H2 DatabaseのEMPLOYEESテーブルから従業員情報を取得して、Eclipseのコンソールビューに表示する方法を学習します。

JDBCプログラムで行うこと

JDBCプログラムでは、Javaプログラムからデータベースへ接続し、SQLを実行します。

今回の目的は、EMPLOYEESテーブルに保存されている全従業員情報を取得して表示することです。

今回取得するEMPLOYEESテーブルのデータ

IDNAMEAGE
EMP001山田 太郎30
EMP002佐藤 花子27

このデータを、JavaプログラムからSELECT文で取得します。

実行するSQL文は、次の内容です。

SELECT ID, NAME, AGE FROM EMPLOYEES

このSELECT文を実行すると、EMPLOYEESテーブルのID、NAME、AGE列を取得できます。

JDBCプログラムの基本的な流れ

JDBCプログラムは、いくつかの決まった手順でデータベースを利用します。

最初は少し長く見えますが、流れを押さえると理解しやすくなります。

JDBCでデータを取得する流れ

順番処理使う主なクラスやインタフェース
1JDBCドライバを読み込むClass.forName
2データベースに接続するDriverManager、Connection
3SELECT文を準備するPreparedStatement
4SELECT文を実行するPreparedStatement
5検索結果を受け取るResultSet
6ResultSetから値を取り出すResultSet
7取得した値を利用するSystem.out.printlnなど
8接続を閉じるtry-with-resources

Javaプログラムからデータベースを使うには、まずH2 Database用のJDBCドライバを読み込みます。

次に、DriverManagerを使ってデータベースへ接続し、Connectionを取得します。

そのConnectionからPreparedStatementを作成し、SQL文を実行します。

SELECT文の結果はResultSetとして返ってくるため、ResultSetから1行ずつ値を取り出します。

図1:JDBCでEMPLOYEESテーブルのデータを取得する流れ

この図から分かること

JDBCでは、Javaプログラムがデータベースへ直接触るのではなく、DBMSにSQL文を送信して処理を依頼します。

JavaプログラムはConnectionでデータベースと接続し、PreparedStatementでSELECT文を送ります。

DBMSはEMPLOYEESテーブルを検索し、その結果をResultSetとしてJavaプログラムへ返します。

JavaプログラムはResultSetから1行ずつ値を取り出し、必要な形で利用します。

JDBCドライバを読み込む

JDBCプログラムでは、最初にJDBCドライバを読み込みます。

H2 Databaseを利用する場合、ドライバクラス名はorg.h2.Driverです。

プログラムでは、次のようにClass.forNameを使って読み込みます。

Class.forName("org.h2.Driver");

この処理により、H2 Databaseへ接続するためのJDBCドライバを利用できる状態にします。

ただし、H2のJDBCドライバがWEB-INF/libやクラスパスに正しく配置されていない場合、ClassNotFoundExceptionが発生します。

そのため、JDBCドライバの読み込みはtry文で囲み、失敗した場合に分かりやすく例外を発生させます。

JDBCドライバ読み込みで確認すること

確認項目内容
ドライバクラス名org.h2.Driver
必要なJARファイルh2-x.x.x.jar
配置場所動的Webプロジェクトではsrc/main/webapp/WEB-INF/lib
失敗時の例外ClassNotFoundException

JDBCドライバを読み込めない状態では、後続のデータベース接続処理を行えません。

そのため、ドライバが見つからない場合は、プログラムを続行せずに停止するようにします。

データベースに接続する

JDBCドライバを読み込んだら、次にデータベースへ接続します。

データベースへ接続するには、DriverManager.getConnectionを使います。

H2 Databaseのstudyデータベースへサーバーモードで接続する場合、JDBC URLは次のようになります。

jdbc:h2:tcp://localhost/~/study

ユーザー名はsa、パスワードは空文字として接続します。

Connection conn = DriverManager.getConnection(
        "jdbc:h2:tcp://localhost/~/study", "sa", "");

この処理が成功すると、Javaプログラムとデータベースの接続を表すConnectionインスタンスを取得できます。

接続情報

項目
JDBC URLjdbc:h2:tcp://localhost/~/study
ユーザー名sa
パスワード空文字
接続先H2 Databaseのstudyデータベース

Connectionは、Javaプログラムとデータベースをつなぐ接続です。

SQLを送るには、このConnectionが必要になります。

try-with-resourcesで接続を自動的に閉じる

データベース接続は、使い終わったら閉じる必要があります。

接続を閉じ忘れると、不要な接続が残り、データベースの利用に影響することがあります。

Javaでは、try-with-resourcesを使うと、tryブロックの処理が終わったときに自動的にリソースを閉じられます。

try (Connection conn = DriverManager.getConnection(
        "jdbc:h2:tcp://localhost/~/study", "sa", "")) {

    // データベース処理を書く

} catch (SQLException e) {
    e.printStackTrace();
}

このように書くと、tryブロックを抜けるときにConnectionが自動的に閉じられます。

データベース接続の閉じ忘れを防げるため、JDBCプログラムではよく使う書き方です。

SELECT文を準備する

データベースへ接続できたら、次にSQL文を準備します。

今回は、EMPLOYEESテーブルからID、NAME、AGEを取得します。

String sql = "SELECT ID, NAME, AGE FROM EMPLOYEES";
PreparedStatement pStmt = conn.prepareStatement(sql);

SQL文はString型の変数sqlに代入します。

その後、ConnectionのprepareStatementメソッドを使って、PreparedStatementを取得します。

PreparedStatementの役割

項目内容
役割SQL文をDBMSへ送信する準備をする
作成方法conn.prepareStatement(sql)
今回のSQLSELECT ID, NAME, AGE FROM EMPLOYEES
実行方法executeQueryを使う

PreparedStatementは、SQL文をデータベースに届けるための部品です。

SELECT文を実行するときは、このPreparedStatementからexecuteQueryを呼び出します。

図2:ConnectionとPreparedStatementでSELECT文を届ける

この図から分かること

DriverManager.getConnectionを実行すると、JavaプログラムとデータベースをつなぐConnectionを取得できます。

Connectionは、JavaプログラムとDBMSをつなぐ道路のような役割を持っています。

そのConnectionからPreparedStatementを作成し、SELECT文をDBMSへ送信します。

PreparedStatementは、SQL文を運ぶ車のような役割です。

JDBCプログラムでは、ConnectionとPreparedStatementを組み合わせて、SQLをデータベースへ届けます。

SELECT文を実行してResultSetを取得する

PreparedStatementを作成したら、SELECT文を実行します。

SELECT文を実行するには、executeQueryを使います。

ResultSet rs = pStmt.executeQuery();

executeQueryを実行すると、DBMSがEMPLOYEESテーブルを検索します。

検索結果はResultSetとして返されます。

executeQueryの役割

項目内容
実行するSQLSELECT文
戻り値ResultSet
役割検索結果を受け取る
今回の結果EMPLOYEESテーブルの全従業員情報

ResultSetには、SELECT文で取得した結果が格納されています。

今回であれば、EMP001の山田 太郎さん、EMP002の佐藤 花子さんの2件が検索結果として入ります。

ResultSetからレコードを取り出す

ResultSetには、検索結果のレコードが格納されています。

ResultSetからデータを取り出すには、nextメソッドを使います。

while (rs.next()) {
    // 1行分のデータを取り出す
}

rs.nextを実行すると、取り出し対象のレコードが次の行へ進みます。

レコードが存在すればtrueを返します。

存在しなければfalseを返します。

そのため、while文と組み合わせることで、検索結果を1行ずつ処理できます。

ResultSetのnextメソッド

戻り値内容
true次のレコードが存在する
false次のレコードが存在しない

検索結果が2件ある場合、rs.nextは2回trueを返し、3回目でfalseを返します。

そのため、while文の中の処理は2回実行されます。

各列の値を取得する

ResultSetで1行分のレコードを選択したら、そのレコードの各列の値を取り出します。

文字列の列はgetString、整数の列はgetIntで取得します。

String id = rs.getString("ID");
String name = rs.getString("NAME");
int age = rs.getInt("AGE");

ID列とNAME列は文字列として取得するため、getStringを使います。

AGE列は整数として取得するため、getIntを使います。

列の値を取得するメソッド

取得する列データ型使用するメソッド
ID文字列getString("ID")
NAME文字列getString("NAME")
AGE整数getInt("AGE")

列名は、SQLで取得した列名に合わせて指定します。

今回のSQLではID、NAME、AGEを取得しているため、ResultSetでも同じ列名で値を取り出します。

取得したデータを表示する

ResultSetから取り出した値は、Javaプログラムの中で利用できます。

今回は、Eclipseのコンソールビューに表示します。

System.out.println("ID:" + id);
System.out.println("名前:" + name);
System.out.println("年齢:" + age + "\n");

このようにして、取得したID、名前、年齢をコンソールに出力します。

JDBCプログラムの学習では、まずコンソールに出力して、データベースから正しく値を取得できているかを確認すると分かりやすいです。

全従業員情報を検索するJDBCプログラムの例

ここでは、EMPLOYEESテーブルから全従業員情報を取得し、コンソールに表示するJDBCプログラムを作成します。

ファイル名: SelectEmployees.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SelectEmployees {
    public static void main(String[] args) {
        // JDBCドライバを読み込む
        try {
            Class.forName("org.h2.Driver");
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(
                    "JDBCドライバを読み込めませんでした");
        }

        // データベースに接続
        try (Connection conn = DriverManager.getConnection(
                "jdbc:h2:tcp://localhost/~/study", "sa", "")) {

            // SELECT文を準備
            String sql = "SELECT ID, NAME, AGE FROM EMPLOYEES";
            PreparedStatement pStmt = conn.prepareStatement(sql);

            // SELECT文を実行し、結果表を取得
            ResultSet rs = pStmt.executeQuery();

            // 結果表に格納されたレコードの内容を表示
            while (rs.next()) {
                String id = rs.getString("ID");
                String name = rs.getString("NAME");
                int age = rs.getInt("AGE");

                // 取得したデータを出力
                System.out.println("ID:" + id);
                System.out.println("名前:" + name);
                System.out.println("年齢:" + age + "\n");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

このプログラムは、デフォルトパッケージに作成する想定です。

H2 Databaseのstudyデータベースへ接続し、EMPLOYEESテーブルからID、NAME、AGEを取得します。

取得した結果はResultSetに格納され、while文で1行ずつ取り出してコンソールに表示します。

SelectEmployees.javaの処理の流れ

SelectEmployees.javaでは、JDBCプログラムに必要な処理を順番に行っています。

SelectEmployees.javaで行っていること

順番処理内容
1importを書くJDBCで使うクラスやインタフェースを読み込む
2JDBCドライバを読み込むClass.forNameでorg.h2.Driverを読み込む
3データベースに接続するDriverManager.getConnectionでConnectionを取得する
4SELECT文を準備するSQL文字列を作り、PreparedStatementを取得する
5SELECT文を実行するexecuteQueryでResultSetを取得する
6検索結果を1行ずつ進めるwhile文とrs.nextを使う
7各列の値を取得するgetStringやgetIntを使う
8取得した値を表示するSystem.out.printlnでコンソールに出力する
9例外を処理するSQLExceptionをcatchする

それぞれの処理が、データベースを利用するために必要な手続きです。

最初は長く見えるかもしれませんが、JDBCプログラムではよく使う形です。

importしているクラスやインタフェース

SelectEmployees.javaでは、java.sqlパッケージから必要なクラスやインタフェースをimportしています。

importしているもの

import役割
java.sql.Connectionデータベースとの接続を表す
java.sql.DriverManagerデータベース接続の入口になる
java.sql.PreparedStatementSQL文を実行する
java.sql.ResultSetSELECT文の検索結果を受け取る
java.sql.SQLExceptionデータベース処理の例外を扱う

これらは、Javaプログラムからデータベースを利用するときに必要になる代表的な部品です。

java.sqlパッケージはJava SEの標準APIに含まれているため、追加でダウンロードする必要はありません。

H2 Databaseに接続するためには、これらに加えてH2のJDBCドライバが必要です。

実行方法

SelectEmployees.javaを作成したら、EclipseでJavaアプリケーションとして実行します。

Eclipseでの実行手順

手順内容
1SelectEmployees.javaを作成する
2H2 Databaseを起動しておく
3H2コンソールで組み込みモード接続を開いたままにしていないことを確認する
4SelectEmployees.javaを選択する
5右クリックする
6実行を選択する
7Javaアプリケーションを選択する
8Eclipseのコンソールビューを確認する

H2 Databaseが起動していない場合、接続できません。

また、組み込みモードでH2コンソールがstudyデータベースに接続したままだと、Javaプログラムから接続できない場合があります。

Javaプログラムから接続する前に、H2コンソール側の接続状態も確認しておきましょう。

実行手順の補足

「SelectEmployees.java」を右クリックして、実行を選択し、Javaアプリケーションを選択します。

実行結果

EMPLOYEESテーブルに山田 太郎さんと佐藤 花子さんのデータが入っている場合、実行するとEclipseのコンソールビューに次のように表示されます。

ID:EMP001
名前:山田 太郎
年齢:30

ID:EMP002
名前:佐藤 花子
年齢:27

コンソールの表示

この結果が表示されれば、JavaプログラムからH2 Databaseへ接続し、EMPLOYEESテーブルのデータを取得できています。

H2コンソールではなく、Javaプログラムからデータベースの中身を取り出せている点が重要です。

図3:ResultSetから1行ずつ値を取り出す

この図から分かること

ResultSetには、SELECT文で取得した検索結果が格納されています。

rs.nextを実行すると、取り出し対象のレコードが1行進みます。

while文と組み合わせることで、検索結果のレコードを先頭から順番に処理できます。

各行では、rs.getString("ID")、rs.getString("NAME")、rs.getInt("AGE")のように列名を指定して値を取得します。

取得した値は、コンソールに出力したり、JavaBeansに格納したり、JSPへ渡したりできます。

JDBCドライバ読み込み失敗時の対応

SelectEmployees.javaでは、最初にClass.forName("org.h2.Driver")を実行しています。

これは、H2 DatabaseのJDBCドライバを読み込むための処理です。

H2のJDBCドライバが適切に配置されていない場合、ClassNotFoundExceptionが発生します。

このプログラムでは、ClassNotFoundExceptionが発生した場合、IllegalStateExceptionを発生させて処理を終了しています。

try {
    Class.forName("org.h2.Driver");
} catch (ClassNotFoundException e) {
    throw new IllegalStateException(
            "JDBCドライバを読み込めませんでした");
}

JDBCドライバを読み込めない状態では、データベース接続もSQL実行もできません。

そのため、プログラムを続けず、設定に問題があることを分かりやすく示しています。

ClassNotFoundExceptionが出たときに確認すること

JDBCドライバの読み込みに失敗した場合は、H2のJDBCドライバが正しく配置されているか確認します。

確認ポイント

確認項目内容
JARファイルh2-x.x.x.jarがあるか
配置場所動的Webプロジェクトならsrc/main/webapp/WEB-INF/libにあるか
ファイル名ダウンロードしたH2のJARファイルを配置しているか
プロジェクト実行しているstudyプロジェクトに配置しているか
ビルドパスJavaアプリケーションとして実行する場合に参照できる状態か

動的WebプロジェクトのWEB-INF/libに配置している場合でも、通常のJavaアプリケーションとして実行するときに参照できない設定になっている場合があります。

Eclipseで実行する場合は、プロジェクトのビルドパスやライブラリの認識状態も確認しましょう。

SQLExceptionが発生したときに確認すること

データベース接続やSQL実行で問題があると、SQLExceptionが発生します。

SelectEmployees.javaでは、SQLExceptionが発生した場合にe.printStackTraceでエラー内容を表示しています。

} catch (SQLException e) {
    e.printStackTrace();
}

実際の業務システムでは、ログに記録したり、利用者向けのエラー画面を表示したりするなど、より適切な処理を行います。

学習段階では、まずエラー内容をコンソールで確認し、原因を探れるようにしておきます。

SQLExceptionで確認したいこと

状況確認すること
接続できないH2 Databaseが起動しているか
接続先が違うJDBC URLがjdbc:h2:tcp://localhost/~/studyになっているか
テーブルがないEMPLOYEESテーブルを作成済みか
SQLエラーSELECT文の列名やテーブル名に誤りがないか
認証エラーユーザー名sa、パスワード空文字で接続しているか
データが表示されないEMPLOYEESテーブルにレコードが追加されているか

SQLExceptionは、データベースに関するさまざまな問題を知らせてくれます。

エラーが出た場合は、接続情報、SQL文、テーブルの有無、データの有無を順番に確認しましょう。

JDBCでデータを取得するときに押さえたいこと

JDBCでデータを取得するときは、Javaプログラム、JDBCドライバ、DBMS、データベースの関係を意識することが大切です。

Javaプログラムは、DriverManagerを使ってConnectionを取得します。

ConnectionからPreparedStatementを作成し、SELECT文を実行します。

SELECT文の結果はResultSetとして返ってきます。

ResultSetからrs.nextで1行ずつ進めながら、getStringやgetIntで列の値を取得します。

今回のSelectEmployees.javaでは、EMPLOYEESテーブルから次のデータを取得しました。

取得したデータ

IDNAMEAGE
EMP001山田 太郎30
EMP002佐藤 花子27

このように、Javaプログラムからデータベースのレコードを取得できるようになると、ServletやJSPで作るWebアプリケーションでも、データベースの内容を画面に表示できるようになります。

まずはコンソールに表示するJDBCプログラムで流れを理解し、その後、Servlet、DAO、JSPへ役割を分けた構成に発展させていきましょう。