Spring Bootを使った新人エンジニア向けWebアプリ開発テキスト

第1章:Spring Bootの概要

1.1 Spring Bootとは?

Spring Boot は、Java を使った Web アプリケーション開発を簡単にするフレームワークです。
Spring フレームワークをベースにしており、設定の手間を減らし、シンプルに開発できるのが特徴です。

主な特徴

  • 設定不要で使える:自動設定(Auto Configuration)により、設定ファイルの記述を最小限にできる。
  • 組み込みサーバ:Tomcat などのサーバが最初から組み込まれており、単体で動作可能。
  • 依存関係の管理が簡単:Maven で必要なライブラリを簡単に追加できる。
  • 軽量な開発環境:Spring Boot DevTools を使うと、変更を即時反映できる。

フレームワークとは?

フレームワークとは、ソフトウェア開発を効率化するための基盤となるプログラムの集合体です。共通の機能や構造を提供し、開発者は基本部分を流用しながらアプリケーションを構築できます。代表例にSpringやLaravelなどがあります。

Tomcatとは?

Tomcat(Apache Tomcat)は、Javaで作られたWebアプリケーションを動作させるためのサーブレットコンテナ(Webサーバー機能を持つ実行環境)です。Webブラウザへ動的なページを返す役割を担います。

1.2 依存関係の確認とプロジェクト構成

Spring Boot のプロジェクトでは、Maven を使って必要なライブラリを管理します。
今回のプロジェクトでは、以下の依存関係を使用します。

<dependencies>
    <!-- Webアプリに必要 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Thymeleafテンプレートエンジン -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Spring JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>

    <!-- MySQLドライバ -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

プロジェクト構成

SpringBootSample/
 ├── src/main/java/com/example/demo/
 │    ├── SpringKaeruApplication.java  # 起動クラス
 │    ├── controller/                  # コントローラ層
 │    │    ├── CarController.java
 │    ├── model/                        # データベースアクセス層
 │    │    ├── Car.java
 │    │    ├── CarDao.java
 │    ├── service/                      # ビジネスロジック層(今回はなし)
 ├── src/main/resources/
 │    ├── application.properties        # 設定ファイル
 │    ├── templates/                    # Thymeleafテンプレート
 │    │    ├── kaeru.html
 ├── pom.xml                            # 依存関係を管理するファイル

Mavenとは? 依存関係とは?

Mavenは、Apacheが開発したJava向けのビルド管理ツールです。プロジェクトの依存関係管理やビルド、テスト、デプロイを自動化し、統一されたプロジェクト構成を提供します。XML形式のPOMファイルで設定し、再現性の高い開発環境を実現できます。

依存関係とは、ある要素が他の要素に依存している状態を指します。ソフトウェア開発では、プログラムやモジュールが特定のライブラリやコンポーネントに依存することを意味し、適切な管理が求められます。



1.3 EclipseでのSpring Bootプロジェクト作成

Spring Boot のプロジェクトを作成し、実際に起動してみましょう。

(1) Eclipseでプロジェクトを作成
  1. Eclipse を開き、「ファイル」→「新規」→「Spring Bootプロジェクト」を選択
  2. 「Spring Boot バージョン」は 3.1.1 を選択
  3. 「依存関係の追加」で以下を選択
    • Spring Web
    • Thymeleaf
    • Spring Data JDBC
    • MySQL Driver
  4. 「完了」をクリックしてプロジェクトを作成
(2) プロジェクトの起動

Eclipse の「SpringBootSampleApplication.java」を開き、main メソッドを実行します。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 新入社員のみなさんへ
 * 
 * このクラスは、Spring Bootアプリケーションのエントリーポイント(起動クラス)になります。
 * 
 * 【ポイント】 1. @SpringBootApplication - Spring Bootにおけるアプリケーションの自動設定(Auto
 * Configuration)を有効にするアノテーションです。
 * 
 * 2. mainメソッド - Javaの実行クラスはmainメソッドを持ちます。Spring Bootアプリケーションも同様にここが起点となり、
 * アプリケーションを起動します。 - SpringApplication.run(SpringKaeruApplication.class, args)
 * と呼び出すことで 内部的にTomcatなどのサーバが起動し、Webアプリケーションとして動作を開始します。
 * 
 * 3. アプリケーション名 - クラス名「SpringKaeruApplication」という部分はプロジェクトごとに自由に設定できます。
 * ただし、慣例として「(プロジェクト名)Application」という形式が多く使われます。
 * 
 * 4. 基本の流れ - mainメソッドが呼び出される → SpringApplication.run(...) が実行される →
 * Springフレームワークによる自動設定やコンポーネントの読み込み → アプリケーション起動
 * 

@SpringBootApplication
public class SpringKaeruApplication {

	/**
	 * Javaプログラムの実行を開始するmainメソッドです。
	 * SpringApplication.run(...)によってSpringアプリケーションを起動します。
	 * 
	 * @param args プログラム実行時に渡される引数(今回は特に使用しない想定)
	 */
	public static void main(String[] args) {
		SpringApplication.run(SpringKaeruApplication.class, args);
	}
}


ブラウザで http://localhost:8080 にアクセスすると、アプリケーションが起動していることを確認できます。

http://localhost:8080とは?

http://localhost:8080 は、ローカル環境で動作するWebアプリケーションにアクセスするためのURL です。

  1. localhost
    • 自分のコンピュータ(ローカルホスト)を指します。
    • 外部のネットワークではなく、開発環境でアプリをテストするときによく使います。
  2. 8080(ポート番号)
    • コンピュータ内で特定のアプリケーション(サーバー)を識別するための番号です。
    • Spring Bootでは、デフォルトで 8080 が使われますが、設定で変更できます。

ハンズオン:Spring Bootの初期プロジェクトを作成し、起動してみよう

【目標】

Spring Boot のプロジェクトを作成し、ブラウザで起動を確認する。

【手順】

  1. Eclipse で Spring Boot プロジェクトを作成する。
  2. SpringKaeruApplication.java を実行し、アプリを起動する。
  3. http://localhost:8080 にアクセスし、エラーページが表示されることを確認する。

→ まだコントローラを作成していないため、エラーになるのが正しい動作です!

【確認ポイント】

  • Eclipse で Spring Boot プロジェクトが作成できたか?
  • main メソッドを実行し、Spring Boot アプリが起動したか?
  • http://localhost:8080 にアクセスし、エラーが表示されたか?

まとめ

  1. Spring Boot は Java の Web アプリ開発を簡単にするフレームワーク。
  2. 設定の手間が少なく、最小限のコードで動作する。
  3. Eclipse で Spring Boot プロジェクトを作成し、実際に起動してみる。

次章では、Spring Boot の基本的な仕組みを理解し、簡単な Web ページを表示する方法を学びます!

第2章:Spring Bootの基本


2.1 Spring Bootのアノテーション

Spring Boot では、コードをシンプルにするために アノテーション を多用します。
本章では、Spring Boot の基本的なアノテーションを理解し、簡単な Web ページを表示する方法を学びます。

アノテーションとは?

アノテーションは、クラスやメソッドに付ける特別な記述で、Springに「このクラスはコントローラー」「このメソッドはGETリクエストを処理」などの指示を与えるものです。

(1) @SpringBootApplication

Spring Boot アプリケーションの起動クラスに付けるアノテーションです。
このアノテーションをつけることで、Spring Bootの自動設定 (Auto Configuration) やコンポーネントスキャンが有効になります

@SpringBootApplication
public class SpringKaeruApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringKaeruApplication.class, args);
    }
}

@SpringBootApplication の内部の動作

  • @Configuration: 設定クラスとして認識される
  • @EnableAutoConfiguration: 自動設定を有効にする
  • @ComponentScan: 指定したパッケージ以下のクラスを自動的に登録する

(2) @Controller

コントローラ (Controller) を表すアノテーションです。
このクラスは、ブラウザからのリクエストを受け取り、適切な処理を実行して画面にデータを渡す役割 を持ちます。

// コントローラーとして動作することを示すアノテーション
@Controller
public class HelloController {

    // HTTPのGETリクエストで「/hello」にアクセスしたときに、このメソッドが呼び出される
    @GetMapping("/hello")
    public String hello() {
        // 「hello」というビュー名を返す
        // src/main/resources/templates/hello.html を表示する仕組み
        return "hello";
    }
}

  • @GetMapping("/hello") によって、http://localhost:8080/hello にアクセスすると hello.html を表示する。

(3) @GetMapping

@GetMapping は、HTTPのGETリクエストを受け付けるメソッド を定義するアノテーションです。
例えば、以下のように使います。

@Controller
public class HelloController {

    // HTTP GETリクエストのマッピングを指定するアノテーション
    // "/" へアクセスがあった場合にこのメソッドが呼び出される
    @GetMapping("/") 
    public String index() {
        // "index" という文字列を返すことで、
        // src/main/resources/templates/index.html のテンプレートを表示する
        return "index"; 
    }
}

  • @GetMapping("/") は、ブラウザからの「/」リクエスト (http://localhost:8080/) を処理 します。

2.2 アプリケーションの起動と開発環境の確認

Spring Boot の開発環境が正しく動作しているか確認します。

  1. Eclipse で SpringKaeruApplication.java を実行する
  2. http://localhost:8080/hello にアクセスし、「エラーページ」が表示されることを確認する
    • エラーページになるのが正しい動作!
    • hello.html をまだ作成していないため、テンプレートが見つからないエラーが出る

2.3 application.properties の基本設定

プロパティとは、アプリケーションの設定情報を管理する仕組みです。Spring Boot では、src/main/resources/application.properties に設定を書いて、アプリの挙動を変更できます。

(1) ポート番号の変更

デフォルトでは 8080 ですが、変更したい場合は以下を追加します。

server.port=8081

これで、http://localhost:8081/ にアクセスするとアプリが動作します。

(2) Thymeleaf のキャッシュ無効化

Thymeleaf のテンプレートを編集するたびに、アプリを再起動しなくても変更を反映する設定です。ただし、Javaコードの変更には影響せず、Javaを修正した場合は アプリケーションの再起動が必要になります。開発環境では有効にしておくと効率が上がりますが、本番環境ではパフォーマンスのために true にしておくのが一般的です。

spring.thymeleaf.cache=false

Thymeleafとは?

Thymeleaf(タイムリーフ)は、Javaのテンプレートエンジンの一つで、Spring Bootと組み合わせてよく使われます。HTMLファイルに特殊なタグや属性を埋め込むことで、サーバー側でデータを動的に埋め込んだり、条件分岐や繰り返し処理を行うことができます。

Thymeleaf(タイムリーフ)は、Javaのテンプレートエンジンの一つで、Spring Bootと組み合わせてよく使われます。HTMLファイルに特殊なタグや属性を埋め込むことで、サーバー側でデータを動的に埋め込んだり、条件分岐や繰り返し処理を行うことができます。



2.4 簡単なWebページを表示する

Spring Boot を使って、簡単な Web ページを表示してみます。

(1) コントローラを作成

src/main/java/com/example/demo/controller/HelloController.java を作成し、以下のコードを記述します。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello"; // hello.html を表示
    }
}

Getリクエストの復習

GETリクエストは、クライアント(ブラウザなど)がサーバーに情報を要求する際に使用するHTTPリクエストの一種です。例えば、WebサイトのURLにアクセスすると、ブラウザは自動的にGETリクエストを送信します。GETリクエストは主にデータの取得を目的とし、リクエストパラメータをURLに含めることができます(例:/search?q=java)。また、サーバー側では@GetMappingを使用して特定のURLに対応する処理を実装します。フォーム送信や機密情報の送信には不向きです。



(2) Thymeleaf のテンプレートを作成

src/main/resources/templates/hello.html を作成し、以下の内容を記述します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
    <!--
        xmlns:th="http://www.thymeleaf.org" は、Thymeleafテンプレートエンジンの名前空間を指定しています。
        このあとでThymeleafの `th:*` 属性(例: th:text, th:if など)を使用できるようになることを学びます。
    -->
<head>
    <meta charset="UTF-8">
    <title>Hello Spring Boot</title>
</head>
<body>
    <h1>Hello, Spring Boot!</h1>
</body>
</html>


(3) アプリを起動し、ブラウザで確認

  1. SpringKaeruApplication.java を実行する
  2. http://localhost:8080/hello にアクセス
  3. 「Hello, Spring Boot!」と表示されることを確認する

ハンズオン:シンプルなHello Worldアプリを作成する

【目標】

Spring Boot を使って、「Hello, Spring Boot!」を表示するWebアプリを作成する。

【手順】

  1. HelloController.java を作成
  2. hello.html を作成
  3. アプリを起動し、ブラウザで動作確認
    • http://localhost:8080/hello にアクセス
    • 「Hello, Spring Boot!」が表示されることを確認

まとめ

  1. @SpringBootApplication はSpring Bootの基本設定をまとめたアノテーション
  2. @Controller はWebリクエストを処理するクラス
  3. @GetMapping でURLと処理を紐づける
  4. Thymeleaf を使って簡単なWebページを作成
  5. application.properties でアプリの設定を変更できる

次章では、Thymeleaf の基本を学び、動的なデータを表示する方法を学習します!

第3章:Thymeleafによる画面表示


3.1 Thymeleafとは?

Thymeleaf(タイムリーフ)は、Spring Boot の公式テンプレートエンジンです。
HTMLファイルの中に動的なデータを埋め込むことができ、JavaとHTMLを直感的に組み合わせられるのが特徴です。

Thymeleafの特徴

  • 通常のHTMLとして表示可能(開発中でもブラウザで確認しやすい)
  • Spring Bootと統合が簡単(特別な設定なしで使用できる)
  • シンプルな構文でJavaオブジェクトを埋め込める
  • セキュリティ対策がデフォルトで有効(XSS対策など)

3.2 HTMLテンプレートの作成

Spring Boot のテンプレートは src/main/resources/templates/ に配置します。

まず、以下のようなシンプルなHTMLを作成します。

(1) テンプレートの作成

src/main/resources/templates/welcome.html を作成し、以下のコードを記述します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Welcome</title>
</head>
<body>
    <h1 th:text="${message}">Hello, Thymeleaf!</h1>
</body>
</html>

(2) コントローラの作成

src/main/java/com/example/demo/controller/WelcomeController.java を作成し、以下のコードを記述します。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class WelcomeController {

    @GetMapping("/welcome")
    public String welcome(Model model) {
        model.addAttribute("message", "Spring Bootへようこそ!");
        return "welcome"; // welcome.html を表示
    }
}

Tomcatを起動せずにtemplatesフォルダのhtmlファイルを確認する

Thymeleafを使ったHTMLテンプレートは、通常Spring Bootアプリケーションを起動してサーバー経由で確認しますが、簡易的にHTMLの内容を確認する方法はいくつかあります。

いちばん簡単なのは、EclipseでHTMLファイルを右クリック → 「次で開く」 → 「Webブラウザー」 を選択することです。Thymeleafの特殊な属性(th:text など)は無視されますが、通常のHTMLの構造やCSSの適用を確認する場合には有効です。



3.3 Thymeleafの基本構文

Thymeleaf では、HTML内で th:* という特別な属性を使って動的なデータを埋め込むことができます。

構文説明
th:text<p th:text="${message}">テキストを埋め込む
th:if / th:unless<p th:if="${isLoggedIn}">条件分岐
th:each<li th:each="item : ${items}">ループ処理
th:href<a th:href="@{/home}">Home</a>リンクの生成
th:action<form th:action="@{/submit}">フォームの送信先
th:value<input th:value="${username}">入力値の設定

3.4 ループ処理(一覧表示)

データのリストを表示する場合、th:each を使います。

(1) コントローラでデータを準備

src/main/java/com/example/demo/controller/ProductController.java を作成します。

package com.example.demo.controller;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ProductController {

    @GetMapping("/products")
    public String products(Model model) {
        // 商品リストを作成(固定のデータを用意)
        List<String> productList = Arrays.asList("車A", "車B", "車C");
        
        // Modelオブジェクトを使ってデータをView(HTML)に渡す
        // "products" というキーで productList を Thymeleaf などのテンプレートエンジンに提供
        return "products"; // products.html を表示
    }
}

org.springframework.ui.Modelとは?

org.springframework.ui.Model は、コントローラーからビュー(HTMLなど)にデータを渡すためのインターフェース です。model.addAttribute("キー", 値) を使うことで、テンプレートエンジン(Thymeleaf など)で利用できるデータをセットできます。

例えば、model.addAttribute("message", "こんにちは") とすると、message という変数がビューで使用でき、HTML側で ${message} のように記述すれば表示できます。


(2) Thymeleafでリストを表示

src/main/resources/templates/products.html を作成し、以下のコードを記述します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>商品一覧</title>
</head>
<body>
    <h1>商品一覧</h1>
    <ul>
        <!--
            th:each="product : ${products}" の解説:
            - "products" はコントローラー側でModelに追加されたリスト。
            - "th:each" は Thymeleaf の繰り返し処理を行う属性。
            - "product" という変数に、リスト "products" の各要素が順番に入る。
            - リストの要素数だけ <li> 要素が生成される。
        -->
        <!--
            th:text="${product}" の解説:
            - "${product}" は、th:each でセットされた各要素。
            - <li> 内のテキストがリストの各アイテムに置き換えられる。
            - 例えば、"車A", "車B", "車C" が products に含まれている場合、
              それぞれの <li> 要素に "車A", "車B", "車C" が表示される。
        -->
        <li th:each="product : ${products}" th:text="${product}">商品名</li>
    </ul>
</body>
</html>

ブラウザで http://localhost:8080/products にアクセスすると、以下のように表示されます。

商品一覧
- 車A
- 車B
- 車C



3.5 条件分岐

Thymeleafでは、th:ifth:unless を使って条件分岐ができます。

(1) コントローラの作成

src/main/java/com/example/demo/controller/DiscountController.java を作成します。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class DiscountController {

    @GetMapping("/discount")
    public String discount(Model model) {
        model.addAttribute("isDiscount", true);
        return "discount";
    }
}

(2) Thymeleafで条件分岐

src/main/resources/templates/discount.html を作成し、以下のコードを記述します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>セール情報</title>
</head>
<body>
    <h1>セール情報</h1>
    
    <!--
        th:if は、Thymeleaf の条件分岐を行う属性。
        ${isDiscount} の値が true(または null 以外の値)ならば、この <p> 要素が表示される。
        例えば、Controller で model.addAttribute("isDiscount", true) と設定すると、
        「今だけ特別割引中!」というテキストが表示される。
    -->
    <p th:if="${isDiscount}">今だけ特別割引中!</p>
    
    <!--
        th:unless は th:if の逆の意味を持ち、${isDiscount} の値が false(または null)ならば、この <p> 要素が表示される。
        例えば、Controller で model.addAttribute("isDiscount", false) または値を設定しない場合、
        「現在セールは行っていません。」が表示される。
    -->
    <p th:unless="${isDiscount}">現在セールは行っていません。</p>
</body>
</html>

ブラウザで http://localhost:8080/discount にアクセスすると、以下のように表示されます。

セール情報
今だけ特別割引中!



3.6 ユーザー入力の受け取り

Spring Boot + Thymeleaf では、フォームを使ってユーザー入力を受け取ることもできます。

(1) フォームを表示

src/main/resources/templates/input.html を作成します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー入力</title>
</head>
<body>
    <h1>名前を入力してください</h1>
    <form th:action="@{/greet}" method="get">
        <!--
            th:action は Thymeleaf の属性で、フォームの送信先 URL を指定します。
            "@{/greet}" は Spring Boot のコントローラのエンドポイント "/greet" にマッピングされます。
            例えば、`@GetMapping("/greet")` のようなメソッドが Spring のコントローラに定義されていれば、
            フォームを送信した際に、そのメソッドが処理を行います。
        -->
        <input type="text" name="name">
        <button type="submit">送信</button>
    </form>
</body>
</html>

(2) 入力を処理

src/main/java/com/example/demo/controller/GreetController.java を作成します。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class GreetController {

    @GetMapping("/greet")
    public String greet(@RequestParam String name, Model model) {
        // クライアントからのリクエストパラメータ "name" を受け取る
        // 例えば "/greet?name=Taro" のようなリクエストが来ると、name には "Taro" が格納される
        model.addAttribute("name", name);
        return "greet";
    }
}

(3) 画面に表示

src/main/resources/templates/greet.html を作成します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>挨拶</title>
</head>
<body>
    <h1>こんにちは、<span th:text="${name}">ゲスト</span>さん!</h1>
</body>
</html>


ハンズオン:Thymeleafを使って商品一覧を表示する

【目標】

Thymeleaf を使い、商品のリストを画面に表示する。

【手順】

  1. コントローラ (ProductController.java) を作成
  2. テンプレート (products.html) を作成
  3. ブラウザで http://localhost:8080/products にアクセスし、リストが表示されることを確認

まとめ

  1. Thymeleaf は Spring Boot の公式テンプレートエンジン
  2. th:text でテキストを埋め込める
  3. th:each でリストを表示できる
  4. th:if で条件分岐ができる

次章では、コントローラの役割を深掘りしていきます!

第4章:Spring MVCの基本


4.1 Spring MVCとは?

Spring MVC は、Spring フレームワークの一部であり、Webアプリケーションの開発を簡単にするための仕組み です。
従来の Servlet + JSP に比べ、コードの記述量が少なく、整理されたアーキテクチャで開発できる のが特徴です。

Spring MVCの基本構造

Spring MVC では、以下の3つの要素を中心に構成されます。

  1. コントローラ (Controller)
    • ブラウザからのリクエストを受け取り、適切な処理を行う
    • @Controller を使って定義する
  2. モデル (Model)
    • データを管理する
    • @Service@Repository を使って定義する
  3. ビュー (View)
    • 画面を表示する(Thymeleafなど)
    • src/main/resources/templates/ にHTMLを配置する

4.2 @Controllerと@GetMappingの使い方

コントローラを使ってWebページを制御します。

(1) @Controller の基本

@Controller は、リクエストを受け取るクラスに付けます。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
    @GetMapping("/")  // http://localhost:8080/ にアクセスしたときの処理
    public String index() {
        return "index";  // index.html を表示
    }
}

解説

  • @Controller を付けることで、このクラスがWebリクエストを処理することを宣言。
  • @GetMapping("/") により、http://localhost:8080/ にアクセスすると index.html を表示。

4.3 Modelを使ってデータを画面に渡す

Model を使ってコントローラから画面(HTML)にデータを渡します。

(1) コントローラでデータを渡す

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class GreetingController {

    @GetMapping("/greeting")
    public String greeting(Model model) {
        model.addAttribute("name", "新入社員");
        return "greeting";  // greeting.html を表示
    }
}

(2) Thymeleafでデータを表示

src/main/resources/templates/greeting.html を作成し、以下のコードを記述。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>挨拶</title>
</head>
<body>
    <h1>こんにちは、<span th:text="${name}">ゲスト</span>さん!</h1>
</body>
</html>

結果

  • http://localhost:8080/greeting にアクセスすると、
    「こんにちは、新入社員さん!」 と表示される。

4.4 URLパラメータを受け取る

URLのパラメータを受け取ることも可能です。

(1) @RequestParam を使う

@RequestParam を使うと、URLのパラメータを取得できます。

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class WelcomeController {

    @GetMapping("/welcome")
    public String welcome(
        // クエリパラメータ "name" を受け取るための @RequestParam アノテーション
        @RequestParam(
            name = "name", // クエリパラメータのキーを指定(例: /welcome?name=太郎)
            required = false, // パラメータが省略されてもエラーにならないようにする
            defaultValue = "ゲスト" // パラメータが渡されなかった場合のデフォルト値
        ) String name, 
            Model model // ビューにデータを渡すための Model オブジェクト
    ) {
        model.addAttribute("name", name);
        return "welcome";
    }
}

(2) HTMLで表示

src/main/resources/templates/welcome.html を作成。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Welcome</title>
</head>
<body>
    <h1>ようこそ、<span th:text="${name}">ゲスト</span> さん!</h1>
</body>
</html>

動作確認

  • http://localhost:8080/welcome?name=田中
  • 「ようこそ、田中さん!」 と表示される。

4.5 フォーム入力の受け取り

フォームを使ってデータを入力し、処理することができます。

(1) フォームを作成

src/main/resources/templates/form.html を作成。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>入力フォーム</title>
</head>
<body>
    <h1>名前を入力してください</h1>
    <form th:action="@{/submit}" method="post">
        <!-- th:action="@{/submit}" は、Thymeleaf の構文を使用してフォームの送信先 URL を指定します。 
             @{} の中にパスを書き、サーバー側のエンドポイント(/submit)にデータを送信します。
             例えば、Spring Boot では @PostMapping("/submit") で処理することが一般的です。 -->
        
        <!-- method="post" は、HTTP メソッドの指定です。
             データを送信する際に GET ではなく POST を使用することで、
             URL にデータを含めず、安全に送信できます。 -->
        <input type="text" name="name">
        <button type="submit">送信</button>
    </form>
</body>
</html>

(2) コントローラでフォームのデータを受け取る

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class FormController {

    @PostMapping("/submit")
    public String submit(@RequestParam String name, Model model) {
        model.addAttribute("name", name);
        return "result";
    }
}

(3) 結果を表示

src/main/resources/templates/result.html を作成。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>結果</title>
</head>
<body>
    <h1>こんにちは、<span th:text="${name}">ゲスト</span> さん!</h1>
</body>
</html>

動作確認

  1. http://localhost:8080/form にアクセス
  2. 名前を入力して送信
  3. result.html に入力した名前が表示される

ハンズオン:コントローラを作成してデータを画面に表示する

【目標】

  • コントローラからデータを Thymeleaf に渡して表示する。

【手順】

  1. コントローラ (GreetingController.java) を作成
  2. Thymeleaf (greeting.html) を作成
  3. ブラウザで http://localhost:8080/greeting にアクセス
  4. 「こんにちは、新入社員さん!」と表示されることを確認

まとめ

  • 「Controller → Model → View」 で構成される。
  • @Controller を使うと、リクエストを処理できる。
  • @GetMapping でページを表示する。
  • Model を使ってコントローラからビューにデータを渡せる。
  • @RequestParam を使うと、URLのパラメータを受け取れる。
  • フォームを使うことで、ユーザー入力を受け取れる。

次章では、Spring Bootとデータベースを連携し、MySQLからデータを取得・表示する方法を学びます!

第5章:データベース接続 (JDBC)


5.1 Spring BootとMySQLの接続

Spring Bootでは、JDBCを使ってMySQLと簡単に連携できます。
本章では、Spring BootからMySQLのデータを取得し、画面に表示する方法 を学びます。


(1) MySQLの準備

すでにMySQLでデータベースとテーブルを作成済みですのでcarsテーブルを使っていきます。



(2) application.properties の設定

Spring BootがMySQLと接続できるように、以下の設定を追加します。

src/main/resources/application.properties

# MySQL接続情報
spring.datasource.url=jdbc:mysql://localhost:3306/sip_a?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Tokyo
spring.datasource.username=newuser
spring.datasource.password=0

# JDBCドライバ
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# SQLログを出力
logging.level.org.springframework.jdbc.core=DEBUG


5.2 JDBCテンプレートの使い方

Spring Bootには JdbcTemplate という便利なライブラリがあり、SQLをシンプルに記述できます。


(1) Carエンティティの作成

src/main/java/com/example/demo/model/Car.java を作成し、以下のコードを記述します。

package com.example.demo.model;

import java.io.Serializable;

/**
 * Carエンティティ
 */
public class Car implements Serializable {

	private int carId;
	private String name;
	private int price;
	/**
	 * 論理削除された日時 (nullの場合は未削除)
	 */
	private String deletedAt;

	public Car() {
	}

	public Car(int carId, String name, int price, String deletedAt) {
		this.carId = carId;
		this.name = name;
		this.price = price;
		this.deletedAt = deletedAt;
	}

	public int getCarId() {
		return carId;
	}

	public void setCarId(int carId) {
		this.carId = carId;
	}

	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;
	}

	public String getDeletedAt() {
		return deletedAt;
	}

	public void setDeletedAt(String deletedAt) {
		this.deletedAt = deletedAt;
	}

	@Override
	public String toString() {
		return "Car{" + "carId=" + carId + ", name='" + name + '\'' + ", price=" + price + ", deletedAt='" + deletedAt
				+ '\'' + '}';
	}
}


(2) DAO(データアクセスオブジェクト)を作成

JdbcTemplate を使って、データベースからデータを取得するDAOクラスを作成します。

DAOクラスとは?

DAO(Data Access Object)クラスとは、データベースとのやり取りを担当するクラスのことです。DAOの役割は、データの取得・登録・更新・削除(CRUD操作)を一元的に管理し、ビジネスロジックとデータ処理を分離することにあります。

DAOクラスを用いることで、SQL文の記述やJDBCの操作を専用のクラスにまとめられ、コードの可読性や保守性が向上します。また、データベースの変更があった場合でも、DAOクラスを修正するだけで済み、システム全体への影響を最小限に抑えられます。

一般的なDAOクラスには、データベース接続用のメソッド、SQL実行メソッド、エンティティクラスとのデータ変換処理などが含まれます。

src/main/java/com/example/demo/model/CarDao.java

// @Repository はこのクラスがデータアクセス(DAO)を担当することを示すSpringのアノテーション。
// Springがこのクラスを自動的にBeanとして管理し、例外をSpringのDataAccessExceptionに変換する。
@Repository
public class CarDao {

    // JdbcTemplate はSpringが提供するデータベース操作を簡単に行うためのクラス。
    // SQLの実行や結果のマッピングを簡潔に記述できる。
    private final JdbcTemplate jdbcTemplate;

    // @Autowired を付けることで、Springがこのクラスのコンストラクタに
    // 必要なBean(JdbcTemplate)を自動的に注入(DI)する。
    @Autowired
    public CarDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 全件取得
     */
    public List<Car> getCars() {
        // 実行するSQL文
        String sql = "SELECT * FROM cars";

        // jdbcTemplate.query() はSQLを実行し、結果をオブジェクトのリストに変換するメソッド。
        // 第2引数に RowMapper を指定することで、SQLの結果セットをCarオブジェクトにマッピングできる。
        List<Car> list = jdbcTemplate.query(sql, new RowMapper<Car>() {
            @Override
            public Car mapRow(ResultSet rs, int rowNum) throws SQLException {
                // Carオブジェクトを作成し、ResultSetから値を取り出してセットする。
                Car c = new Car();
                c.setCarId(rs.getInt("car_id")); // car_id カラムの値を取得し、セット
                c.setName(rs.getString("name")); // name カラムの値を取得し、セット
                c.setPrice(rs.getInt("price")); // price カラムの値を取得し、セット
                c.setDeletedAt(rs.getString("deleted_at")); // deleted_at カラムの値を取得し、セット
                return c; // マッピングされたCarオブジェクトを返す
            }
        });
        return list; // 取得したCarオブジェクトのリストを返す
    }
}
//(中略)


5.3 データ取得の基本 (SELECT文)

Spring Bootのコントローラを作成し、データベースから取得したデータを画面に表示してみます。

(1) コントローラの作成

src/main/java/com/example/demo/controller/CarController.java

package com.example.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.example.demo.model.Car;
import com.example.demo.model.CarDao;

@Controller
public class CarController {

    private final CarDao carDao;

    @Autowired
    public CarController(CarDao carDao) {
        this.carDao = carDao;
    }

    @GetMapping("/cars")
    public String listCars(Model model) {
        List<Car> cars = carDao.getCars();
        model.addAttribute("cars", cars);
        return "car_list"; // car_list.html を表示
    }
}


(2) Thymeleafでデータを表示

src/main/resources/templates/car_list.html を作成します。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>車一覧</title>
</head>
<body>
    <h1>車一覧</h1>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>価格</th>
        </tr>
        <tr th:each="car : ${cars}">
            <!-- `th:text` は Thymeleaf の属性で、サーバー側で car.carId の値に置き換えられる -->
            <!-- もし car.carId の値が存在しない場合(このファイルを単体で実行したときなど)、"1" が表示される -->
            <td th:text="${car.carId}">1</td>

            <!-- 同様にcar.name の値がない場合、"車の名前" が表示される -->
            <td th:text="${car.name}">車の名前</td>

            <!-- 同様にcar.price の値がない場合、"2000000"(価格の例)が表示される -->
            <td th:text="${car.price}">2000000</td>
        </tr>
    </table>
</body>
</html>


(3) アプリを実行して確認

  1. SpringKaeruApplication.java を起動する
  2. http://localhost:8080/cars にアクセス
  3. MySQLのデータが画面に表示されていれば成功!

ハンズオン:データベースから車一覧を取得し、画面に表示する

【目標】

Spring Boot を使い、MySQLのデータを画面に表示する。

【手順】

  1. データベース (MySQL) の作成
  2. application.properties にDB接続設定を追加
  3. エンティティ (Customer.java) を作成
  4. DAO (CustomerDao.java) を作成
  5. コントローラ (CustomerController.java) を作成
  6. ビュー (customer_list.html) を作成
  7. ブラウザで http://localhost:8080/customers にアクセスし、データが表示されることを確認

まとめ

  • Spring Boot では JdbcTemplate を使って MySQLと簡単に接続できる
  • DAOパターンを使って、データアクセスを分離する
  • @Repository を使ってデータベースアクセスクラスを定義する
  • @Autowired を使ってコンポーネントを自動的に注入できる
  • Thymeleaf を使ってデータを動的に表示する

次章では、データの追加・更新・削除を行い、より本格的なデータ操作を学びます!

第6章:データの追加・更新・削除


6.1 データの追加(INSERT)

データベースに新しいデータを追加するには、SQLの INSERT 文を使います。
Spring Bootでは、JdbcTemplate を使って簡単にデータを追加できます。


(1) フォームを作成

新しい車を登録するためのフォームを作成します。

src/main/resources/templates/car_form.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>車の追加</title>
</head>
<body>
    <h1>新しい車を追加</h1>
    <form th:action="@{/cars/add}" method="post">
        <label>車の名前:</label>
        <input type="text" name="name" required>
        <br>
        <label>価格:</label>
        <input type="number" name="price" required>
        <br>
        <button type="submit">追加</button>
    </form>
</body>
</html>


(2) コントローラの追加

フォームのデータを受け取って、データベースに登録します。

src/main/java/com/example/demo/controller/CarController.java

package com.example.demo.controller;

import com.example.demo.model.Car;
import com.example.demo.model.CarDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Controller
public class CarController {

    private final CarDao carDao;

    @Autowired
    public CarController(CarDao carDao) {
        this.carDao = carDao;
    }

    @GetMapping("/cars")
    public String listCars(Model model) {
        List<Car> cars = carDao.getCars();
        model.addAttribute("cars", cars);
        return "car_list"; // car_list.html を表示
    }

    @GetMapping("/cars/form")
    public String carForm() {
        return "car_form"; // car_form.html を表示
    }

    @PostMapping("/cars/add")
    public String addCar(@RequestParam String name, @RequestParam int price) {
        carDao.addCar(new Car(0, name, price));
        return "redirect:/cars"; // 登録後に一覧ページへリダイレクト
        // リダイレクトを使用する理由:
        // 1. PRG (Post/Redirect/Get) パターンを適用することで、
        //    フォームの再送信による二重登録を防ぐ。
        // 2. ユーザーが「更新」ボタンを押しても、再度POSTリクエストが送信されることがない。
        // 3. 処理が完了した後、明示的に一覧ページへ遷移させることで、
        //    ユーザーに最新のデータを表示できる。
    }
}


(3) DAOにデータ追加メソッドを作成

JdbcTemplate を使って INSERT 文を実行するメソッドを追加します。

src/main/java/com/example/demo/model/CarDao.java

public int addCar(Car car) {
    // SQL文を定義。`?` はプレースホルダ(後から値を埋め込む)
    String sql = "INSERT INTO cars (name, price) VALUES (?, ?)";

    // updateメソッドの引数
    // 第1引数: 実行するSQL文(プレースホルダを含む)
    // 第2引数: プレースホルダの1つ目の値(carのname)
    // 第3引数: プレースホルダの2つ目の値(carのprice)    
    return jdbcTemplate.update(sql, car.getName(), car.getPrice());
}


(4) 確認

  1. http://localhost:8080/cars/form にアクセス
  2. 車の名前と価格を入力して送信
  3. http://localhost:8080/cars でデータが追加されていることを確認

6.2 データの更新(UPDATE)

データを更新するには、SQLの UPDATE 文を使います。


(1) フォームの作成

編集用のフォームを作成します。

src/main/resources/templates/car_edit.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>車の編集</title>
</head>
<body>
    <h1>車の編集</h1>
    <form th:action="@{/cars/update}" method="post">
        <!-- 
        th:value 属性は、Thymeleaf の式を使用して HTML 要素の value 属性に値を設定するためのものです。
        ここでは、car オブジェクトの carId プロパティの値を hidden フィールドに設定しています。
        ユーザーには表示されませんが、フォーム送信時に carId の値がサーバーへ送信されます。
        -->
        <input type="hidden" name="carId" th:value="${car.carId}">
        <label>車の名前:</label>
        <input type="text" name="name" th:value="${car.name}" required>
        <br>
        <label>価格:</label>
        <input type="number" name="price" th:value="${car.price}" required>
        <br>
        <button type="submit">更新</button>
    </form>
</body>
</html>


(2) コントローラの追加

更新処理のためのコントローラを追加します。

src/main/java/com/example/demo/controller/CarController.java

@GetMapping("/cars/edit")
public String editCar(@RequestParam int id, Model model) {
    Car car = carDao.getCar(id);
    model.addAttribute("car", car);
    return "car_edit";
}

@PostMapping("/cars/update")
public String updateCar(@RequestParam int carId, @RequestParam String name, @RequestParam int price) {
    carDao.updateCar(new Car(carId, name, price));
    return "redirect:/cars";
}


(3) DAOに更新メソッドを追加

src/main/java/com/example/demo/model/CarDao.java

public int updateCar(Car car) {
    String sql = "UPDATE cars SET name=?, price=? WHERE car_id=?";
    return jdbcTemplate.update(sql, car.getName(), car.getPrice(), car.getCarId());
}


(4) 確認

  1. http://localhost:8080/cars で「編集」リンクをクリック
  2. 車の情報を変更して送信
  3. http://localhost:8080/cars でデータが更新されたことを確認

6.3 データの削除(DELETE)

データを削除するには、SQLの DELETE 文を使います。


(1) 削除ボタンを追加

car_list.html に削除ボタンを追加します。

src/main/resources/templates/car_list.html

<tr th:each="car : ${cars}"> <!-- carsリストの各要素をcarとしてループ処理 -->
    <td th:text="${car.carId}"></td> <!-- carのcarIdを表示 -->
    <td th:text="${car.name}"></td> <!-- carのnameを表示 -->
    <td th:text="${car.price}"></td> <!-- carのpriceを表示 -->
    <td>
        <!-- 編集リンク: car.carIdをURLのクエリパラメータとして渡し、編集ページへ遷移 -->
        <a th:href="@{/cars/edit(id=${car.carId})}">編集</a>
        
        <!-- 削除リンク: car.carIdをURLのクエリパラメータとして渡し、削除処理を実行 -->
        <!-- onclick属性でJavaScriptのconfirm関数を使用し、ユーザーに削除の確認を促す -->
        <a th:href="@{/cars/delete(id=${car.carId})}" onclick="return confirm('削除しますか?')">削除</a>
    </td>
</tr>

(2) コントローラの追加

削除処理のためのメソッドを追加します。

src/main/java/com/example/demo/controller/CarController.java

@GetMapping("/cars/delete")
public String deleteCar(@RequestParam int id) {
    carDao.deleteCar(id);
    return "redirect:/cars";
}


(3) DAOに削除メソッドを追加

src/main/java/com/example/demo/model/CarDao.java

public int deleteCar(int carId) {
    String sql = "DELETE FROM cars WHERE car_id=?";
    return jdbcTemplate.update(sql, carId);
}


(4) 確認

  1. http://localhost:8080/cars にアクセス
  2. 「削除」ボタンをクリック
  3. データが削除されることを確認

ハンズオン:新しい顧客を追加・編集・削除できる機能を作成する

【目標】

Spring Boot を使い、MySQLのデータを追加・更新・削除できるようにする。

【手順】

  1. 追加フォーム (customer_form.html) を作成
  2. 更新フォーム (customer_edit.html) を作成
  3. 一覧ページ (customer_list.html) に削除ボタンを追加
  4. DAO (CustomerDao.java) に追加・更新・削除メソッドを追加
  5. コントローラ (CustomerController.java) に追加・更新・削除処理を追加
  6. ブラウザで操作し、動作を確認

まとめ

JdbcTemplate は、Spring Framework の org.springframework.jdbc.core.JdbcTemplate クラスで提供される JDBC 操作のための便利な API です。ここでは、主要なメソッドを用途ごとにまとめます。


1. データの取得(SELECT)

メソッド説明
query(String sql, RowMapper<T> rowMapper)SQL を実行し、RowMapper を使用して結果をオブジェクトに変換する
query(String sql, Object[] args, RowMapper<T> rowMapper)プレースホルダー付き SQL を実行し、結果を RowMapper で処理する
queryForObject(String sql, Class<T> requiredType)単一の結果を取得し、指定した型 (Integer など) で返す
queryForObject(String sql, Object[] args, Class<T> requiredType)プレースホルダー付き SQL で単一の値を取得する
queryForMap(String sql)クエリの結果を Map<String, Object> で取得する(1行のみ)
queryForList(String sql, Class<T> elementType)クエリの結果を List<T> で取得する

2. データの追加・更新・削除(INSERT / UPDATE / DELETE)

メソッド説明
update(String sql)SQL を実行し、変更された行数を返す
update(String sql, Object... args)プレースホルダー付き SQL を実行し、変更された行数を返す

3. データの挿入とキーの取得(INSERT)

メソッド説明
update(PreparedStatementCreator psc, KeyHolder kh)挿入時に自動生成されたキーを KeyHolder で取得する

次章では、リクエストパラメータとフォーム入力を活用し、検索機能を実装します!

JUnitによるテスト

Spring Bootプロジェクトでは、テストのために JUnit を標準で利用することが多いです。
spring-boot-starter-test という依存関係を追加すると、JUnitやMockitoなど、テストに必要なライブラリがまとめて導入されます。

1. 依存関係の追加

pom.xml<dependencies> 内に以下を追記することで、JUnit環境が整います。(初期プロジェクト作成時に「Spring Boot Starter Test」を選ぶと自動的に含まれます。)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>  <!-- テスト時のみ有効 -->
</dependency>

2. テストクラスの配置

Mavenプロジェクトの構造では、src/test/java にテスト用のクラスを作成します。
例えば、CarDaoTest.java というクラスを作り、DAOのメソッドが正しく動作するかを確認するテストを書きます。

SpringBootSample/
 ├── src/main/java/com/example/demo/   # 本番コード
 │    └── model/CarDao.java
 └── src/test/java/com/example/demo/   # テストコード
      └── model/CarDaoTest.java

3. JUnitテストの基本例

以下は、JUnit 5 (Jupiter) を使ったテストの簡単な例です。

package com.example.demo.model;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * CarDaoのメソッドをテストする例
 */
@SpringBootTest
class CarDaoTest {

    @Autowired
    private CarDao carDao;

    @Test
    void testGetCars() {
        // 例:全件取得が0件以上であることを検証
        assertFalse(carDao.getCars().isEmpty(), "車の一覧が空でないこと");
    }

    @Test
    void testAddCar() {
        // 例:新しいCarを追加し、戻り値が1(1行追加)であることを検証
        Car newCar = new Car(0, "TestCar", 999999, null);
        int result = carDao.addCar(newCar);
        assertEquals(1, result, "INSERTが成功した場合は1が返るはず");
    }
}

  • @SpringBootTest
    Spring Bootアプリケーションのコンテキストを読み込み、DI(依存性注入)を有効にしてテストできるアノテーションです。
    @Autowired で DAO や Service を注入できます。
  • @Test
    JUnit 5 (JUnit Jupiter) におけるテストメソッドであることを示すアノテーションです。
    メソッド名は自由ですが、testSomething() という形にすると分かりやすいです。
  • assertXXX(...) 系メソッド
    JUnitで用意されているアサーション(検証)メソッドです。
    • assertFalse(条件, "メッセージ") : 条件がfalseであることを期待
    • assertEquals(期待値, 実際の値, "メッセージ") : 値が等しいことを期待

4. テストの実行

Eclipse などのIDE上で、テストクラス(例: CarDaoTest.java)を右クリックし、「JUnitテストを実行」を選択するとテストが走ります。
また、Mavenを使ってコマンドラインから実行する場合は、以下のように mvn test を実行します。

bashコピーするmvn clean test
  • 成功時: すべてのテストメソッドが問題なく通れば、BUILD SUCCESS と表示されます。
  • 失敗時: どのテストが失敗したか、どのアサーションで引っかかったかが表示されます。

5. JUnitのメリット

  • バグの早期発見:リファクタリングや機能追加時にテストを実行し、既存機能が壊れていないか確認できる。
  • ドキュメントとして機能:テストコードが動作仕様の一部として機能する。
  • 開発効率の向上:テストによって安心感を得られるため、新機能の追加や既存コードの修正がしやすくなる。

6. まとめ

JUnit の assertXXX(...) 系メソッドよく使われるものに限定して まとめた表を作成しました。

メソッド説明使用例
assertEquals(expected, actual)期待値実際の値が等しいことを検証するassertEquals(5, result);
assertNotEquals(unexpected, actual)期待しない値実際の値が異なることを検証するassertNotEquals(0, count);
assertTrue(condition)条件が true であることを検証するassertTrue(result > 0);
assertFalse(condition)条件が false であることを検証するassertFalse(list.isEmpty());
assertNull(actual)オブジェクトが null であることを検証するassertNull(user);
assertNotNull(actual)オブジェクトが null でない ことを検証するassertNotNull(product);
assertSame(expected, actual)同じオブジェクト であることを検証するassertSame(obj1, obj2);
assertNotSame(unexpected, actual)異なるオブジェクト であることを検証するassertNotSame(newObj1, newObj2);
assertArrayEquals(expected, actual)配列の内容が等しい ことを検証するassertArrayEquals(new int[]{1,2,3}, actualArray);
assertThrows(expectedType, executable)例外が発生することを検証するassertThrows(IllegalArgumentException.class, () -> methodCall());

補足

  • assertEqualsassertNotEquals は、数値・文字列・オブジェクトの比較によく使われます。
  • assertTrue / assertFalse は、条件式の検証に便利です。
  • assertNull / assertNotNull は、値が 適切にセットされているかどうか を確認する際に有効です。
  • assertSame / assertNotSame は、参照が同じかどうか をテストします(== の動作)。
  • assertArrayEquals は、配列の中身が同じかどうかを確認するために使います。
  • assertThrows は、例外の発生を確認する ための重要なメソッドで、エラーハンドリングのテストでよく使われます。

JUnit 5 では、これらのメソッドは org.junit.jupiter.api.Assertions に含まれています。
import static org.junit.jupiter.api.Assertions.*; を記述すると、メソッド名だけで呼び出せるようになります。

第7章:リクエストパラメータとフォーム入力


7.1 リクエストパラメータとは?

リクエストパラメータとは、URLやフォームを通じて送信されるデータ のことです。
Spring Bootでは、@RequestParam を使って簡単に取得できます。

例: クエリパラメータ

http://localhost:8080/search?query=トヨタ

このURLでは、query"トヨタ" という値が渡されています。


7.2 @RequestParam でパラメータを取得

@RequestParam を使うと、URLからパラメータを取得できます。

(1) コントローラの作成

src/main/java/com/example/demo/controller/SearchController.java

package com.example.demo.controller;

import com.example.demo.model.Car;
import com.example.demo.model.CarDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Controller
public class SearchController {

    private final CarDao carDao;

    @Autowired
    public SearchController(CarDao carDao) {
        this.carDao = carDao;
    }

    @GetMapping("/search")
    public String search(@RequestParam(name = "query", required = false) String query, Model model) {
        List<Car> cars;
        if (query == null || query.isEmpty()) {
            cars = carDao.getCars();  // 何も入力されていない場合は全件取得
        } else {
            cars = carDao.searchCars(query);
        }
        model.addAttribute("cars", cars);
        return "search_results"; // search_results.html を表示
    }
}

(2) DAOに検索メソッドを追加

LIKE を使った部分一致検索を行います。

src/main/java/com/example/demo/model/CarDao.java

public List<Car> searchCars(String keyword) {
    // SQLクエリを定義: carsテーブルからnameカラムがキーワードを含むレコードを取得
    String sql = "SELECT * FROM cars WHERE name LIKE ?";
    
    // 検索キーワードを部分一致検索用に加工
    String param = "%" + keyword + "%";
    
    // jdbcTemplateを使用してクエリを実行し、結果をCarオブジェクトのリストに変換
    return jdbcTemplate.query(
        sql, 
        new Object[]{param}, // プレースホルダにバインドする値
        (rs, rowNum) -> 
            // ResultSetの各行をCarオブジェクトにマッピング
            new Car(rs.getInt("car_id"), rs.getString("name"), rs.getInt("price"))
    );
}


7.3 Thymeleafで検索フォームを作成

(1) 検索フォーム

src/main/resources/templates/search.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>検索</title>
</head>
<body>
    <h1>車を検索</h1>
    <form th:action="@{/search}" method="get">
        <input type="text" name="query" placeholder="車の名前を入力">
        <button type="submit">検索</button>
    </form>
</body>
</html>

(2) 検索結果の表示

src/main/resources/templates/search_results.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>検索結果</title>
</head>
<body>
    <h1>検索結果</h1>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>価格</th>
        </tr>
        <tr th:each="car : ${cars}">
            <td th:text="${car.carId}"></td>
            <td th:text="${car.name}"></td>
            <td th:text="${car.price}"></td>
        </tr>
    </table>
</body>
</html>


(3) 動作確認

  1. http://localhost:8080/search にアクセス
  2. 検索フォームに「車」と入力して送信
  3. 検索結果が一覧表示される

7.4 並べ替え機能を追加

検索結果を昇順・降順で並べ替えられるようにします。

(1) コントローラの修正

並べ替えのパラメータ (order) を追加します。

@GetMapping("/search")
public String search(
        // クエリパラメータ "query" を受け取る(指定がなくてもOK)
        @RequestParam(name = "query", required = false) String query,
        
        // クエリパラメータ "order" を受け取る(指定がない場合はデフォルト値 "asc")
        @RequestParam(name = "order", required = false, defaultValue = "asc") String order,
        
        // Viewにデータを渡すためのModelオブジェクト
        Model model) {
    
    // "order" パラメータが "desc" の場合は降順、それ以外は昇順
    boolean isAscending = !order.equals("desc");
    
    List<Car> cars; // 検索結果のリストを格納する変数
    
    if (query == null || query.isEmpty()) {
        // クエリが指定されていない場合は、全ての車を価格順でソートして取得
        cars = carDao.getSortedCars("price", isAscending);
    } else {
        // クエリが指定されている場合は、検索結果を指定の順序で取得
        cars = carDao.searchAndSortCars(query, isAscending);
    }
    
    // 取得した車のリストをモデルに追加し、ビューに渡す
    model.addAttribute("cars", cars);
    
    // 入力された検索クエリもモデルに追加(ビューで再表示できるようにするため)
    model.addAttribute("query", query);
    
    // "search_results" テンプレート(HTML)をレンダリングする
    return "search_results";
}

(2) DAOの変更

新しい検索+並べ替えメソッドを追加。

public List<Car> getSortedCars(String sortField, boolean isAscending) {
    // ソート順を決定(昇順: "ASC", 降順: "DESC")
    String sortOrder = isAscending ? "ASC" : "DESC";
    
    // SQLクエリを動的に生成(ソートするカラム名を外部から受け取る)
    // 【注意】文字列連結によるSQLクエリの生成はSQLインジェクションのリスクがある
    String sql = "SELECT * FROM cars ORDER BY " + sortField + " " + sortOrder;
    
    // クエリを実行し、結果をCarオブジェクトのリストにマッピング
    return jdbcTemplate.query(sql, (rs, rowNum) -> 
        new Car(rs.getInt("car_id"), rs.getString("name"), rs.getInt("price"))
    );
}

public List<Car> searchAndSortCars(String keyword, boolean isAscending) {
    // ソート順を決定(昇順: "ASC", 降順: "DESC")
    String sortOrder = isAscending ? "ASC" : "DESC";
    
    // 検索条件(nameに部分一致するデータを取得)
    // SQLクエリの?プレースホルダーを使用することでSQLインジェクションを防ぐ
    String sql = "SELECT * FROM cars WHERE name LIKE ? ORDER BY price " + sortOrder;
    
    // LIKE検索用のパラメータを設定(部分一致検索のために%を付与)
    String param = "%" + keyword + "%";
    
    // クエリを実行し、結果をCarオブジェクトのリストにマッピング
    return jdbcTemplate.query(sql, new Object[]{param}, (rs, rowNum) -> 
        new Car(rs.getInt("car_id"), rs.getString("name"), rs.getInt("price"))
    );
}

(3) 並べ替えボタンの追加

search_results.html に並べ替えボタンを追加

<p>並べ替え:</p>
<form th:action="@{/search}" method="get">
    <input type="hidden" name="query" th:value="${query}">
    <button type="submit" name="order" value="asc">価格の昇順▲</button>
    <button type="submit" name="order" value="desc">価格の降順▼</button>
</form>


(4) 動作確認

  1. http://localhost:8080/search で検索
  2. 「価格の昇順▲」「価格の降順▼」ボタンを押す
  3. 価格順に並べ替えられることを確認

ハンズオン:検索フォームを作成して、検索結果を表示する

【目標】

  • フォームから入力したキーワードで顧客データを検索する

【手順】

  1. 検索フォーム (search.html) を作成
  2. コントローラ (SearchController.java) を作成
  3. DAO (CustomerDao.java) に検索メソッドを追加
  4. 検索結果 (search_results.html) を作成
  5. ブラウザで http://localhost:8080/search にアクセス
  6. 検索と並べ替えが動作することを確認

まとめ

  • @RequestParam を使うと、URLのクエリパラメータを取得できる
  • LIKE を使うと、部分一致検索ができる
  • ORDER BY を使って、昇順・降順の並べ替えができる
  • JdbcTemplate を使って、データベースと連携できる

次章では、セッションと認証の仕組みを学び、ログイン機能を実装します!

第8章:セッションと認証


8.1 セッションとは?

Webアプリケーションやネットワークの分野で「セッション」という言葉は、ある特定の時間内にクライアント(ユーザーのブラウザなど)とサーバーがやり取りを行う一連の通信のことを指します。セッションは、一回の接続やリクエスト単体ではなく、ログイン状態の維持や複数の操作の関連付けを可能にする仕組みとして重要です。

例えば、ECサイトでユーザーがログインし、カートに商品を追加して決済を行うまでの流れは、同じセッション内で処理される必要があります。このため、サーバーは「セッションID」という識別子を発行し、ブラウザのクッキーやURLパラメータなどを使って管理します。

HTTPは本来「ステートレス(状態を保持しない)」なプロトコルのため、通常のリクエストは毎回新しい接続として扱われます。しかし、セッションを利用することで、サーバー側がユーザーごとの状態を一時的に保持し、利便性を向上させることができます。

Spring Boot では、HttpSession を使ってセッション管理 ができます。


8.2 ログイン機能の実装

(1) テーブルの作成

まず、ログインユーザーを管理する login_user テーブルを作成します。

CREATE TABLE login_user (
  id INT AUTO_INCREMENT PRIMARY KEY,
  login_id VARCHAR(45) NOT NULL UNIQUE,
  password VARCHAR(100) NOT NULL
);

INSERT INTO login_user (login_id, password) VALUES
('user1', 'password1'),
('admin', 'adminpass');


(2) ユーザーモデルの作成

src/main/java/com/example/demo/model/User.java

package com.example.demo.model;

import java.io.Serializable;

public class User implements Serializable {
    private int id;
    private String loginId;
    private String password;

    public User() {}

    public User(int id, String loginId, String password) {
        this.id = id;
        this.loginId = loginId;
        this.password = password;
    }

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

    public String getLoginId() { return loginId; }
    public void setLoginId(String loginId) { this.loginId = loginId; }

    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}


(3) DAOの作成

ユーザー情報を取得するDAOを作成します。

src/main/java/com/example/demo/model/UserDao.java

package com.example.demo.model;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;

// @Repository アノテーションを付けることで、このクラスがデータアクセス層 (DAO) のコンポーネントであることを示す。
// Spring によって Bean として管理され、DI (依存性注入) の対象になる。
@Repository
public class UserDao {

    private final JdbcTemplate jdbcTemplate;

    // @Autowired により、Spring の DI コンテナが JdbcTemplate のインスタンスを自動的に注入する。
    // JdbcTemplate はデータベースへの問い合わせを簡潔に記述できるようにする Spring の便利なクラス。
    @Autowired
    public UserDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // 指定された loginId をもとに、login_user テーブルからユーザー情報を取得する。
    public User findByLoginId(String loginId) {
        // プレースホルダー (?) を使って SQL インジェクションを防ぐ。
        String sql = "SELECT * FROM login_user WHERE login_id = ?";
        
        // queryForObject メソッドを使って、1件のデータを取得。
        // 第3引数には、RowMapper を指定し、ResultSet から User オブジェクトを生成する。
        return jdbcTemplate.queryForObject(sql, new Object[]{loginId}, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                // ResultSet からデータを取得し、User インスタンスを生成して返す。
                return new User(rs.getInt("id"), rs.getString("login_id"), rs.getString("password"));
            }
        });
    }
}


(4) ログインフォームの作成

ユーザーがログインするためのフォームを作成します。

src/main/resources/templates/login.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ログイン</title>
</head>
<body>
    <h1>ログイン</h1>
    <form th:action="@{/login}" method="post">
        <label>ログインID:</label>
        <input type="text" name="loginId" required>
        <br>
        <label>パスワード:</label>
        <input type="password" name="password" required>
        <br>
        <button type="submit">ログイン</button>
    </form>

    <p th:if="${error}" style="color:red;">ログインIDまたはパスワードが違います。</p>
</body>
</html>


(5) ログイン処理

セッションを使って、ログイン状態を管理します。

src/main/java/com/example/demo/controller/LoginController.java

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.model.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import jakarta.servlet.http.HttpSession;

/**
 * ユーザーのログイン機能を提供するコントローラークラス。
 * Spring MVC の @Controller アノテーションを使用して、リクエストのハンドリングを行う。
 */
@Controller
public class LoginController {

    private final UserDao userDao;

    /**
     * @Autowired を使用して、UserDao のインスタンスを DI(依存性注入)する。
     * DI により、UserDao のインスタンス管理を Spring に任せることができる。
     */
    @Autowired
    public LoginController(UserDao userDao) {
        this.userDao = userDao;
    }

    /**
     * ログイン画面を表示する。
     * GETリクエスト "login" に対応。
     * @return ログイン画面のビュー名 "login" を返す。
     */
    @GetMapping("/login")
    public String showLoginForm() {
        return "login";
    }

    /**
     * ログイン処理を行う。
     * POSTリクエスト "login" に対応。
     * 
     * @param loginId  ユーザーが入力したログインID
     * @param password ユーザーが入力したパスワード
     * @param session  ログイン情報をセッションに保存するための HttpSession
     * @param model    ログイン失敗時にエラーメッセージを表示するための Model
     * @return ログイン成功時はマイページへリダイレクト、失敗時はログイン画面を再表示。
     */
    @PostMapping("/login")
    public String login(@RequestParam String loginId, @RequestParam String password, HttpSession session, Model model) {
        try {
            // ユーザー情報をデータベースから取得
            User user = userDao.findByLoginId(loginId);
            
            // 取得したユーザー情報があり、かつパスワードが一致する場合
            if (user != null && user.getPassword().equals(password)) {
                // セッションにユーザー情報を保存し、マイページにリダイレクト
                session.setAttribute("loginUser", user);
                return "redirect:/mypage";
            }
        } catch (Exception e) {
            // データベースエラーなどの例外が発生した場合の処理
            // (ここでは特に何もしないが、ログにエラーメッセージを出すとよい)
        }
        
        // ログイン失敗時、エラーフラグを設定し、再びログイン画面を表示
        model.addAttribute("error", true);
        return "login";
    }

    /**
     * ログアウト処理を行う。
     * GETリクエスト "logout" に対応。
     * @param session 現在のセッションを無効化する。
     * @return ログアウト後はログイン画面へリダイレクト。
     */
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate(); // セッションを破棄してログアウト処理を実行
        return "redirect:/login";
    }
}


(6) マイページの作成

ログイン後のページを作成し、セッション情報を表示します。

src/main/resources/templates/mypage.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>マイページ</title>
</head>
<body>
    <h1>マイページ</h1>
    <p>ようこそ、<span th:text="${loginUser.loginId}">ゲスト</span> さん!</p>
    <a href="/logout">ログアウト</a>
</body>
</html>


(7) マイページのコントローラ

src/main/java/com/example/demo/controller/MyPageController.java

package com.example.demo.controller;

import com.example.demo.model.User;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * ユーザーのマイページを表示するコントローラー
 */
@Controller
public class MyPageController {

    /**
     * マイページを表示する処理
     * 
     * @param session HTTPセッション。ログインユーザー情報を取得するために使用。
     * @param model   ビューにデータを渡すためのオブジェクト。
     * @return マイページのテンプレート名。未ログインの場合はログインページへリダイレクト。
     */
    @GetMapping("/mypage")  // "/mypage" にアクセスした際にこのメソッドが実行される
    public String myPage(HttpSession session, Model model) {
        // セッションからログインユーザー情報を取得
        User loginUser = (User) session.getAttribute("loginUser");

        // ログインユーザーが存在しない場合、ログインページへリダイレクト
        if (loginUser == null) {
            return "redirect:/login";
        }

        // ログインユーザー情報をビューに渡す
        model.addAttribute("loginUser", loginUser);

        // "mypage" テンプレートを表示
        return "mypage";
    }
}


8.3 ログイン機能の動作確認

【目標】

ログインフォームからユーザーが認証できるようにする。

【手順】

  1. http://localhost:8080/login にアクセス
  2. ログインIDとパスワードを入力
  3. 「ログイン」ボタンを押す
  4. マイページ (http://localhost:8080/mypage) にリダイレクトされる
  5. 「ログアウト」ボタンを押すと、ログアウトして /login に戻る

まとめ

  1. HttpSession を使って ログイン状態を管理 する
  2. @RequestParamフォームの入力を取得 する
  3. @PostMapping を使って ログイン処理を実装 する
  4. ログイン後の画面 (mypage.html) を作成し、セッション情報を表示

次章では、簡易的な販売管理システムを作成し、アプリケーションの統合を行います!

第9章:簡易的な販売管理システムの作成


9.1 アプリの仕様設計

この章では、Spring Boot を使って簡易的な販売管理システムを作成 します。

システムの機能

ログイン機能(前章で実装済み)
車の一覧表示(第5章で実装済み)
車の購入機能(新規追加)
購入履歴の表示機能(新規追加)


9.2 データベースの設計

(1) sales テーブルの作成

車の購入履歴を管理する sales テーブルを作成します。

CREATE TABLE sales (
    sale_id INT AUTO_INCREMENT PRIMARY KEY,
    car_id INT NOT NULL,
    customer_id INT NOT NULL,
    saleDateTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (car_id) REFERENCES cars(car_id),
    FOREIGN KEY (customer_id) REFERENCES login_user(id)
);

9.3 車の購入機能の実装

(1) DAOの作成

JdbcTemplate を使って、車の購入データを登録します。

src/main/java/com/example/demo/model/SaleDao.java

package com.example.demo.model;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class SaleDao {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public SaleDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public int addSale(int carId, int customerId) {
        String sql = "INSERT INTO sales (car_id, customer_id) VALUES (?, ?)";
        return jdbcTemplate.update(sql, carId, customerId);
    }
}

(2) コントローラの作成

車を購入すると、sales テーブルにデータが追加されるようにします。

src/main/java/com/example/demo/controller/SaleController.java

package com.example.demo.controller;

import com.example.demo.model.SaleDao;
import com.example.demo.model.User;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class SaleController {

    private final SaleDao saleDao;

    @Autowired
    public SaleController(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

    @GetMapping("/purchase")
    public String purchase(@RequestParam int carId, HttpSession session) {
        User loginUser = (User) session.getAttribute("loginUser");

        if (loginUser == null) {
            return "redirect:/login";
        }

        saleDao.addSale(carId, loginUser.getId());
        return "redirect:/mypage";
    }
}

(3) 購入ボタンの追加

購入ボタンを car_list.html に追加します。

src/main/resources/templates/car_list.html

<tr th:each="car : ${cars}">
    <td th:text="${car.carId}"></td>
    <td th:text="${car.name}"></td>
    <td th:text="${car.price}"></td>
    <td>
        <a th:href="@{/purchase(carId=${car.carId})}">購入</a>
    </td>
</tr>

(4) 動作確認

  1. http://localhost:8080/cars にアクセス
  2. 購入ボタンを押す
  3. データベースの sales テーブルにデータが登録されているか確認

9.4 購入履歴の表示

(1) DAOの作成

sales テーブルから購入履歴を取得します。

src/main/java/com/example/demo/model/SaleDao.java

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.jdbc.core.RowMapper;

public List<String> getSalesByUserId(int customerId) {
    String sql = "SELECT c.name FROM sales s INNER JOIN cars c ON s.car_id = c.car_id WHERE s.customer_id = ?";
    return jdbcTemplate.query(sql, new Object[]{customerId}, new RowMapper<String>() {
        @Override
        public String mapRow(ResultSet rs, int rowNum) throws SQLException {
            return rs.getString("name");
        }
    });
}

(2) コントローラの作成

src/main/java/com/example/demo/controller/MyPageController.java

package com.example.demo.controller;

import com.example.demo.model.SaleDao;
import com.example.demo.model.User;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@Controller
public class MyPageController {

    private final SaleDao saleDao;

    @Autowired
    public MyPageController(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

    @GetMapping("/mypage")
    public String myPage(HttpSession session, Model model) {
        User loginUser = (User) session.getAttribute("loginUser");
        if (loginUser == null) {
            return "redirect:/login";
        }

        List<String> sales = saleDao.getSalesByUserId(loginUser.getId());
        model.addAttribute("loginUser", loginUser);
        model.addAttribute("sales", sales);
        return "mypage";
    }
}

(3) マイページの修正

src/main/resources/templates/mypage.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>マイページ</title>
</head>
<body>
    <h1>マイページ</h1>
    <p>ようこそ、<span th:text="${loginUser.loginId}">ゲスト</span> さん!</p>

    <h2>購入履歴</h2>
    <ul>
        <li th:each="sale : ${sales}" th:text="${sale}"></li>
    </ul>

    <a href="/logout">ログアウト</a>
</body>
</html>

(4) 動作確認

  1. http://localhost:8080/cars で車を購入
  2. http://localhost:8080/mypage にアクセス
  3. 購入履歴が表示されることを確認

9.5 ハンズオン:完成した販売管理アプリを動かしてみる

【目標】

  • ログイン → 車の購入 → 購入履歴を確認する の一連の流れを実装する。

【手順】

  1. sales テーブルを作成
  2. DAO (SaleDao.java) を作成
  3. 購入処理 (SaleController.java) を実装
  4. 購入履歴の表示 (MyPageController.java) を実装
  5. ブラウザで購入し、購入履歴が正しく表示されることを確認

まとめ

  • INSERT を使って 購入データを登録
  • INNER JOIN を使って 購入履歴を取得
  • HttpSession を使って ログイン状態を管理
  • Thymeleaf を使って 購入履歴を表示

🚀 おめでとうございます! 簡易的な販売管理システムが完成しました!
次章では、全体の統合とアプリの改善点をまとめます。

第10章:アプリケーションの統合と改善


10.1 アプリケーションの全体構成

これまでの章で実装した機能を統合し、販売管理システムとして完成させます
各機能を整理し、アプリケーションの構成を振り返ります。

✅ 実装した機能

機能概要
ログイン機能ユーザー認証を行い、セッション管理をする
車の一覧表示MySQL の cars テーブルからデータを取得して表示
車の検索機能LIKE を使用して検索
車の並べ替え価格の昇順・降順にソート
車の購入機能購入したデータを sales テーブルに登録
購入履歴の表示ユーザーごとの購入履歴を sales テーブルから取得

🔧 改善ポイント

  • ログインしていないユーザーのアクセス制限
  • セキュリティの強化(パスワードのハッシュ化)
  • UI の整理(メニューの追加)
  • 例外処理の追加(エラーハンドリング)

10.2 ログインしていないユーザーのアクセス制限

現在の状態では、ログインしなくても /cars/mypage にアクセスできます。
これを制限するために、ログインしていない場合は /login にリダイレクトするように修正します。

(1) アクセス制限を追加

各コントローラで、ログイン状態をチェックし、未ログインの場合はリダイレクトします。

src/main/java/com/example/demo/controller/MyPageController.java

@GetMapping("/mypage")
public String myPage(HttpSession session, Model model) {
    User loginUser = (User) session.getAttribute("loginUser");
    if (loginUser == null) {
        return "redirect:/login";
    }
    List<String> sales = saleDao.getSalesByUserId(loginUser.getId());
    model.addAttribute("loginUser", loginUser);
    model.addAttribute("sales", sales);
    return "mypage";
}

src/main/java/com/example/demo/controller/CarController.java

@GetMapping("/cars")
public String listCars(HttpSession session, Model model) {
    if (session.getAttribute("loginUser") == null) {
        return "redirect:/login";
    }
    List<Car> cars = carDao.getCars();
    model.addAttribute("cars", cars);
    return "car_list";
}


10.3 セキュリティの強化(パスワードのハッシュ化)

現在はプレーンテキストのパスワードをデータベースに保存していますが、
セキュリティの観点から、ハッシュ化 して保存するように変更します。

(1) パスワードのハッシュ化

Spring Security の BCryptPasswordEncoder を使ってパスワードを暗号化します。

(a) 依存関係を追加

pom.xml に以下の依存関係を追加。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

(b) パスワードをハッシュ化

UserDao にパスワードをハッシュ化して保存する処理を追加します。

src/main/java/com/example/demo/model/UserDao.java

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public int registerUser(String loginId, String password) {
    // ユーザー情報をデータベースに登録するためのSQL文
    String sql = "INSERT INTO login_user (login_id, password) VALUES (?, ?)";
    
    // BCryptPasswordEncoder を使用してパスワードをハッシュ化
    // ハッシュ化することで、データベースに生のパスワードを保存せずに済み、セキュリティを強化できる
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    String hashedPassword = encoder.encode(password); // パスワードをハッシュ化
    
    // SQL を実行してユーザー情報をデータベースに保存
    // loginId はそのまま、password はハッシュ化したものを保存する
    return jdbcTemplate.update(sql, loginId, hashedPassword);
}

(c) ログイン時にハッシュ化されたパスワードを比較

LoginController.java

@PostMapping("/login") // HTTP POSTリクエストを受け付けるエンドポイント(ログイン処理)
public String login(
        @RequestParam String loginId,  // リクエストパラメータから loginId を取得
        @RequestParam String password, // リクエストパラメータから password を取得
        HttpSession session,           // セッション管理のための HttpSession オブジェクト
        Model model) {                 // View にデータを渡すための Model オブジェクト
    try {
        // ユーザー情報をDBから検索
        User user = userDao.findByLoginId(loginId);
        
        // パスワードのハッシュ化・検証のための BCryptPasswordEncoder を作成
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        
        // ユーザーが存在し、パスワードが一致する場合
        if (user != null && encoder.matches(password, user.getPassword())) {
            // セッションにログインユーザー情報を保存
            session.setAttribute("loginUser", user);
            
            // マイページへリダイレクト
            return "redirect:/mypage";
        }
    } catch (Exception e) {
        // 例外処理:通常はログを出力するべき(ここでは省略)
        // 例外が発生してもログイン失敗として処理を継続
    }
    
    // ログイン失敗時にエラーフラグを設定し、ログイン画面に戻す
    model.addAttribute("error", true);
    return "login";
}


10.4 メニューの追加

アプリのナビゲーションを改善するため、メニューを追加 します。

(1) メニューを作成

src/main/resources/templates/menu.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>メニュー</title>
</head>
<body>
    <h1>メニュー</h1>
    <ul>
        <li><a href="/mypage">マイページ</a></li>
        <li><a href="/cars">車一覧</a></li>
        <li><a href="/search">車の検索</a></li>
        <li><a href="/logout">ログアウト</a></li>
    </ul>
</body>
</html>

(2) 各ページにメニューを組み込み

各ページの先頭に以下のコードを追加します。

<div th:replace="menu :: *"></div>


10.5 例外処理の追加

データベース接続エラーや存在しないデータを取得したときのために、エラーハンドリングを追加します。

(1) グローバルエラーハンドリング

Spring Boot の @ControllerAdvice を使い、アプリ全体のエラーをキャッチします。このコードは決まり文句なので詳細な解説は割愛します。

src/main/java/com/example/demo/exception/GlobalExceptionHandler.java

package com.example.demo.exception;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public String handleException(Exception e, Model model) {
        model.addAttribute("errorMessage", "エラーが発生しました: " + e.getMessage());
        return "error";
    }
}

(2) エラーページの作成

src/main/resources/templates/error.html

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>エラー</title>
</head>
<body>
    <h1>エラーが発生しました</h1>
    <p th:text="${errorMessage}"></p>
    <a href="/">トップページへ戻る</a>
</body>
</html>


10.6 動作確認

【目標】

全体の動作を統合し、本番環境に近い形でアプリケーションを動作させる

【手順】

  1. ログインしていない状態で /cars にアクセスし、リダイレクトされるか確認
  2. パスワードのハッシュ化が適用されているか確認
  3. メニューが正しく表示され、ナビゲーションできるか確認
  4. エラー発生時に error.html が表示されるか確認

まとめ

  • ログインしていないユーザーを /login にリダイレクト
  • パスワードを BCryptPasswordEncoder でハッシュ化
  • メニューを追加して UI を改善
  • @ControllerAdvice を使い、エラー処理を統一

おめでとうございます! 本書のゴールである Spring Boot を使ったWebアプリケーションの完成 です!
当社の新人研修では、このアプリをベースに、機能を拡張していくことで、さらに実践的なスキルを磨きましょう!

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。