【超初心者向け】Spring Boot超入門（WEBアプリ編）

# はじめに
こんにちは。
今回の超入門シリーズはSpring Bootです。
Spring Bootって、フレームワークとしては幅広く使われていますが、Spring Boot自体がよくわからなかったり、文献が断片的だったりして、中々まとまっているものがなく、この記事を執筆するに至りました。
Spring Bootが多機能なので、サラっとまとめられないのはわかるんですけどね。
Spring BootはWEBアプリからREST-API、バッチ機能まで幅広く利用できるフレームワークですが、今回はタイトルの通り、WEBアプリケーション向けの使い方にフォーカスします。

# やりたいこと
前述しましたが、Spring Bootを使ったWEBアプリケーションでも、断片的なサンプルが掲載されている文献はたくさんあります。
ただ、当記事では、断片的な情報を**まとめる**ことが目的ですので、業務アプリケーションの構築上、大体必須になる要素をいくつかピックアップして、それらを合体したサンプルアプリケーションを交えて紹介します。

■当記事で実現すること
* 基本となるアプリケーションの構築方法（これは当たり前ですね）
* Thymeleaf少し複雑に使う
  * スタイルシート（CSS）を適用する
  * 制御文を使ってみる
* DB連携
* 画面を戻る処理を実装する

超入門にしてはちょっと難しい内容になりますが、欲しいところだけ選んで実装できるように紹介しますので、頑張って一緒に勉強しましょう。

# 開発環境一覧
今回使用した開発環境ほか、製品などの一覧です。
表に載せた以外に、細かい依存性などはありますが、pom.xmlを後で掲載しますので、詳しくはそちらをご覧ください。

|種別|製品名|バージョン|特記事項|
|-|-|-|-|
|実行環境|Open JDK|Java SE 21|Eclipse付属のJRE|
|統合開発環境|Pleiades All in one Eclipse|2024-06||
|DBMS|ORACLE|19c|Oracle社が配布してくれている構築済みVMを使いました。<br>Oracle様、ありがとうございます。|
|フレームワーク|Spring Boot|3.3.3|当記事の本丸|
|SQLクライアント|MyBati |3.5.16||
|ビルドツール|Maven|3.9.9||

# 環境構築
それでは、さっそく開発環境を構築していきましょう。
今回はSTS（Spring Tool Suite）とMavenを使用します。
ツールを使わず、自力で構築できればそれがベストなのですが、難しすぎるので今回はツールの力を借りましょう。
他の文献も基本的にはツールを使って構築するものが多いので、おそらくはツールを使うのがデファクトスタンダードなのでしょう。
ビルドツールにはGradleが人気のようですが、今回はMavenで行きます。
筆者の私がGradleの使い方をよく知らないのもありますが、「MavenとかGradleとかよくわからん」という人にも、[Maven超入門](https://qiita.com/tarosa0001/items/e5667cfa857529900216 "Maven超入門")という、初学者向けの記事の用意があったので、Mavenで行くことにしました。

## Eclipseのインストール
こちらは割愛します。

## Mavenのインストールと設定
続いて、Mavenをインストール、設定します。
Mavenについては[こちら](https://qiita.com/tarosa0001/items/e5667cfa857529900216 "Maven超入門")をご覧いただき、設定してください。

## DBMSの準備
当記事ではORACLEを使いますが、使うDBMSはご自由に選定してください。
ORACLEを使用される際は、[こちら](https://qiita.com/tarosa0001/items/8f1e29fcb287faf00a3c "Oracle学習環境メモ")にインストール済みVMを紹介していますので、お役立てください。

## プロジェクトの作成
必要製品のインストール、設定が終わったところで、プロジェクトを作成します。
プロジェクトの作成にはSTSと言うツールを使いますが、前述したEclipseのUltimateであれば、初めから入っているので、そのまま始められます。
さっそく始めましょう。

ファイル＞新規＞Springスターター・プロジェクトを選びます。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/61699da8-fd92-2e5a-3db3-e7a5b00e6eba.png)

ポップアップにプロジェクト名などを入力して、次へ行きます。
タイプは初期値がGradleになっているので、Mavenを選択します。
記事とはずれますが、Gradleに詳しい方は、そのまま次へ行っても問題ありません。
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/c543b2b3-1b1a-abfa-d72d-e069c4c2bbbd.png)

使用するライブラリ類にチェックを入れます。
後から自力でビルド設定に追加することもできますが、ここでチェックを入れておけばpom.xmlなどビルド設定に依存関係を自動で入れてくれます。
当記事のサンプルで使用するライブラリをチェックした状態がこちらです。
※正確には、Spring Data JDBCは使わないので、チェックは不要です。

余談ですが、このポップアップでは、「Spring Web」となっていますが、「Spring MVC」というものもあるようで、Webと何が違うかは筆者もよくわかっていません。
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/2df3eb90-4510-5bc2-cac7-a74998cdeee1.png)

使用するJDBCドライバまでチェックを付けたら完了します。
![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/3f452b26-2165-c9f7-eded-17ffaf9321c7.png)

完了すると、Eclipse上でビルドが走りますが、エラーになることがあります。
エラーになったときは、コマンドプロンプトからビルドすれば、依存関係が解決してエラーを消すことができます。
![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/17a9727c-e387-c7ac-64ec-7f58e35a7a3d.png)
![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/c99a0322-677b-006a-4b8e-008fa7026dfc.png)

コマンドでビルドした後、プロジェクトを右クリック＞Maven＞プロジェクトの更新を選ぶか、`Alt + F5`でリフレッシュすると、エラーが消えます。
![007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/ca89cb4e-5a89-a574-0e53-371dff312c75.png)
![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/723e542a-2f8b-65c1-6508-6fbd3ad7f83e.png)

エラーが消えれば、プロジェクトの作成は完了です。

# サンプルアプリケーションの実装
それでは、ここからサンプルを実装していきます。

## アプリケーションツリー
まずは、サンプルアプリケーション全体のツリーを紹介します。
厳密にはtest用フォルダなどもあるのですが、サンプルでは使用しないので割愛しています。
```SHELL:ツリー
プロジェクトルート
└─src
    └─main
        ├─java
        │  └─com
        │      └─example
        │          └─demo
        │              │  SpringBootSampleApplication.java
        │              │
        │              └─t
        │                  ├─controller
        │                  │      FinishController.java
        │                  │      HomeController.java
        │                  │
        │                  ├─entity
        │                  │      UserTable.java
        │                  │
        │                  ├─mapper
        │                  │      UserMapper.java
        │                  │
        │                  └─model
        │                          Address.java
        │                          User.java
        │                          ViewCommonData.java
        │
        └─resources
            │  application.yml
            │
            ├─com
            │  └─example
            │      └─demo
            │          └─t
            │              └─mapper
            │                      UserMapper.xml
            │
            ├─static
            │  └─css
            │          SpringBootSample.css
            │
            └─templates
                │  confirm.html
                │  finish.html
                │  form.html
                │
                └─folder
                        irregular.html

```

## テーブルの作成
今回のアプリケーションで使用するテーブルのDDLを載せておきます。
DDLはORACLE用ですので、ご利用のDBMSに合わせて調整してください。
```SQL:USER_TABLE.sql
CREATE TABLE USER_TABLE
(
    UNO        NUMBER
  , UNAME      VARCHAR2(24)
  , AGE        NUMBER
  , BDATE      DATE
);
```

## アプリケーションの全体的な設定
実装の初めにアプリケーションの全体的な設定を行います。
Spring Bootアプリケーションでは、起動時の設定などは「application.properties」または「application.yml」に書いていきます。
propertiesと、ymlという形式が選べます。
ymlとは、propertiesを「:」を使用した階層形式で表現したもので、propertiesを見やすくした形式とお考え下さい。
Eclipseでプロジェクトを作成すると、デフォルトではproperties形式でファイルができますので、yml形式を使う場合は、拡張子を「yml」にリネームします。
ただ、Eclipseを使用すると、Tabで自動的にインデントが入るのですが、ymlでは半角空白でインデントしないとエラーになりますので、インデントは手作業で直します。
こちらが今回の設定の内容です。
```yml:application.yml
spring:
  application:
    name: SpringBootSample

  datasource:
    url: jdbc:oracle:thin:@localhost:1521/ORCL
    username: USER01
    password: ORACLE

mybatis:
  configuration:
    map-underscore-to-camel-case: true

# ORACLEのVMがデフォルトの8080を使っているので変更
server:
  port: 8083
```
設定の内容はこちらです。
* アプリケーション名
* 使用するDBの接続定義
* MyBatis固有の設定
スネークケースとキャメルケースのマップを有効化しています。
* WEBアプリで待ち受けるポート番号
筆者の環境では、ORACLEのゲストOSが8080を使用していて競合するので、変更しています。
この設定をしないとデフォルトの8080を使いますが、競合するなどの事情がなければ、この設定は不要です。

これらをproperties形式で書くとこうなります。
```properties:application.properties
spring.application.name=SpringBootSample

datasource.url=jdbc:oracle:thin:@localhost:1521/ORCL
datasource.username=USER01
datasource.password=ORACLE

mybatis.configuration.map-underscore-to-camel-case=true

# ORACLEのVMがデフォルトの8080を使っているので変更
server.port=8083
```
yml形式だと、同じ内容を何度も書かなくて済みますので、すっきりしますね。
設定内容がこれだけだとあまり効果が実感できないのですが、内容が増えて複雑になってくると、結構違いがでます。
と言っても、ここは好き好きなので、どちらを使っても問題ありません。

## アプリケーションの実装
では、ここからはアプリケーション本体を実装していきます。

### Main処理
まずはアプリケーションのMain処理ですが、今回は特に何もしません。
Main処理はプロジェクトの作成時に自動生成されていますので、そのまま使います。
**起動前**に何かしらの処理をやりたい場合は、ここでやりましょう。
```java:SpringBootSampleApplication
package com.example.demo;

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

/**
 * <h1>[Main]</h1><br>
 *<br>
 * Main処理を持つクラス。<br>
 * アプリケーションを起動する。
 */
@SpringBootApplication
public class SpringBootSampleApplication {
	public static void main(String[] args) {
		SpringApplication.run(SpringBootSampleApplication.class, args);
	}
}
```

### Modelの実装
ここからは、業務処理で参照するデータの入れ物などの準備をします。
実装していく順番上、先にオブジェクトを定義しておかないとエラーになるというだけで、別にいつ作ってもいいです。
まずはModelを実装します。
Modelとは、画面上で入力したフォームの内容を保持するためのオブジェクトです。
POJOとして実装しますが、今回は、クラスを入れ子にしたり、共通部品を持たせたりしたいので、3つ作ります。
1. ViewCommonData
全画面共通データを持つ。
1. User
ユーザー情報入力で受け付けたデータを持つ。
1. Address
住所を持つ。
住所情報だけを外出し、かつ、Modelのフィールドを入れ子にするサンプルにするのに、わざと別クラスにしました。

それでは実装です。
```java:ViewCommonData.java
package com.example.demo.t.model;

import lombok.Data;

/**
 * <h1>[共通データ]</h1><br>
 *<br>
 * 各画面で共通で使用するデータを保持する。<br>
 * 画面で使用するモデルは必ずこのクラスを継承する。<br>
 * コントローラーで使用するModelは、ModelAttributeを使用すると、そのクラスに保持しているデータ以外を受け渡せなくなる。<br>
 * 例えば、エラーメッセージを出し分けたい場合、変数を使うが、エラーメッセージをユーザー情報などに直接持たせるのはダサいので、こういう方法で対処する。<br>
 * 他にかっこいいやり方があれば、是非コメントください。
 */
@Data
public class ViewCommonData {
	/** エラーメッセージ */
	protected String errMsg;
}
```
```java:User.java
package com.example.demo.t.model;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <h1>[ユーザー情報]</h1><br>
 *<br>
 * USERの情報を持つ。<br>
 * LombokのアノテーションでGetとSetは自動生成する。
 */
@Data
@EqualsAndHashCode(callSuper=true)
public class User extends ViewCommonData{
	/** 個人番号 */
	private long uNo;

	/** 氏名 */
	private String uName;

	/** 年齢 */
	private long age;

	/** 誕生日 */
	private String bDate;

	/** 住所 */
	private Address address = new Address();
}
```
```java:Address.java
package com.example.demo.t.model;

import java.util.ArrayList;
import java.util.List;

import lombok.Data;

/**
 * <h1>[住所]</h1><br>
 *<br>
 * 住所を持つ。
 */
@Data
public class Address {
	/** 都道府県 */
	private String region;

	/** 市区町村 */
	private String city;

	/** 市区町村以下 */
	private List<String> addressLine = new ArrayList<>();
}
```

### Entityの実装
MyBatis連携で使用するEntityを実装します。
```java:UserTable
package com.example.demo.t.entity;

import java.sql.Date;

import lombok.Data;

/**
 * <h1>[ユーザーテーブルのエンティティ]</h1><br>
 *<br>
 * USER_TABLEの情報を持つ。
 */
@Data
public class UserTable {
	/** 番号 */
	private long uNo;

	/** 氏名 */
	private String uName;

	/** 年齢 */
	private long age;

	/** 誕生日 */
	private Date bDate;
}
```

### Mapperの実装
MyBatisのSQLを実行するためのMapperと呼ばれるインターフェースを実装します。
具象クラスはMyBatisが良きに生成しますので、実装として用意するのはインターフェースのみです。
```java:package com.example.demo.t.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.example.demo.t.entity.UserTable;

/**
 * <h1>[ユーザーテーブルのMapper]</h1><br>
 *<br>
 * ユーザーテーブルのSQLをマップする。
 */
@Mapper
public interface UserMapper {
	/** 番号でのSELECT */
	public List<UserTable> selectByUno(long uNo);

	/** INSERT */
	public int insert(UserTable ut);
}
```

### 画面の実装
お待たせしました。
準備が終わったので、ここから画面や業務処理を実装していきます。
実装にはSpring Bootの標準？のテンプレートエンジンThymeleaf（タイムリーフ）を使用します。
今回は4画面実装します。
1. 入力画面
1. 入力内容確認画面
1. 完了画面
1. エラー画面

ではさっそく実装していきましょう。
まずは入力画面です。
実装する画面のファイルは、templatesフォルダに配置します。
```html:form.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
	<meta charset="UTF-8">
	<!-- スタイルシートを読み込む -->
	<link th:href="@{/css/SpringBootSample.css}" rel="stylesheet" type="text/css">
	<title>ユーザー情報入力</title>
</head>

<body>
	<div id="container">
		<h1 id="page-hedder">SpringBoot + MVC + MyBatisのサンプルアプリケーション</h1>
		少しだけ凝ったSpringMVCのサンプルアプリケーションです。<br>
		頑張って勉強しましょう。
		<h2>情報入力</h2>
		下の表に、次のページに引き継ぐ情報を入力します。
		<form th:action="@{/form}" th:object="${user}" method="post">
			<table>
				<tr>
					<th>入力項目</th>
					<th colspan="2">入力値</th>
				</tr>
				<tr>
					<td>個人番号</td>
					<td colspan="2"><input type="number" th:field="*{uNo}"></td>
				</tr>
				<tr>
					<td>氏名</td>
					<td colspan="2"><input type="text" th:field="*{uName}"></td>
				</tr>
				<tr>
					<td>誕生日</td>
					<td colspan="2"><input type="text" th:field="*{bDate}"></td>
				</tr>
				<tr>
					<td>年齢</td>
					<td colspan="2"><input type="number" th:field="*{age}"></td>
				</tr>
				<tr>
					<th rowspan="5">住所</th>
					<td>都道府県</td>
					<!-- データがクラスの入れ子になってい場合は変数名の後に「.」でつなげる -->
					<td><input type="text" th:field="*{address.region}"></td>
				</tr>
				<tr>
					<td>市区町村</td>
					<td><input type="text" th:field="*{address.city}"></td>
				</tr>
				<tr>
					<td>住所1</td>
					<!-- コレクションの場合は変数名を指定するだけで勝手にaddしてくれる -->
					<td><input type="text" th:field="*{address.addressLine}"></td>
				</tr>
				<tr>
					<td>住所2</td>
					<td><input type="text" th:field="*{address.addressLine}"></td>
				</tr>
				<tr>
					<td>住所3</td>
					<!--
						インデックスを指定しても入れることはできる。
						確認画面から戻ってきてた時、インデックスを指定していると、コレクション内の要素を狙い打って表示できる。
						インデックス指定抜きでうまいこと表示するやり方は不明。
						心あたりのあるかは是非コメントください。
					-->
					<td><input type="text" th:field="*{address.addressLine[2]}"></td>
				</tr>

				<button style="float:right">送信</button>
			</table>
		</form>
	</div>
</body>

</html>
```

続いて確認画面です。
内容を修正するケースを想定して、入力画面に戻るボタンを実装しています。
詳しい業務処理は後述しますが、確認画面では戻る処理に加えて、1つのフォームに複数のsubmitを実装しています。
```html:confirm.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">>
<head>
	<meta charset="UTF-8">
	<link th:href="@{/css/SpringBootSample.css}" rel="stylesheet" type="text/css">
	<title>情報引き継ぎ画面</title>
</head>

<body>
	<div id="container">
		<h1 id="page-hedder">情報引き継ぎ画面</h1>
		<form th:action="@{/commit}" th:object="${user}" method="post">
			<h2>普通に処理する</h2>
			入力画面から引き継いだ情報を普通に表示する。<br>
			<span th:inline="text">
				<!--
					formで受け取った値は表示するには使えるが、inputに指定しない値は次のsubmitには引き継げない。
				-->
				個人番号：[[${user.uNo}]]<br>
				氏名：[[${user.uName}]]<br>
				誕生日：[[${user.bDate}]]<br>
				年齢：[[${user.age}]]<br>
				住所：[[${user.address.region}]][[${user.address.city}]][[${user.address.addressLine[0]}]][[${user.address.addressLine[1]}]][[${user.address.addressLine[2]}]]
			</span>

			<h2>住所をループ処理</h2>
			住所1～3をループで処理してみる。

			<h3>HTMLのタグに埋め込んど表示する場合</h3>
			<th:block th:each="address : ${user.address.addressLine}">
				<label th:text="${address}" />
			</th:block>

			<h3>生の値をそのまま出力する場合</h3>
			<th:block th:each="address : ${user.address.addressLine}">
				[[${address}]]
			</th:block>
			<br>

			<!-- 入力画面に戻るときのためにhiddenでも持たせておく -->
			<input type="hidden" th:field="*{uNo}" value="${user.uNo}" />
			<input type="hidden" th:field="*{uName}" value="${user.uName}" />
			<input type="hidden" th:field="*{bDate}" value="${user.bDate}" />
			<input type="hidden" th:field="*{age}" value="${user.age}" />
			<input type="hidden" th:field="*{address.region}" value="${user.address.region}" />
			<input type="hidden" th:field="*{address.city}" value="${user.address.city}" />
			<!-- Listの場合は一つで持てる -->
			<input type="hidden" th:field="*{address.addressLine}" value="${user.address.addressLine}" />

			<!--
				一つのフォームに2種類のsubmitを用意する。
				フォームに2つ以上のsubmitで動きを分けたいときは、気にせず2つ用意する。
				押されたsubmitのnameがパラメータとしてコントローラーにパラメータとして渡され、コントローラーの呼び出し時にnameのパラメータにより、呼び出す処理を振り分けることができる。
			-->
			<input type="submit" name="back" style="float:left" value="入力画面に戻る">
			<input type="submit" name="commit" style="float:right" value="完了">
		</form>
	</div>
</body>
</html>
```

続いて完了画面です。
```html:finish.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<link th:href="@{/css/SpringBootSample.css}" rel="stylesheet" type="text/css">
	<title>完了</title>
</head>
<body>
	<div id="container">
		<h1 id="page-hedder">完了画面</h1>
		データの登録が完了しました。

		<!-- 最後の出力 -->
		<h2>登録データ</h2>
		登録したデータはこちら<br>
		個人番号：[[${userTable.uNo}]]<br>
		氏名：[[${userTable.uName}]]<br>
		年齢：[[${userTable.age}]]<br>
		誕生日：[[${userTable.bDate}]]<br>
	</div>
</body>
</html>
```

最後にエラー画面です。
エラー画面では、共通データに持たせたメッセージと、画面のスコープのModelというオブジェクトに持たせたメッセージを2種類表示します。
```html:irregular.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<link th:href="@{/css/SpringBootSample.css}" rel="stylesheet" type="text/css">
	<title>エラー</title>
</head>

<body>
	<div id="container">
		<h1 id="page-hedder">エラー画面</h1>
		<h2>エラー内容</h2>
		[[${user.errMsg}]]<br>
		後付けのメッセージは「[[${message}]]」
		<form th:action="@{/form}" th:object="${user}" method="get">
			<input type="hidden" th:value="*{uNo}" />
			<input type="hidden" th:value="*{uName}" />

			<button style="float:right">入力画面に戻る</button>
		</form>
	</div>
</body>
</html>
```

### 使用したスタイルシート
スタイルシートはこちらです。
スタイルシートなどの静的なコンテンツは、staticフォルダに配置しましょう。
ソースコード上説明していませんでしたが、読み込むスタイルシートは、staticをルートとして、そこからのパスで読み込みます。
余談ですが、最近は、フォントは游ゴシック、デスクトップアプリケーション向けにはメインのコンテンツ幅は1100pxが流行りのようです。
```css:SpringBootSample.css
@charset "UTF-8";

body {
	font-family: "游ゴシック体", YuGothic, "游ゴシック Medium", "Yu Gothic Medium", "游ゴシック", "Yu Gothic", sans-serif;
}

div#container {
	width  : 1100px;
	margin : 0 auto;
}

h1#page-hedder {
	width            : 100%;
	background-color : #4472C4;
	color            : white;
	padding-left     : 5px;
}

h2 {
	width         : 100%;
	border-left   : 3px solid  #4472C4;
	border-bottom : 1px   solid  #4472C4;
	color         : #4472C4;
	padding-left  : 5px;
}

h3 {
	width         : 100%;
	border-bottom : 1px solid #4472C4;
	color         : #4472C4;
	padding-left  : 5px;
}

table {
	border-collapse : collapse;
	border          : 1px   solid  #4472C4;
}

table th {
	background-color : #4472C4;
	border-bottom    : 3px double #4472C4;
	padding-left     : 5px;
	padding-right    : 5px;
}

table td {
	border        : 1px solid #4472C4;
	padding-left  : 5px;
	padding-right : 5px;
}
```

### 入り口から呼び出すコントローラーの実装
ここからは業務処理です。
業務処理の初めには、アプリケーションの入り口となる画面に紐づく処理を実装します。
Spring BootのWEBアプリケーションでは、画面からのリクエストを受け付けて実行する業務処理を、**コントローラー**と呼びます。
Javaで実装したクラスをコントローラーとして認識させるには、`@Controller`というアノテーションをクラスに付与します。
これ以外にも、Spring BootアプリケーションではSpringへの設定は基本的に不要で、色々なアノテーションを使ってフレームワークを制御します。
とても便利なのですが、このアノテーションが曲者で、たくさんある上に似たようなものが多く、何を使えばいいのか、初学者が躓くことは必至です。
ここでアノテーションに対する詳しい説明は割愛しますが、最後の参考文献に、わかりやすく説明してくれている文献を紹介しますので、詳しくはそちらをご覧ください。
話を戻して、**コントローラー**の実装です。
```java:HomeController
package com.example.demo.t.controller;

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

import com.example.demo.t.model.User;

/**
 * <h1>[HOMEのコントローラー]</h1><br>
 *<br>
 * 業務処理を持つ。
 */
@Controller
public class HomeController {
	/**
	 * <h2>[Get処理]</h2><br>
	 *<br>
	 * 何の変哲もない、Getリクエストを受け付けて、入力画面を返すだけの処理。<br>
	 * Spring Bootでは、URLとリクエストメソッドはアノテーションを使ってマッピングする。<br>
	 * この処理は、/formというURLに、Getリクエストを受信したときに起動する。<br>
	 * 画面に保持するModelを、特定のオブジェクトにロックオンして紐づける場合は、ModelAttributeとうアノテーションを付与しておけば、Thymeleafからは簡単に参照できる。
	 * @param user ユーザー情報
	 * @return 遷移先
	 */
	@GetMapping("/form")
	private String readForm(@ModelAttribute User user) {
		return "form";
	}

	/**
	 * <h2>[Post処理]</h2><br>
	 *<br>
	 * ModelAttributeで紐づけるオブジェクトに加えて、Modelそのものをもう一つの引数として受け取ることもできる。<br>
	 * サンプルでは、入力チェックとエラー画面への遷移をわかりやすくするように、業務処理内に実装する。<br>
	 * エラーメッセージは、共通オブジェクトに持たせるものと、画面スコープのModelに別出しして持たせるものと、2種類のやり方で返してみる。
	 * @param user  ユーザー情報
	 * @param model モデル
	 * @return 遷移先
	 */
	@PostMapping("/form")
	private String confirm(@ModelAttribute User user, Model model) {
		// エラーの場合は遷移先を分けてみる
		if(user.getAge() < 0) {
			user.setErrMsg("年齢の値が不正です。");

			// Modelにもエラーメッセージを追加してみる
			model.addAttribute("message", "エラーです。");

			// 遷移先はtemplatesフォルダーからのパスをファイル名で指定する
			return "folder/irregular";
		}

		return "confirm";
	}
}
```

### 確認画面から呼び出すコントローラーの実装
続いて、確認画面をトリガーとする業務処理です。
```java:FinishController.java
package com.example.demo.t.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.example.demo.t.entity.UserTable;
import com.example.demo.t.mapper.UserMapper;
import com.example.demo.t.model.User;

/**
 * <h1>[入力情報確定のコントローラー]</h1><br>
 *<br>
 * DBへの登録処理を持つ。
 */
@Controller
public class FinishController {
	/** ユーザーテーブルのMapper */
	@Autowired
	private UserMapper userMapper;

	/**
	 * <h2>[コミット処理]</h2><br>
	 *<br>
	 * 完了ページに出す情報は適当にSELECTしているので、入力情報と合いません。
	 * @param user  引き継いだユーザー情報
	 * @param model モデル
	 * @return 遷移先
	 * @throws ParseException 誕生日の変換エラー時
	 */
	@PostMapping(value="/commit", params="commit")
	public String commit(@ModelAttribute User user, Model model) throws ParseException {
		// --------------------------------------------------------------------
		// パラメータに引き渡すオブジェクト組み立て
		// --------------------------------------------------------------------
		UserTable ut = new UserTable();
		ut.setUNo(user.getUNo());
		ut.setUName(user.getUName());
		ut.setAge(user.getAge());
		ut.setBDate(new java.sql.Date(new SimpleDateFormat("yyyy/MM/dd").parse(user.getBDate()).getTime()));

		// --------------------------------------------------------------------
		// INSERT
		// --------------------------------------------------------------------
		userMapper.insert(ut);

		// --------------------------------------------------------------------
		// INSERTしたレコードをSELECT
		// 個人番号をSELECTで付与している都合上、1を使う。
		// --------------------------------------------------------------------
		List<UserTable> record = userMapper.selectByUno(1);

		// --------------------------------------------------------------------
		// 検索結果を次ページに引き渡し
		// --------------------------------------------------------------------
		model.addAttribute("userTable", record.get(0));

		return "finish";
	}

	/**
	 * <h2>[戻る処理]</h2><br>
	 *<br>
	 * 戻るボタンを押したときに入力画面に戻る。<br>
	 * PostMappingの引数にparamsを指定すると、押したsubmitのname属性が判定に追加できる。<br>
	 * その場合、マップするURLにはvalue=を指定すること。<br>
	 * formにコントローラーのクラスは1つしか紐づけられないので、仕方なしにリダイレクトという手段を取る。<br>
	 * ただし、RedirectAttributesに情報を持たせるこのやり方で実装すると、引き継ぐ情報が丸見えになるので、セキュリティー的に見えると困る情報があるときは、セッションに持たせて引き継ぐなど別の方法で実装しましょう。<br>
	 * サンプルでは、情報の少なかったこのやり方で実装する。
	 * @param user               引き継いだユーザー情報
	 * @param redirectAttributes リダイレクトに情報を引き継ぐためのオブジェクト
	 * @return リダイレクト先
	 */
	@PostMapping(value="/commit", params="back")
	public String back(@ModelAttribute User user, RedirectAttributes redirectAttributes) {
		// リダイレクト先に引き継ぐ値を詰める
		redirectAttributes.addFlashAttribute("user", user);

		return "redirect:/form";
	}
}
```

### SQLの実装
最後に、確認画面から呼び出すSQLを実装します。
SQLはMyBatisを使って発行しますので、XMLに実装します。
XMLはresourcesフォルダより、Mapperのインターフェースと同じツリー、同じ名前で配置すると、Spring Bootではうまく読み取ってくれます。
場合によっては、XMLを別フォルダにまとめたいこともあると思いますが、そのやり方はわかっていません。
ご存じの方はコメントいただけると幸いです。
```XML:UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
	Mapperの定義を追加する。
	ここでのnamespaceは、使用するMapperクラスのパッケージ名含む完全限定名を指定する。
-->
<mapper namespace="com.example.demo.t.mapper.UserMapper">
	<!--
		返却する方の定義を追加する。
		idは何でもいい感があるが、typeは使用するEntityの完全限定名を指定する。
	-->
	<resultMap id="userMap" type="com.example.demo.t.entity.UserTable">
		<result column="UNO"   jdbcType="INTEGER" property="uNo" />
		<result column="UNAME" jdbcType="VARCHAR" property="uName" />
		<result column="AGE"   jdbcType="INTEGER" property="age" />
		<result column="BDATE" jdbcType="DATE" property="bDate" />
	</resultMap>

	<!--
		さて本番のSQLの定義を追加する。
		idはメソッドと同じ名前を指定し、resultMapは↑で指定したidを使用する。
	-->
	<select id="selectByUno" resultMap="userMap" parameterType="long">
		SELECT
		    *
		FROM
		    USER_TABLE
		WHERE
		    UNO = #{uNo}
	</select>

	<insert id="insert" parameterType="com.example.demo.t.entity.UserTable">
		INSERT INTO
		    USER_TABLE (
		        UNO
		      , UNAME
		      , AGE
		      , BDATE
		    ) SELECT
		        NVL(MAX(UNO), 0) + 1
		      , #{uName}
		      , #{age}
		      , #{bDate}
		    FROM
		        USER_TABLE
	</insert>
</mapper>
```

### 実装の完了
ここまでで、サンプルの実装は完了です。
実装の最後にビルドに使ったpom.xmlを掲載し、実装の完了とします。
次からは、実際に動かしてみます。
```XML:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>SpringBootSample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBootSample</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>21</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.3</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc11</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.3</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>
```

# サンプルアプリケーションの動作確認
では、実装したサンプルを動かしましょう。
アプリケーションを実行するには、プロジェクトを右クリック＞実行＞Spring Bootアプリケーションを選びます。
サンプルはWEBアプリケーションですが、Spring Bootではビルド成果物にAPサーバー機能（Tomcat）が内蔵されていて（組み込みTomcatと呼ばれるそう）、別にサーバーを立ててデプロイしなくても、そのまま実行できるようになっています。
![009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/d04a33fd-3987-2bfd-8a09-73f257c48a81.png)

## アプリケーションにアクセス
では、起動したアプリケーションにアクセスします。
ブラウザを起動し、URLに http://localhost:8083/form を入力します。
※デフォルトはポートが8080です。

入り口の画面が表示できました。
![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/3b519421-ef5c-f57c-0710-52143dde9d27.png)

## 確認画面に行ってみる
入力するところを入力して、フォームを送信してみましょう。
![011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/3226982b-4a4a-aed0-97f7-fc58e352c0eb.png)

確認画面に行くことができました。
![012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/57f5af7e-e3b8-7928-42af-66bd86dfe3c5.png)

## 入力内容確定
では、入力内容を確定させましょう。
![013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/1e986952-2db5-9432-2e02-0efe6f217d40.png)

DBにもレコードが入り、完了画面まで行くことができました。
でも、前述のとおり、完了画面に表示しているデータは適当です。
サンプルでは諸事情で適当にしていますが、ちゃんと表示させたい場合はSQLをちゃんと実装しましょう。
![014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/cab0dc31-c2fa-509d-055b-f9998e60a5e1.png)

## エラー画面の確認
通常の業務フローが全うできることが確認できたので、エラー画面を確認してみましょう。
年齢に入力エラーとなるよう値を入力して送信すると、エラーになります。
![015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/2b76c34b-4309-0596-6652-54cbb540f352.png)

2種類のエラーメッセージも無事に表示できていますね。
![017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/77d03266-c341-f109-c623-d7c65de5e0d3.png)

## 入り口に戻る
エラー画面では、確認画面とは別の方法で入力画面に戻ります。
戻ると言うか、このボタンでは/formのURLをキックしているだけなので、単純に入力画面を初期表示します。
![016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/aa5834de-4ab8-726e-768c-e3cf7faefb10.png)
![018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/81f222b7-d162-3f09-1a5e-45fd3f7c1dee.png)

## 確認画面から戻る
次は確認画面から戻ってみます。
確認画面からは、入力情報を引き継いで戻るように実装していますので、ちゃんと情報が引き継げるか確認しましょう。
![019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/cd7d2fea-4449-63bd-f915-ffe0a8f4086b.png)
![020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/ec2c4090-01b0-b21c-7cd8-b46c97a1c13f.png)

戻ることはできましたが、住所で使っているArrayListにインデックスを指定していない箇所は表示が変です。
うまく表示できるやり方をご存じの方は、是非コメントください。
![021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189344/7145c4fb-669e-3725-95d6-4d120b5c76c9.png)

これで、サンプルアプリケーションの動作確認も完了です。

# 最後に
いかがでしたでしょうか。
今回の超入門シリーズはSpring BootのWEBアプリケーションでした。
Spring Bootは便利なのですが、いろんなツールが必要だったり、Javaバージョンの縛りがあったり、アノテーションが難しかったり、初学者にはハードルが高いのが実情です。
当記事が、そんな超初心者の方の一助になれば幸いです。

# 参考文献
今回の記事を執筆するにあたり、助けていただいた文献を紹介します。

* [Spring Boot + Spring MVC ～サンプルアプリ実装～](https://qiita.com/t-shin0hara/items/687085ec34ae78ca2260 "Spring Boot + Spring MVC ～サンプルアプリ実装～")
* [Spring + MyBatis接続設定方法](https://qiita.com/wataru908/items/115c7ada775f04dba4b5 "Spring + MyBatis接続設定方法")
* [Springで複数のsubmitボタン対応](https://qiita.com/NagaokaKenichi/items/3a4486cb6f32a3c9d69f "Springで複数のsubmitボタン対応")
* [SpringBoot でポートを変更する](https://qiita.com/hfh3oa/items/3552a51cf1d29a54c1f7 "SpringBoot でポートを変更する")
* [アノテーションの説明をわかりやすくしてくれるところ](https://stechup.co.jp/springboot-annotation/)
* [【2024年最新】Webサイトの横幅サイズはどう決める？最新トレンドも紹介](https://design-baum.jp/design/4144/ "【2024年最新】Webサイトの横幅サイズはどう決める？最新トレンドも紹介")