カテゴリ: Servlet 更新日: 2026/03/15

JavaサーブレットとJDBCを連携!DB接続とSELECT文実行の完全ガイド

JDBCを使ってサーブレットでデータを取得しよう(SELECT)
JDBCを使ってサーブレットでデータを取得しよう(SELECT)

新人と先輩の会話形式で理解しよう

新人

「先輩、Javaのサーブレットを使ってデータベースからデータを取ってきたいんですけど、どうすればいいんでしょうか?」

先輩

「Webアプリでデータを扱うには、サーブレットとJDBC(Java Database Connectivity)を連携させるのが基本だよ。まずは全体像を掴んで、必要な準備から始めてみようか。」

新人

「JDBC…?何だか難しそうですね。初心者でも設定できるでしょうか?」

先輩

「大丈夫、手順を一つずつ踏めば簡単だよ。まずはDB接続に必要な『JDBCドライバ』の準備から、実際にSELECT文を実行するステップまで詳しく解説するね。」

1. サーブレットとJDBCを連携させるメリットと全体像

1. サーブレットとJDBCを連携させるメリットと全体像
1. サーブレットとJDBCを連携させるメリットと全体像

Javaで開発するWebアプリケーションにおいて、サーブレット(Servlet)JDBC(Java Database Connectivity)を連携させることは、動的なサイトを作るための必須スキルです。なぜこの二つを組み合わせるのか、その理由と仕組みを紐解いていきましょう。

なぜサーブレットとDBを連携させるのか?

サーブレットは、ブラウザからのリクエスト(「このページを見せて」というお願い)を受け取り、サーバー側で処理を行うプログラムです。しかし、サーブレット単体では「データの保存」が得意ではありません。そこで、データの管理に特化したデータベース(DB)の出番です。

例えば、ショッピングサイトで商品一覧を表示する場合、サーブレットがデータベースに「今ある商品のリストをちょうだい」と頼み、受け取ったデータを画面に表示します。このように、「計算や判断をするサーブレット」「データを保管するデータベース」を繋ぐ橋渡し役がJDBCなのです。

連携の全体イメージ:3層構造の理解

Webアプリケーションの構造は、よく「レストラン」に例えられます。この例えを使うと、連携の仕組みが非常に分かりやすくなります。

  • クライアント(お客さま): ブラウザを使って注文を出します。
  • サーブレット(店員さん): 注文を受けて、厨房(DB)に食材を取りに行きます。
  • JDBC(注文伝票と運搬ワゴン): 店員さんがDBとやり取りするための共通のルールと道具です。
  • データベース(冷蔵庫・倉庫): 全ての食材(データ)が保管されています。

この連携によって、ユーザーごとに異なる情報を表示したり、大量のデータを高速に検索したりすることが可能になります。プログラミング未経験の方でも、「画面の裏側では、Javaのプログラムがデータベースに問い合わせをしているんだな」というイメージを持っておけば十分です。

JDBCとは何か?(用語解説)

JDBCとは、Javaプログラムからリレーショナルデータベースに接続するための標準的なAPI(機能セット)です。Oracle、MySQL、PostgreSQLなど、世の中には多くのデータベース製品がありますが、JDBCという共通のルールがあるおかげで、Javaエンジニアはどのデータベースを使う場合でも、ほぼ同じ書き方でプログラムを作ることができます。

2. DB接続に必要なJDBCドライバの準備とビルドパスの設定

2. DB接続に必要なJDBCドライバの準備とビルドパスの設定
2. DB接続に必要なJDBCドライバの準備とビルドパスの設定

サーブレットからデータベースに接続するためには、まず「道具箱」を準備する必要があります。それがJDBCドライバです。

JDBCドライバとは「通訳」のこと

Javaの言葉と、データベース(例えばMySQLやPostgreSQL)の言葉は、実は少しだけ違います。JDBCドライバは、Javaが発した命令をデータベースが理解できる形式に変換してくれる「通訳さん」のような役割を果たします。自分が使いたいデータベース製品の種類に合わせたドライバをダウンロードしてくる必要があります。

JDBCドライバの入手方法

一般的には、各データベースの公式サイトから「Connector/J」(MySQLの場合)などの名称で配布されている「.jar」という拡張子のファイルを入手します。このファイルをプロジェクトに組み込むことで、Javaは初めてデータベースとの会話ができるようになります。

Eclipseでのビルドパス(Build Path)の設定手順

Javaの開発ツールであるEclipse(エクリプス)を使っている場合、ダウンロードしたJARファイルをプロジェクトに認識させる「ビルドパスの設定」が必要です。パソコンをあまり触ったことがない方でも、以下の手順で進めれば大丈夫です。

  1. プロジェクトの「WebContent/WEB-INF/lib」フォルダに、ダウンロードしたJARファイルをコピー&ペーストします。
  2. Eclipse上でプロジェクトを右クリックし、「リフレッシュ(更新)」を押してファイルを反映させます。
  3. (必要に応じて)プロジェクトを右クリック > 「ビルド・パス」 > 「ビルド・パスの構成」を選択。
  4. 「ライブラリー」タブで「JARの追加」を押し、先ほどコピーしたファイルを選択して「適用して閉じる」をクリックします。

この設定を行うことで、Javaのコード内でデータベース接続用のクラスがエラーなく読み込めるようになります。この「ビルドパスを通す」という作業は、Java開発において「ライブラリ(便利な外部機能)を使うための基本の儀式」のようなものです。

Webアプリケーションにおける注意点

通常のJavaプログラムと異なり、サーブレット(Webアプリ)の場合は、JARファイルを「WEB-INF/lib」フォルダに入れることが非常に重要です。ここに配置することで、Tomcat(トムキャット)などのWebサーバーが自動的にドライバを認識してくれるようになります。初心者が一番最初につまずきやすいポイントなので、場所を間違えないようにしましょう。

3. SELECT文を実行するための基本的な5つのステップ

3. SELECT文を実行するための基本的な5つのステップ
3. SELECT文を実行するための基本的な5つのステップ

準備が整ったら、いよいよJavaからデータベースを操作しましょう。最もよく使われる「データの検索(SELECT文)」を実行する手順は、決まった5つのステップで構成されます。この流れは、どんなWebアプリケーションでも共通の黄金パターンです。

ステップ1:JDBCドライバをロードする

まずは「今からこの通訳さんを使いますよ」とJavaに教えます。Class.forName()という命令を使いますが、最近のバージョンでは省略できることも多いです。しかし、基本を学ぶ上では明示的に書く習慣をつけておくと、古いシステムを触る時にも困りません。

ステップ2:データベースに接続する(Connectionの確立)

データベースという「家」に鍵を開けて入る作業です。URL、ユーザー名、パスワードという3つの情報を使って、接続オブジェクト(Connection)を作成します。

ステップ3:SQL文を発行する準備をする(Statementの作成)

「どんな命令を送るか」を準備します。ここでは、データベースへの問い合わせ文であるSQL(エスキューエル)を作成します。例えば、「SELECT * FROM users(ユーザー一覧を全員分ちょうだい)」といった命令です。

ステップ4:SQL文を実行して結果を受け取る(ResultSetの取得)

実際に命令を送り、返ってきたデータを受け取ります。この返ってきたデータの塊をResultSet(リザルトセット)と呼びます。表形式でデータが入っており、1行ずつ取り出して処理を行います。

ステップ5:接続を閉じる(クローズ処理)

使い終わったら、必ずデータベースとの接続を閉じます。水道の蛇口を閉め忘れると水が溢れるように、接続を繋ぎっぱなしにするとサーバーのメモリが足りなくなり、故障の原因になります。

実際のJavaコード例(基本的なSELECT処理)

それでは、これらのステップをコードに落とし込んでみましょう。サーブレットの中でDB接続を行う例です。


import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/UserListServlet")
public class UserListServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // データベース接続情報(環境に合わせて書き換えます)
        String url = "jdbc:mysql://localhost:3306/my_database";
        String user = "root";
        String password = "password123";

        // ステップ1〜5の処理
        try {
            // ステップ1: ドライバのロード
            Class.forName("com.mysql.cj.jdbc.Driver");

            // ステップ2: 接続(Connection)
            try (Connection conn = DriverManager.getConnection(url, user, password);
                 // ステップ3: 命令の準備(Statement)
                 Statement stmt = conn.createStatement()) {

                String sql = "SELECT id, name FROM users";
                
                // ステップ4: 実行と結果取得(ResultSet)
                try (ResultSet rs = stmt.executeQuery(sql)) {
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        String name = rs.getString("name");
                        System.out.println("ID: " + id + ", 名前: " + name);
                    }
                }
            } // ステップ5: closeはtry-with-resources文で自動的に行われます
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}

このコードはコンソールに出力するシンプルな例ですが、実際にはここで取得したデータをJSP(画面を作る部品)に渡してブラウザに表示させます。

発展:PreparedStatementを使った安全な検索

実際の開発では、セキュリティ(SQLインジェクション対策)のために、Statementの代わりにPreparedStatementを使うのが一般的です。検索条件を「?」で指定する書き方です。


// 特定のIDのユーザーを検索する例
String sql = "SELECT name FROM users WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
    // 1番目の「?」に検索したいID(例:10)をセットする
    pstmt.setInt(1, 10);
    
    try (ResultSet rs = pstmt.executeQuery()) {
        if (rs.next()) {
            String userName = rs.getString("name");
            System.out.println("検索結果: " + userName);
        }
    }
}

このように、JavaのJDBCを使えば、データベースから自由自在にデータを取得することができます。まずは「ドライバを用意する」「5つのステップを守る」という2点をしっかり押さえておきましょう。

初心者向けのポイント解説

  • SQL文とは: データベースに対する「お願い」を書くための言語です。Javaとは別の言語ですが、Javaのプログラムの中に文字列として書き込みます。
  • 例外処理(try-catch): DB接続は、パスワード間違いやサーバーダウンなどで失敗することがあります。その時にアプリが強制終了しないように「もしエラーが起きたらこうする」という処理を書くのが一般的です。
  • インポート文: java.sql.* パッケージに入っているクラスをたくさん使います。Eclipseなら、エラーが出た場所にマウスを合わせて「インポートの追加」を選ぶと自動で書いてくれます。

4. データベース接続(Connection)とSQL実行(Statement)の実装方法

4. データベース接続(Connection)とSQL実行(Statement)の実装方法
4. データベース接続(Connection)とSQL実行(Statement)の実装方法

Javaのプログラムからデータベースを操作するための心臓部とも言えるのが、Connection(コネクション)Statement(ステートメント)です。これらはJDBCが提供するインターフェースであり、データベースとの通信経路を確保し、具体的な命令を届ける役割を担っています。まずはそれぞれの役割と、具体的な実装時のポイントを詳しく見ていきましょう。

Connection:データベースへの専用道路を引く

データベース接続を確立するということは、お使いのJavaプログラムからデータベースサーバーまでの間に、データの往来が可能な専用の通信路を作ることを意味します。この通信路を管理するのがjava.sql.Connectionオブジェクトです。

接続を確立するためには、DriverManager.getConnection()メソッドを使用します。このとき、接続先を示すJDBC URL、データベースにアクセスするためのユーザー名、そしてパスワードの3要素が必須となります。URLの形式はデータベース製品ごとに異なりますが、MySQLの場合はjdbc:mysql://ホスト名:ポート番号/データベース名という形式が一般的です。

Statement:SQLをデータベースへ届ける運搬車

接続という道路が完成したら、次はその上を走る運搬車が必要です。それがjava.sql.Statementです。Statementオブジェクトは、作成したConnectionから生成されます。このオブジェクトの中に「SELECT」「INSERT」「UPDATE」「DELETE」といったSQL文を載せて実行することで、データベースに対して具体的な操作を依頼することができるのです。

PreparedStatement:安全性と効率を高める上位互換

実際の現場で強く推奨されるのがjava.sql.PreparedStatementです。これは通常のStatementを拡張したもので、事前にSQL文の型をデータベースに伝えておき、変動する値の部分を「?(プレースホルダ)」として定義します。これにより、SQLインジェクションという重大なセキュリティ被害を防ぐことができるだけでなく、一度解析したSQLを再利用するため処理速度が向上するというメリットもあります。


// PreparedStatementを使用したデータの登録例
String insertSql = "INSERT INTO products (product_name, price) VALUES (?, ?)";

try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
    
    // 1番目の「?」に商品名をセット
    pstmt.setString(1, "高性能ノートPC");
    // 2番目の「?」に価格をセット
    pstmt.setInt(2, 125000);
    
    // 実行(更新系はexecuteUpdateを使用する)
    int affectedRows = pstmt.executeUpdate();
    System.out.println(affectedRows + "件のデータを登録しました。");
} catch (SQLException e) {
    e.printStackTrace();
}

上記のコードのように、executeUpdate()メソッドは、追加や更新によって影響を受けた行数を数値で返します。これにより、正しく処理が行われたかをプログラム側で判断することが可能になります。

5. 取得結果を処理するResultSetの使い方とデータ抽出のコツ

5. 取得結果を処理するResultSetの使い方とデータ抽出のコツ
5. 取得結果を処理するResultSetの使い方とデータ抽出のコツ

SELECT文を実行してデータベースからデータを取り出した際、その結果を保持する特別なオブジェクトがResultSet(リザルトセット)です。ResultSetは、検索結果を仮想的な「表」として扱います。しかし、Javaのリストのように一度に全データをメモリに読み込むのではなく、カーソルと呼ばれるポインタを一行ずつ動かしながらデータを読み進める仕組みになっています。

ResultSetの基本的な操作フロー

ResultSetからデータを取り出すには、まずnext()メソッドを呼び出す必要があります。このメソッドは、カーソルを次の行に移動させ、データが存在すればtrueを、なければfalseを返します。そのため、通常はwhile(rs.next())というループ処理の中でデータを抽出します。

データ型に応じた抽出メソッドの選択

ResultSetには、列のデータ型に合わせて様々な「getメソッド」が用意されています。これらを使い分けることで、データベース上の型を適切なJavaの型へと変換できます。

  • getString("列名"): 文字列(VARCHAR等)を取得。
  • getInt("列名"): 整数(INT等)を取得。
  • getDate("列名"): 日付(DATE等)を取得。
  • getDouble("列名"): 浮動小数点(DOUBLE等)を取得。

データ抽出を効率化するコツ

データ抽出の際には、列の名前を直接指定する方法(rs.getString("user_name"))と、列の番号を指定する方法(rs.getString(1))があります。開発現場では、コードの可読性を高め、後の修正ミスを防ぐために、列名での指定が強く推奨されます。また、大量のデータを一度に取得するとメモリを圧迫するため、SQL文側でLIMIT句などを使って取得件数を絞り込むことも、パフォーマンスを維持する重要なコツです。


// ResultSetを駆使してユーザー一覧を表示する高度な例
String query = "SELECT id, name, email, created_at FROM users WHERE status = ?";

try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement pstmt = conn.prepareStatement(query)) {
    
    pstmt.setString(1, "active"); // 有効なユーザーのみ
    
    try (ResultSet rs = pstmt.executeQuery()) {
        System.out.println("--- ユーザー一覧 ---");
        while (rs.next()) {
            // 列名を指定して安全にデータを取得
            int userId = rs.getInt("id");
            String userName = rs.getString("name");
            String userEmail = rs.getString("email");
            java.sql.Timestamp createdAt = rs.getTimestamp("created_at");
            
            // 取得したデータの表示
            System.out.println("ID: " + userId + " | 名前: " + userName + " | メール: " + userEmail + " | 登録日: " + createdAt);
        }
    }
} catch (SQLException e) {
    System.err.println("データベースからのデータ取得中にエラーが発生しました。");
    e.printStackTrace();
}

--- ユーザー一覧 ---
ID: 1 | 名前: 田中太郎 | メール: tanaka@example.com | 登録日: 2026-01-01 10:00:00.0
ID: 2 | 名前: 佐藤花子 | メール: sato@example.com | 登録日: 2026-01-15 14:30:00.0

6. リソースリークを防ぐtry-with-resourcesによる適切なクローズ処理

6. リソースリークを防ぐtry-with-resourcesによる適切なクローズ処理
6. リソースリークを防ぐtry-with-resourcesによる適切なクローズ処理

Javaのデータベースプログラミングにおいて、最も注意しなければならないのがリソースの解放です。Connection、Statement、ResultSetといったオブジェクトは、コンピュータのメモリやネットワーク接続を消費する「リソース」です。これらを使い終わった後に放置すると、サーバーの資源が枯渇し、最終的にはシステム全体が停止する「リソースリーク」を引き起こします。

昔ながらのclose処理と、その限界

Java 6以前では、finallyブロックの中で一つずつclose()メソッドを呼び出す必要がありました。しかし、close処理自体が例外を投げる可能性があるため、コードが非常に複雑で読みにくいものになっていました。これを「ボイラープレートコード(定型的な面倒な記述)」と呼び、バグの温床となっていました。

現代の標準:try-with-resources構文

Java 7以降で導入されたtry-with-resources構文は、この問題を劇的に解決しました。tryの後の括弧()の中でリソースを宣言するだけで、tryブロックを抜ける際にJavaが自動的に、かつ確実にclose()を呼び出してくれます。途中でエラーが発生しても、正常に終了しても関係なく解放されるため、極めて安全です。

リソース解放の順番:後入れ先出しの原則

リソースを閉じる順番には決まりがあります。基本的には「開いた順序の逆」で閉じていきます。つまり、ResultSet、Statement、Connectionの順です。try-with-resourcesを使えば、この順序もJavaが適切に管理してくれるため、開発者はビジネスロジックの開発に集中できるのです。

サーブレットにおける長期運用の鍵

Webアプリケーションは、一度起動すると数ヶ月、数年にわたって動き続けることも珍しくありません。一回のリクエストで解放し忘れたリソースは微々たるものかもしれませんが、数万人のユーザーがアクセスすれば、あっという間にサーバーは限界を迎えます。サーブレット内でJDBCを扱う際は、必ずtry-with-resourcesを使用し、例外が発生した場合でも確実にリソースが返却される設計を徹底しましょう。


// 複数のリソースを同時に管理するtry-with-resourcesの書き方
public void deleteUser(int userId) {
    String sql = "DELETE FROM users WHERE id = ?";
    
    // セミコロンで区切って複数のリソースを宣言できる
    try (Connection conn = DriverManager.getConnection(url, user, password);
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
        
        pstmt.setInt(1, userId);
        int result = pstmt.executeUpdate();
        
        if (result > 0) {
            System.out.println("ID: " + userId + " のユーザーを削除しました。");
        } else {
            System.out.println("該当するユーザーが見つかりませんでした。");
        }
        
    } catch (SQLException e) {
        // ログ出力やエラー画面への遷移処理など
        System.err.println("削除処理中に例外が発生しましたが、接続は自動で閉じられます。");
        e.printStackTrace();
    }
}

ここに注意!

「自分一人で開発しているから大丈夫」と思いがちですが、データベース接続は有限の資源です。特にMySQLなどのデータベースサーバー側でも「同時に接続できる最大数」が決まっており、それを超えると新しいユーザーがサイトにアクセスできなくなります。適切なクローズ処理は、単なるマナーではなく、システムを安定稼働させるための絶対条件です。

7. 実践的なサーブレットとJDBCの連携パターン

7. 実践的なサーブレットとJDBCの連携パターン
7. 実践的なサーブレットとJDBCの連携パターン

これまでの知識を総動員して、実際のWebアプリケーション開発で使われる設計思想を学びましょう。サーブレットの中に直接すべてのDB処理を書き込むのは、小規模な練習用プログラムであれば問題ありません。しかし、プログラムが大きくなるにつれて、役割を分担させることが重要になります。

DAO(Data Access Object)パターンの導入

プロの現場では、データベース操作専用のクラスであるDAO(ダオ)を作成するのが一般的です。サーブレットは「画面からの入力を受け取り、DAOに処理を頼むだけ」という形にします。これにより、データベースの種類が変わった時や、テーブルの構造が変わった時に、サーブレット側を修正する必要がなくなるという大きなメリットがあります。

DTO(Data Transfer Object)によるデータの受け渡し

ResultSetから取り出したデータは、そのまま扱うのではなく、DTO(ディートーオー)と呼ばれる専用のJavaクラス(JavaBeans)に詰め替えて持ち運びます。例えば「UserDTO」というクラスを作り、そこにidやnameといったフィールドを持たせます。これによって、複数のデータを一つのオブジェクトとして、サーブレットからJSPへと簡単に渡すことができるようになります。

コネクションプーリングによる高速化

実は、データベースへの接続(Connectionの確立)は、コンピュータにとって非常に重い処理です。アクセスのたびに接続と切断を繰り返すと、Webサイトの表示が遅くなってしまいます。そこで、あらかじめいくつかの接続を作って「プール(貯水池)」に溜めておき、必要な時だけ貸し出すコネクションプーリングという技術が実戦では使われます。JDBCの基本を理解した後は、Apache DBCPやHikariCPといったライブラリ、あるいはTomcatのデータソース機能についても調べてみると、さらに一歩進んだJavaエンジニアになれるはずです。

JavaサーブレットとJDBCの連携は、Web開発の基盤となる非常に奥が深いテーマです。まずは基本的なSELECT文の実行と、確実なリソースの解放をマスターすることから始めてください。一つ一つのステップを丁寧に実装していくことで、安全で堅牢なデータベース連携アプリケーションを構築できるようになります。

7. 取得したデータをブラウザに表示するためのDTOとListの活用

7. 取得したデータをブラウザに表示するためのDTOとListの活用
7. 取得したデータをブラウザに表示するためのDTOとListの活用

データベースから取得したデータを、単にコンソールに表示するだけではWebアプリケーションとして成立しません。ブラウザ上の綺麗な表形式などで表示するためには、DTO(Data Transfer Object)List(リスト)を組み合わせたデータの構造化が不可欠です。ここでは、現場で必須となるデータの持ち運び方について深掘りします。

DTO(Data Transfer Object)とは何か?

データベースの1行(レコード)を、Javaの1つのオブジェクトとして表現したものをDTOと呼びます。例えば、「会員テーブル」に「ID」「名前」「メールアドレス」という列がある場合、それらと同じフィールドを持つUserDTOクラスを作成します。これにより、バラバラのデータとして扱うのではなく、「一人のユーザーという固まり」として安全にプログラム内で受け渡しができるようになります。

DTOを作成する際は、カプセル化の原則に基づき、フィールドはprivateにし、外部からアクセスするためのgettersetterを用意するのが基本ルールです。また、引数なしのコンストラクタを持つ「JavaBeans」の形式に従うことで、多くのフレームワークとの親和性も高まります。

Listを活用して複数行のデータをまとめる

データベースからSELECT文で全件取得した場合、複数のレコードが返ってきます。この複数のDTOを一つにまとめてサーブレットから画面(JSP)へ渡すために、java.util.ArrayListを使用します。ResultSetをループで回しながら、1行ごとにDTOをインスタンス化し、それをListにどんどん追加していくという流れです。

この手法の最大のメリットは、「データベース接続を早めに閉じられる」ことです。ResultSetのまま画面に渡そうとすると、画面を表示する瞬間までDB接続を維持しなければならず、リソースの無駄遣いやエラーの原因になります。一度Listに詰め替えてしまえば、DB接続を切断した後でも、メモリ上のListから自由にデータを取り出して画面に表示できるのです。

実装例:DTOクラスとサーブレットでのリスト化

まずは、データを格納するための箱となるDTOクラスを定義します。シンプルに会員情報を保持する設計にします。


package model;

import java.io.Serializable;

/**
 * 会員情報を保持するDTOクラス
 * JavaBeansの構成に従います
 */
public class UserDTO implements Serializable {
    private int id;
    private String name;
    private String email;

    public UserDTO() {}

    public UserDTO(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

次に、このDTOを使用してデータベースから取得したデータをListに格納し、リクエストスコープに保存してJSPへ転送するサーブレットの処理を記述します。


@WebServlet("/ShowUsersServlet")
public class ShowUsersServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        List<UserDTO> userList = new ArrayList<>();
        String url = "jdbc:mysql://localhost:3306/sample_db";
        String user = "root";
        String pass = "password";

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            try (Connection conn = DriverManager.getConnection(url, user, pass);
                 PreparedStatement pstmt = conn.prepareStatement("SELECT id, name, email FROM users");
                 ResultSet rs = pstmt.executeQuery()) {

                while (rs.next()) {
                    // 1行のデータをDTOに詰め替える
                    UserDTO u = new UserDTO();
                    u.setId(rs.getInt("id"));
                    u.setName(rs.getString("name"));
                    u.setEmail(rs.getString("email"));
                    
                    // リストに追加する
                    userList.add(u);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 取得したリストをJSPに渡す準備
        request.setAttribute("users", userList);
        request.getRequestDispatcher("/WEB-INF/view/userList.jsp").forward(request, response);
    }
}

JSPでの表示処理(JSTLの活用)

サーブレットから渡されたListは、JSP側でc:forEachタグ(JSTL)を使ってループ処理することで、HTMLのテーブルとして出力します。JavaコードをHTMLに埋め込むスクリプトレット(<% %>)は現在非推奨となっており、タグライブラリを使用するのが標準的な開発スタイルです。


<!-- JSP内での表示例 -->
<table class="table table-striped">
    <thead>
        <tr>
            <th>ID</th><th>名前</th><th>メールアドレス</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="user" items="${users}">
            <tr>
                <td>${user.id}</td>
                <td>${user.name}</td>
                <td>${user.email}</td>
            </tr>
        </c:forEach>
    </tbody>
</table>

8. SQLインジェクションを防ぐPreparedStatementの重要性

8. SQLインジェクションを防ぐPreparedStatementの重要性
8. SQLインジェクションを防ぐPreparedStatementの重要性

Webアプリケーションの開発において、絶対に避けては通れないのがセキュリティ対策です。特にデータベースを扱う際に最も警戒すべき攻撃がSQLインジェクションです。この攻撃は、悪意のあるユーザーが入力フォームなどに不正なSQLの断片を紛れ込ませることで、データベース内の情報を盗み出したり、データを勝手に消去したりする恐ろしいものです。

なぜ脆弱性が生まれるのか?

例えば、ログイン画面でユーザーIDを受け取り、それを文字列連結でSQLを組み立てているとします。"SELECT * FROM users WHERE id = '" + inputId + "'" というコードに対し、攻撃者が ' OR '1'='1 という値を入力した場合、完成するSQLは SELECT * FROM users WHERE id = '' OR '1'='1' となり、パスワードを知らなくても全ユーザーの情報が筒抜けになってしまいます。これがSQLインジェクションの典型的な手口です。

PreparedStatementが最強の盾になる理由

JDBCのPreparedStatementは、この攻撃を防ぐための最も効果的な手段です。PreparedStatementは、SQL文の骨組み(構文)を先にデータベースに送り、後から値だけをセットする「2段階方式」を採用しています。値として渡された文字列の中にたとえSQLの命令が含まれていても、データベース側はそれを単なる「文字データ」として扱い、命令として実行することはありません。この仕組みをプレースホルダと呼びます。

実装時のベストプラクティス

以下のコードは、検索機能を安全に実装する例です。ユーザーの入力をそのままSQLに組み込まず、必ず?(プレースホルダ)を経由させます。また、値のセット時には、データの型(文字列ならsetString、数値ならsetInt)を明示的に指定するため、意図しない型のデータが入り込む隙を与えません。


// 検索キーワードを受け取る
String keyword = request.getParameter("searchWord");

// 悪い例(脆弱性あり): 
// String sql = "SELECT * FROM items WHERE name LIKE '%" + keyword + "%'";

// 良い例(安全):
String sql = "SELECT * FROM items WHERE name LIKE ?";

try (Connection conn = DriverManager.getConnection(url, user, pass);
     PreparedStatement pstmt = conn.prepareStatement(sql)) {
    
    // 値をセットする際、特殊文字は自動的にエスケープされる
    pstmt.setString(1, "%" + keyword + "%");
    
    try (ResultSet rs = pstmt.executeQuery()) {
        while (rs.next()) {
            // 処理を記述
        }
    }
} catch (SQLException e) {
    e.printStackTrace();
}

パフォーマンス面でのメリット

PreparedStatementを使うメリットはセキュリティだけではありません。多くのデータベース製品では、一度送られたSQLの構文を解析(パース)してキャッシュする仕組みを持っています。プレースホルダを使ったSQLは、値が変わっても「同じ構造のSQL」と見なされるため、2回目以降の実行時に解析処理が省略され、パフォーマンスが向上します。特に大量のデータを連続して登録(INSERT)するような場面では、その差は歴然です。

初心者の方は、「SQLに変数を入れるときは、絶対にStatementではなくPreparedStatementを使う」という鉄則を、今この瞬間に自分自身のルールとして刻んでください。これはプロのエンジニアとして働く上で、最も基本的かつ重要なマナーでもあります。

9. サーブレットでのJDBC操作におけるよくあるエラーと解決策のまとめ

9. サーブレットでのJDBC操作におけるよくあるエラーと解決策のまとめ
9. サーブレットでのJDBC操作におけるよくあるエラーと解決策のまとめ

開発を進めていると、必ずと言っていいほどエラー(例外)に遭遇します。特にサーブレットとデータベースの連携は、プログラムの外(ネットワークやDB設定)に原因があることが多いため、エラーログを読み解く力が試されます。ここでは初心者が陥りやすいエラーとその解決策をまとめました。

ClassNotFoundException: com.mysql.cj.jdbc.Driver

これは「JDBCドライバ(通訳さん)が見つかりません」というエラーです。プログラム側でドライバを指定しているのに、実際のファイル(JARファイル)がプロジェクトに含まれていない場合に発生します。

  • 解決策: WEB-INF/libフォルダにドライバのJARファイルが配置されているか再確認してください。Eclipseのビルドパス設定だけでなく、Webアプリとして実行される場所(libフォルダ)に物理的にファイルが存在することが必須です。

java.sql.SQLException: Access denied for user...

「ユーザーのアクセスが拒否されました」というエラーです。接続情報のURL、ユーザー名、パスワードのいずれかが間違っていることを示しています。

  • 解決策: まずはデータベースに直接(コマンドラインや管理ツールから)ログインできるか試してください。また、Javaコード内のタイポ(打ち間違い)や、大文字小文字の区別を今一度チェックしましょう。特に開発環境と本番環境でパスワードが異なる場合は注意が必要です。

Communications link failure

「データベースサーバーと通信ができません」というエラーです。データベース自体が起動していないか、ファイアウォールによって接続が遮断されている可能性があります。

  • 解決策: データベース(MySQL等)が実行中であるか確認してください。また、接続URLのホスト名やポート番号(通常は3306等)が正しいかも確認ポイントです。

java.sql.SQLException: Operation not allowed after ResultSet closed

「ResultSetを閉じた後に、そのデータを使おうとしています」というエラーです。try-with-resourcesでResultSetを自動で閉じた後に、その中のデータを取り出そうとすると発生します。

  • 解決策: 前述の「DTOへの詰め替え」を徹底してください。DB接続が開いているうちにすべてのデータをJavaのオブジェクト(ListやDTO)にコピーし、接続を閉じた後はそのリストを操作するように設計を変更します。

Column 'xxx' not found

「指定した列名が結果に見当たりません」というエラーです。rs.getString("name")と書いているのに、SQL文でその列を取得していない(または列名が間違っている)場合に起こります。

  • 解決策: SQL文(SELECT句)と、ResultSetからデータを取得する際の列名が完全に一致しているか確認してください。エイリアス(AS句)を使っている場合は、エイリアス名の方を指定する必要があります。

エラーを恐れず成長の糧にしよう

エラーが出たときはパニックにならず、まずはスタックトレース(エラーの詳細表示)を一番下まで読みましょう。そこに「Caused by: ...」という記述があれば、それが真の原因です。JDBC周りのトラブルは一度解決してしまえば、次からはすぐに対処できるようになります。不具合を一つずつ潰していく過程こそが、Javaエンジニアとしての実力を最も高めてくれる貴重な時間なのです。

ここまでの知識で、サーブレットからデータベースを自在に操り、取得したデータを美しく画面に表示する基礎はすべて揃いました。あとは自分の手でコードを書き、試行錯誤を繰り返すことで、本当の意味でのスキルが身につきます。次は登録処理や削除処理にも挑戦し、より高度なWebアプリケーションの構築を目指しましょう。

コメント
コメント投稿は、ログインしてください

まだ口コミはありません。

カテゴリの一覧へ
新着記事
application.ymlの基本的な記述ルールを完全解説!初心者向けインデントと階層の書き方
Spring Bootでサーバーのポート番号を変更する方法を完全ガイド!初心者でもわかるserver.portの設定
Springでよく使うアノテーション一覧(超入門)初心者向けのやさしい解説
Springプロジェクトの全体構成を見てみよう!初心者向けにわかりやすく解説
人気記事
No.1
Java&Spring記事人気No1
SQLのロック(LOCK)を完全ガイド!初心者でもわかるデータの整合性の守り方
No.2
Java&Spring記事人気No2
SQLのビュー(VIEW)を完全ガイド!初心者でもわかる仮想テーブルの使い方
No.3
Java&Spring記事人気No3
Spring Bootのディレクトリ構成と役割とは?初心者でもわかる完全ガイド
No.4
Java&Spring記事人気No4
Springフレームワークとは?初心者向けにやさしく解説