0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java研修】Servlet/JSP入門⑦ JDBC連携(データベース操作)

0
Posted at

はじめに

Servlet/JSP入門の第7回は JDBC連携(データベース操作) です。

Webアプリケーションではほぼ必ずデータベースを使います。ユーザー情報、商品データ、注文履歴......これらはすべてDBに保存されます。JavaからDBに接続するための標準APIが JDBC(Java Database Connectivity) です。

第7回で学ぶこと

  • JDBCの概要とドライバの導入
  • Connection, Statement, PreparedStatement, ResultSet の使い方
  • CRUD操作(Create, Read, Update, Delete)
  • SQLインジェクション対策
  • DAOパターン(Data Access Object)
  • コネクションプーリングの基礎
  • 完成例:ユーザー管理Webアプリ

1. JDBCの概要

JDBCとは?

JDBC は、JavaプログラムからリレーショナルDBに接続し、SQLを実行するための標準APIです。

┌──────────┐       JDBC API        ┌──────────────┐
│          │  ──────────────────→   │              │
│  Java    │                       │  データベース   │
│ アプリ    │  ←──────────────────   │  (MySQL等)   │
│          │       結果セット        │              │
└──────────┘                       └──────────────┘
         ↕
   JDBCドライバ(各DB用)

JDBCの処理の流れ

1. JDBCドライバをロード
2. データベースに接続(Connection取得)
3. SQL文を作成(Statement / PreparedStatement)
4. SQLを実行
5. 結果を処理(ResultSet)
6. リソースを解放(close)

使用するデータベース

本記事では MySQL を使用します(H2 Database でも基本は同じです)。

DB 特徴 JDBCドライバ
MySQL 最も広く使われるOSS RDBMS mysql-connector-j
H2 Database Java製の軽量DB(組み込み可能) h2
PostgreSQL 高機能なOSS RDBMS postgresql

2. 環境準備

MySQLのセットアップ

MySQLをインストールし、テスト用のデータベースとテーブルを作成します。

-- データベース作成
CREATE DATABASE web_study DEFAULT CHARACTER SET utf8mb4;

-- データベースを使用
USE web_study;

-- ユーザーテーブル作成
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    age INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- テストデータ挿入
INSERT INTO users (name, email, age) VALUES
('田中太郎', 'tanaka@example.com', 25),
('鈴木花子', 'suzuki@example.com', 30),
('佐藤次郎', 'sato@example.com', 28);

JDBCドライバの導入

方法1:手動でJARを追加

  1. Maven Repository から mysql-connector-j-x.x.x.jar をダウンロード
  2. プロジェクトの WEB-INF/lib/ にコピー

方法2:Mavenを使う場合

pom.xml に依存を追加:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.3.0</version>
</dependency>

3. JDBCの基本操作

3.1 データベースへの接続

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

public class DBConnectionTest {
    public static void main(String[] args) {
        // JDBC接続情報
        String url = "jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo";
        String user = "root";
        String password = "your_password";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            System.out.println("データベースに接続しました!");
            System.out.println("DB製品名: " + conn.getMetaData().getDatabaseProductName());
            System.out.println("DBバージョン: " + conn.getMetaData().getDatabaseProductVersion());
        } catch (SQLException e) {
            System.out.println("接続に失敗しました: " + e.getMessage());
        }
    }
}

JDBC URLの構成

jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo
│    │       │         │    │          │
│    │       │         │    │          └─ オプション(パラメータ)
│    │       │         │    └─ データベース名
│    │       │         └─ ポート番号
│    │       └─ ホスト名
│    └─ DBの種類
└─ JDBCプロトコル

3.2 データの検索(SELECT)

import java.sql.*;

public class SelectExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo";
        String user = "root";
        String password = "your_password";

        String sql = "SELECT id, name, email, age FROM users";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            System.out.println("=== ユーザー一覧 ===");
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                int age = rs.getInt("age");

                System.out.println(id + ": " + name + " (" + email + ") " + age + "歳");
            }

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

出力例:

=== ユーザー一覧 ===
1: 田中太郎 (tanaka@example.com) 25歳
2: 鈴木花子 (suzuki@example.com) 30歳
3: 佐藤次郎 (sato@example.com) 28歳

3.3 データの挿入(INSERT)

String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";

try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement pstmt = conn.prepareStatement(sql)) {

    pstmt.setString(1, "高橋美咲");
    pstmt.setString(2, "takahashi@example.com");
    pstmt.setInt(3, 22);

    int rows = pstmt.executeUpdate();
    System.out.println(rows + "件のデータを挿入しました。");

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

3.4 データの更新(UPDATE)

String sql = "UPDATE users SET age = ? WHERE name = ?";

try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement pstmt = conn.prepareStatement(sql)) {

    pstmt.setInt(1, 26);
    pstmt.setString(2, "田中太郎");

    int rows = pstmt.executeUpdate();
    System.out.println(rows + "件のデータを更新しました。");

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

3.5 データの削除(DELETE)

String sql = "DELETE FROM users WHERE id = ?";

try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement pstmt = conn.prepareStatement(sql)) {

    pstmt.setInt(1, 4);

    int rows = pstmt.executeUpdate();
    System.out.println(rows + "件のデータを削除しました。");

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

JDBC主要クラスまとめ

クラス/インターフェース 役割
DriverManager DBへの接続を管理
Connection DBとの接続を表す
Statement SQL文を実行する(固定SQL向き)
PreparedStatement パラメータ付きSQL文を実行する(推奨)
ResultSet SELECTの結果セットを保持

SQL実行メソッドの使い分け

メソッド 用途 戻り値
executeQuery() SELECT文 ResultSet
executeUpdate() INSERT/UPDATE/DELETE文 int(影響行数)

4. SQLインジェクション対策

危険な例:文字列連結でSQLを組み立てる

// ❌ 絶対にやってはいけない!
String name = request.getParameter("name");
String sql = "SELECT * FROM users WHERE name = '" + name + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

もし name' OR '1'='1 と入力されると......

SELECT * FROM users WHERE name = '' OR '1'='1'

全レコードが取得されてしまいます!さらに '; DROP TABLE users; -- のような入力でテーブルが削除される危険もあります。

安全な例:PreparedStatement を使う

// ✅ PreparedStatementを使えば安全
String name = request.getParameter("name");
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
ResultSet rs = pstmt.executeQuery();

?(プレースホルダ)に値をバインドすることで、入力値がSQLの構文として解釈されることを防ぎます。

方法 安全性 用途
Statement + 文字列連結 危険 パラメータなしの固定SQL
PreparedStatement + ? 安全 パラメータありのSQL(常にこちらを使う)

鉄則:ユーザー入力をSQLに含める場合は、必ず PreparedStatement を使う!


5. DAOパターン(Data Access Object)

DAOパターンとは?

DAO は、データベースへのアクセス処理を1つのクラスに集約するデザインパターンです。

┌──────────┐     ┌──────────┐     ┌──────────┐
│ Servlet  │ ──→ │   DAO    │ ──→ │ Database │
│(Controller)│     │          │     │          │
└──────────┘     └──────────┘     └──────────┘
                  ↑
           DB操作はすべてDAOに集約

メリット:

  • DB操作のコードが一箇所にまとまる
  • ServletにSQL文が散らばらない
  • DBを変更しても影響範囲がDAO内に限定される

5.1 Userモデルクラス(JavaBean)

package model;

import java.sql.Timestamp;

public class User {
    private int id;
    private String name;
    private String email;
    private int age;
    private Timestamp createdAt;

    // デフォルトコンストラクタ
    public User() {}

    // パラメータ付きコンストラクタ
    public User(String name, String email, int age) {
        this.name = name;
        this.email = email;
        this.age = age;
    }

    // getter / setter
    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; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    public Timestamp getCreatedAt() { return createdAt; }
    public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
}

5.2 UserDAO クラス

package dao;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

import model.User;

public class UserDAO {

    private static final String URL = "jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo";
    private static final String DB_USER = "root";
    private static final String DB_PASS = "your_password";

    // DB接続を取得
    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, DB_USER, DB_PASS);
    }

    // 全件取得
    public List<User> findAll() {
        List<User> users = new ArrayList<>();
        String sql = "SELECT id, name, email, age, created_at FROM users ORDER BY id";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {

            while (rs.next()) {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setEmail(rs.getString("email"));
                user.setAge(rs.getInt("age"));
                user.setCreatedAt(rs.getTimestamp("created_at"));
                users.add(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return users;
    }

    // IDで1件取得
    public User findById(int id) {
        String sql = "SELECT id, name, email, age, created_at FROM users WHERE id = ?";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setInt(1, id);
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    User user = new User();
                    user.setId(rs.getInt("id"));
                    user.setName(rs.getString("name"));
                    user.setEmail(rs.getString("email"));
                    user.setAge(rs.getInt("age"));
                    user.setCreatedAt(rs.getTimestamp("created_at"));
                    return user;
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 新規登録
    public boolean insert(User user) {
        String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getEmail());
            pstmt.setInt(3, user.getAge());

            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    // 更新
    public boolean update(User user) {
        String sql = "UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getEmail());
            pstmt.setInt(3, user.getAge());
            pstmt.setInt(4, user.getId());

            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    // 削除
    public boolean delete(int id) {
        String sql = "DELETE FROM users WHERE id = ?";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setInt(1, id);
            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
}

6. コネクションプーリング

なぜコネクションプーリングが必要?

毎回 DriverManager.getConnection() で接続を作ると、接続のたびにTCP通信・認証が発生し非常に遅くなります。

毎回新規接続の場合:
  リクエスト1 → 接続作成(遅い) → SQL実行 → 接続破棄
  リクエスト2 → 接続作成(遅い) → SQL実行 → 接続破棄

コネクションプーリングの場合:
  起動時 → 接続を複数作成してプールに保持
  リクエスト1 → プールから借りる(速い) → SQL実行 → プールに返す
  リクエスト2 → プールから借りる(速い) → SQL実行 → プールに返す

Tomcatのコネクションプール設定

src/main/webapp/META-INF/context.xml を作成:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Resource
        name="jdbc/web_study"
        auth="Container"
        type="javax.sql.DataSource"
        maxTotal="20"
        maxIdle="5"
        maxWaitMillis="10000"
        driverClassName="com.mysql.cj.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/web_study?useSSL=false&amp;serverTimezone=Asia/Tokyo"
        username="root"
        password="your_password" />
</Context>

src/main/webapp/WEB-INF/web.xml にリソース参照を追加:

<resource-ref>
    <res-ref-name>jdbc/web_study</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

DAOでコネクションプールを使う

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class UserDAO {

    private DataSource dataSource;

    public UserDAO() {
        try {
            Context initContext = new InitialContext();
            Context envContext = (Context) initContext.lookup("java:comp/env");
            dataSource = (DataSource) envContext.lookup("jdbc/web_study");
        } catch (Exception e) {
            throw new RuntimeException("DataSourceの取得に失敗しました", e);
        }
    }

    private Connection getConnection() throws SQLException {
        return dataSource.getConnection();  // プールから接続を取得
    }

    // 以降のメソッドは同じ
}
方法 メリット デメリット
DriverManager シンプル 毎回接続で遅い
コネクションプール 高速、リソース管理が効率的 設定が必要

学習段階では DriverManager で十分です。 実務ではコネクションプールが必須になります。


7. 完成例:ユーザー管理Webアプリ

DAOパターンを使って、ブラウザからユーザーの一覧表示・追加・削除ができるアプリを作ります。

プロジェクト構成

WebStudy/
├── src/main/java/
│   ├── model/
│   │   └── User.java              ← データモデル
│   ├── dao/
│   │   └── UserDAO.java           ← DB操作
│   └── servlet/
│       ├── UserListServlet.java    ← 一覧表示
│       ├── UserAddServlet.java     ← ユーザー追加
│       └── UserDeleteServlet.java  ← ユーザー削除
├── src/main/webapp/
│   └── WEB-INF/
│       └── jsp/
│           ├── userList.jsp        ← 一覧画面
│           └── userForm.jsp        ← 登録フォーム

UserListServlet.java

package servlet;

import java.io.IOException;
import java.util.List;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import dao.UserDAO;
import model.User;

@WebServlet("/users")
public class UserListServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        UserDAO dao = new UserDAO();
        List<User> users = dao.findAll();

        request.setAttribute("users", users);
        request.getRequestDispatcher("/WEB-INF/jsp/userList.jsp").forward(request, response);
    }
}

UserAddServlet.java

package servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import dao.UserDAO;
import model.User;

@WebServlet("/users/add")
public class UserAddServlet extends HttpServlet {

    // フォーム表示
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        request.getRequestDispatcher("/WEB-INF/jsp/userForm.jsp").forward(request, response);
    }

    // ユーザー登録処理
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");

        String name = request.getParameter("name");
        String email = request.getParameter("email");
        int age = Integer.parseInt(request.getParameter("age"));

        User user = new User(name, email, age);
        UserDAO dao = new UserDAO();
        dao.insert(user);

        // 一覧画面にリダイレクト
        response.sendRedirect(request.getContextPath() + "/users");
    }
}

UserDeleteServlet.java

package servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import dao.UserDAO;

@WebServlet("/users/delete")
public class UserDeleteServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        int id = Integer.parseInt(request.getParameter("id"));

        UserDAO dao = new UserDAO();
        dao.delete(id);

        response.sendRedirect(request.getContextPath() + "/users");
    }
}

userList.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page import="java.util.List" %>
<%@ page import="model.User" %>
<!DOCTYPE html>
<html>
<head>
    <title>ユーザー管理</title>
    <style>
        body { font-family: sans-serif; margin: 20px; }
        table { border-collapse: collapse; width: 100%; max-width: 800px; }
        th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
        th { background-color: #4CAF50; color: white; }
        tr:nth-child(even) { background-color: #f2f2f2; }
        .btn { padding: 5px 10px; color: white; border: none; cursor: pointer; border-radius: 3px; }
        .btn-danger { background-color: #f44336; }
        .btn-primary { background-color: #2196F3; text-decoration: none; padding: 8px 16px; }
    </style>
</head>
<body>
    <h1>ユーザー管理</h1>
    <p><a href="<%= request.getContextPath() %>/users/add" class="btn btn-primary">新規登録</a></p>

    <%
        List<User> users = (List<User>) request.getAttribute("users");
    %>

    <table>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>メールアドレス</th>
            <th>年齢</th>
            <th>登録日時</th>
            <th>操作</th>
        </tr>
        <% for (User user : users) { %>
        <tr>
            <td><%= user.getId() %></td>
            <td><%= user.getName() %></td>
            <td><%= user.getEmail() %></td>
            <td><%= user.getAge() %></td>
            <td><%= user.getCreatedAt() %></td>
            <td>
                <form action="<%= request.getContextPath() %>/users/delete" method="post"
                      style="display:inline;"
                      onsubmit="return confirm('本当に削除しますか?');">
                    <input type="hidden" name="id" value="<%= user.getId() %>">
                    <button type="submit" class="btn btn-danger">削除</button>
                </form>
            </td>
        </tr>
        <% } %>
    </table>
</body>
</html>

userForm.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>ユーザー登録</title>
    <style>
        body { font-family: sans-serif; margin: 20px; }
        label { display: block; margin-top: 10px; font-weight: bold; }
        input[type="text"], input[type="email"], input[type="number"] {
            padding: 8px; width: 300px; margin-top: 5px;
        }
        .btn { padding: 10px 20px; color: white; border: none; cursor: pointer; margin-top: 15px; border-radius: 3px; }
        .btn-primary { background-color: #4CAF50; }
    </style>
</head>
<body>
    <h1>ユーザー登録</h1>
    <form action="<%= request.getContextPath() %>/users/add" method="post">
        <label>名前</label>
        <input type="text" name="name" required>

        <label>メールアドレス</label>
        <input type="email" name="email" required>

        <label>年齢</label>
        <input type="number" name="age" min="0" max="150" required>

        <br>
        <button type="submit" class="btn btn-primary">登録</button>
    </form>
    <p><a href="<%= request.getContextPath() %>/users">← 一覧に戻る</a></p>
</body>
</html>

練習問題

問題1:商品テーブルのCRUD ⭐

以下のSQLでテーブルを作成し、JDBC で全件取得してコンソールに表示するプログラムを作成してください。

CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price INT NOT NULL
);

INSERT INTO products (name, price) VALUES
('ノートPC', 98000),
('マウス', 2500),
('キーボード', 8000);
模範解答
package exercise;

import java.sql.*;

public class ProductSelect {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo";
        String user = "root";
        String password = "your_password";

        String sql = "SELECT id, name, price FROM products ORDER BY id";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {

            System.out.println("=== 商品一覧 ===");
            System.out.printf("%-5s %-15s %s%n", "ID", "商品名", "価格");
            System.out.println("-".repeat(35));

            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int price = rs.getInt("price");
                System.out.printf("%-5d %-15s %,d円%n", id, name, price);
            }

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

ポイント: try-with-resources を使うことで、ConnectionPreparedStatementResultSet を自動的に閉じることができます。リソースの閉じ忘れはバグの原因になるため、必ず使いましょう。

問題2:検索機能付きServlet ⭐⭐

名前でユーザーをあいまい検索できるServletを作成してください。

  • URLパターン:/users/search
  • GETパラメータ keyword で検索
  • LIKE を使ったあいまい検索
  • 結果をHTMLテーブルで表示
模範解答
package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/users/search")
public class UserSearchServlet extends HttpServlet {

    private static final String URL = "jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo";
    private static final String DB_USER = "root";
    private static final String DB_PASS = "your_password";

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=UTF-8");
        PrintWriter out = response.getWriter();

        String keyword = request.getParameter("keyword");

        out.println("<!DOCTYPE html>");
        out.println("<html><head><title>ユーザー検索</title></head><body>");
        out.println("<h1>ユーザー検索</h1>");
        out.println("<form action='" + request.getRequestURI() + "' method='get'>");
        out.println("  名前: <input type='text' name='keyword' value='" + (keyword != null ? keyword : "") + "'>");
        out.println("  <button type='submit'>検索</button>");
        out.println("</form>");

        if (keyword != null && !keyword.isEmpty()) {
            String sql = "SELECT id, name, email, age FROM users WHERE name LIKE ?";

            try (Connection conn = DriverManager.getConnection(URL, DB_USER, DB_PASS);
                 PreparedStatement pstmt = conn.prepareStatement(sql)) {

                pstmt.setString(1, "%" + keyword + "%");

                try (ResultSet rs = pstmt.executeQuery()) {
                    out.println("<h2>検索結果(キーワード: " + keyword + ")</h2>");
                    out.println("<table border='1'>");
                    out.println("<tr><th>ID</th><th>名前</th><th>メール</th><th>年齢</th></tr>");

                    int count = 0;
                    while (rs.next()) {
                        out.println("<tr>");
                        out.println("<td>" + rs.getInt("id") + "</td>");
                        out.println("<td>" + rs.getString("name") + "</td>");
                        out.println("<td>" + rs.getString("email") + "</td>");
                        out.println("<td>" + rs.getInt("age") + "</td>");
                        out.println("</tr>");
                        count++;
                    }
                    out.println("</table>");
                    out.println("<p>" + count + "件見つかりました。</p>");
                }
            } catch (SQLException e) {
                out.println("<p style='color:red;'>エラー: " + e.getMessage() + "</p>");
            }
        }

        out.println("</body></html>");
    }
}

ポイント: LIKE 検索のワイルドカード % は Java側で "%" + keyword + "%" のように付与し、PreparedStatementsetString でバインドします。こうすればSQLインジェクションも防げます。

問題3:DAOパターンで商品管理 ⭐⭐⭐

以下の要件で、DAOパターンを使った商品管理アプリを作成してください。

  • Product モデルクラス(id, name, price)
  • ProductDAO クラス(findAll, findById, insert, update, delete)
  • ProductListServlet/products で一覧表示)
  • ProductAddServlet/products/add でGET→フォーム表示、POST→登録処理)
  • JSPで画面を作成
模範解答

Product.java

package model;

public class Product {
    private int id;
    private String name;
    private int price;

    public Product() {}

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    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 int getPrice() { return price; }
    public void setPrice(int price) { this.price = price; }
}

ProductDAO.java

package dao;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import model.Product;

public class ProductDAO {

    private static final String URL = "jdbc:mysql://localhost:3306/web_study?useSSL=false&serverTimezone=Asia/Tokyo";
    private static final String DB_USER = "root";
    private static final String DB_PASS = "your_password";

    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, DB_USER, DB_PASS);
    }

    public List<Product> findAll() {
        List<Product> products = new ArrayList<>();
        String sql = "SELECT id, name, price FROM products ORDER BY id";

        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {

            while (rs.next()) {
                Product p = new Product();
                p.setId(rs.getInt("id"));
                p.setName(rs.getString("name"));
                p.setPrice(rs.getInt("price"));
                products.add(p);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return products;
    }

    public Product findById(int id) {
        String sql = "SELECT id, name, price FROM products WHERE id = ?";
        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    Product p = new Product();
                    p.setId(rs.getInt("id"));
                    p.setName(rs.getString("name"));
                    p.setPrice(rs.getInt("price"));
                    return p;
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean insert(Product product) {
        String sql = "INSERT INTO products (name, price) VALUES (?, ?)";
        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, product.getName());
            pstmt.setInt(2, product.getPrice());
            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean update(Product product) {
        String sql = "UPDATE products SET name = ?, price = ? WHERE id = ?";
        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, product.getName());
            pstmt.setInt(2, product.getPrice());
            pstmt.setInt(3, product.getId());
            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean delete(int id) {
        String sql = "DELETE FROM products WHERE id = ?";
        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);
            return pstmt.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
}

ProductListServlet.java

package servlet;

import java.io.IOException;
import java.util.List;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import dao.ProductDAO;
import model.Product;

@WebServlet("/products")
public class ProductListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ProductDAO dao = new ProductDAO();
        List<Product> products = dao.findAll();
        request.setAttribute("products", products);
        request.getRequestDispatcher("/WEB-INF/jsp/productList.jsp").forward(request, response);
    }
}

ProductAddServlet.java

package servlet;

import java.io.IOException;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import dao.ProductDAO;
import model.Product;

@WebServlet("/products/add")
public class ProductAddServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.getRequestDispatcher("/WEB-INF/jsp/productForm.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");

        String name = request.getParameter("name");
        int price = Integer.parseInt(request.getParameter("price"));

        Product product = new Product(name, price);
        new ProductDAO().insert(product);

        response.sendRedirect(request.getContextPath() + "/products");
    }
}

ポイント: DAOパターンにより、Servlet(Controller)にはDB操作のコードが一切含まれません。責務が明確に分離され、テストやメンテナンスが容易になります。


まとめ

学んだこと キーワード
JDBCの概要 JDBC URL、DriverManager、JDBCドライバ
接続と操作 Connection、Statement、PreparedStatement、ResultSet
CRUD操作 SELECT(executeQuery)、INSERT/UPDATE/DELETE(executeUpdate)
セキュリティ SQLインジェクション、PreparedStatement の ?
DAOパターン DB操作の集約、Model + DAO の分離
コネクションプーリング DataSource、Tomcat の Resource 設定

次回は EL式とJSTL を学び、JSPのスクリプトレット地獄から解放されましょう!


シリーズ一覧:Servlet/JSP入門

  1. 環境構築とはじめてのServlet
  2. HTTPリクエストとレスポンス
  3. JSPの基礎
  4. フォーム処理(GET/POST)
  5. セッション管理とCookie
  6. MVCパターン(Servlet + JSP)
  7. 👉 JDBC連携(データベース操作)(本記事)
  8. EL式とJSTL
  9. フィルターとリスナー
  10. 総合演習:掲示板アプリを作ろう

著者: @kotaro_ai_lab
AI駆動開発やテック情報を毎日発信しています。フォローお気軽にどうぞ!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?