学習にちょうど良いWebアプリを作成【SpringBoot+Thymeleaf+PostgreSQL】

# 1. 対象読者
・Java/Spring Boot/Thymeleafを学んでいる
・練習のために簡単なWebアプリを作成したい

# 2. ゴール
顧客管理(登録/更新/削除/一覧)ができる、簡単なWebアプリを作成します。
認証認可/単体テストとかは、盛り沢山になっちゃうので省略です。
でも単体テストは時間があったら追記した方が良いかもしれない。

完成イメージは以下の通りです。
![app.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/5487ba9a-23ed-e69d-6a66-63d16ca14a6f.gif)

# 3. 完成品
完成品を先に見たい方はどうぞ。

ソースは[こちら](https://github.com/tk230to/tksystem)。
※application.ymlのDB接続情報はご自身の環境に合わせて変えてください。

動くものは[こちら](https://tksystem.herokuapp.com/)。
※Heroku無償版なので初期表示に時間が掛かります。

# 4. 前提
開発環境とPostgreSQLのインストールは実施済みとします。
インストールしていない場合は、以下のリンクからどうぞ。

- [Eclipse 2019](https://mergedoc.osdn.jp/)
- [PostgreSQL 12.2](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads)

# 5. 全体構成
今回作成するWebアプリの全体構成は以下の通りです。
![overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/82e56e99-0182-3bbb-45be-4a124cc6b0b2.png)

## 5.1 利用技術
ざっくり説明すると・・・

<dl>
  <dt>Spring Boot</dt>
  <dd>Webアプリ等を簡単に作成できるJavaのフレームワーク。</dd>

  <dt>Thymeleaf</dt>
  <dd>画面(HTML+JS+CSS)を作成できるテンプレートエンジン。</dd>

  <dt>Bootstrap</dt>
  <dd>CSSのフレームワーク。デザインに使う。</dd>

  <dt>Gradle</dt>
  <dd>ビルドツール。</dd>

  <dt>PostgreSQL</dt>
  <dd>オープンソースのデータベース。</dd>
</dl>

## 5.2 構成
<dl>
  <dt>Model</dt>
  <dd>
    DBデータを操作する役割のクラス。以下の2種類がある。<br>
    <br>
    <b>(1) Entityクラス</b><br>
    　DBのテーブル定義に対応するクラス。<br>
    　基本的には、クラス名＝テーブル名、フィールド名＝列名が対応する。<br>
    <br>
    <b>(2) Repositoryクラス</b><br>
    　DBの参照/登録/更新/削除をするメソッドを持つクラス。<br>
    <br>
  </dd>

  <dt>View</dt>
  <dd>
    ユーザに表示する画面。<br>
    具体的には、Thymeleafで定義したHTMLのテンプレートのこと。<br>
    ControllerによってModelのデータが流し込まれる。<br>
    <br>

  <dt>Controller</dt>
  <dd>
    ユーザからの入力を受け取り、次画面を返す役割のクラス。<br>
    具体的には、HTTPリクエストに応じてModelにDB参照/更新等を指示し、<br>
    Viewにデータを渡して、次画面のHTTPレスポンスを作成する。<br>
    <br>
  </dd>

  <dt>設定ファイル</dt>
  <dd>
    DB接続情報等の設定を保持する。<br>
    <br>
  </dd>

  <dt>外部ライブラリ群</dt>
  <dd>
    Spring Boot/JPA/JDBC等の様々なライブラリ群。<br>
    <br>
  </dd>

  <dt>ビルド設定ファイル(build.gradle)</dt>
  <dd>
    使用する外部ライブラリ等の設定を保持する。<br>
  </dd>
</dl>

# 6. 機能設計
開発がメインテーマなので、機能設計は簡単に書きます。

## 6.1 画面設計
### 画面一覧

|No | 画面名      | 説明                           |
|:--|:------------|:------------------------------|
|1 | トップ画面   |各画面への入口となる画面。         |
|2 | 新規登録画面 |顧客情報を新規登録する画面。       |
|3 | 更新画面     |顧客情報を変更する画面。          |
|4 | 一覧画面     |顧客情報を一覧表示、削除する画面。 |

### 画面レイアウト/画面遷移
[2. ゴール](#2-ゴール)の通り。

### 画面項目定義/インタラクション
分かると思うので省略。

## 6.2 URL設計

|No | URL                   | HTTPメソッド | 説明        |
|:--|:----------------------|:------------|:------------|
|1  |/                      | GET         | トップ画面   |
|2  |/customers             | GET         | 一覧画面     |
|3  |/customers/create      | GET         | 新規登録画面 |
|4  |/customers/create      | POST        | 新規登録実行 |
|5  |/customers/{id}/update | GET         | 更新画面     |
|6  |/customers/{id}/update | POST        | 更新実行     |
|7  |/customers/{id}/delete | GET         | 削除実行     |

[ここ](https://qiita.com/irxground/items/cd83786b10d81eecce77)を参考に設計。

## 6.3 DB設計
### テーブル一覧
|No | テーブル名 | 説明 |
|:--|:------------|:----|
|1 | customer |顧客情報を管理する。|

### テーブル定義(customer)
|No | 列名 | PK | 非Null | 説明                        |
|:--|:-----|:---|:-------|:---------------------------|
|1 | id   |○    | ○      | 顧客ID。シーケンスで自動採番。|
|2 | name |     | ○      | 顧客名。                    |

※シーケンス名は「customer_id_seq」
※列を増やしたければ後から簡単に増やせるし、
　盛り沢山にならないようシンプルにしました。

# 7. 開発
それでは本題の開発に進んでいきます。

## 7.0 全体の流れ
Spring公式サイトから雛形をダウンロードして、開発します。
テーブルは、Spring JPAの機能でModelから自動生成します。
![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/865cd5ce-eed1-bced-9db7-e6656172d965.png)

## 7.1 雛形ダウンロード
![flow1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/eb875442-2c47-dd58-4cee-4ca97c6648e2.png)

Webアプリのプロジェクトの雛形を作成します。
[Spring Initializr](https://start.spring.io/)からダウンロードするのが簡単です。
以下のように入力して、「Generate」ボタンを押すとダウンロードできます。

![spring01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/78bf208a-b9f1-5554-90f2-e76eb154e95c.png)

### 7.1.1 入力内容の説明
<dl>
  <dt>Project</dt>
  <dd>Gradleを選びます。Mavenが好きな方はMavenでもOKです。</dd>
  <dt>Group / Artifact / Name
  <dd>プロジェクト名とパッケージ名です。練習なので適当でOKです。</dd>
  <dt>Dependencies</dt>
  <dd>使用するライブラリを指定します。</dd>
</dl>

### 7.1.2 Dependenciesの説明
使用する外部ライブラリ群を設定します。

<dl>
  <dt>Spring Boot DevTools</dt>
  <dd>
    開発効率向上のためのライブラリです。<br>
    ソース変更の都度、自動でビルド＆再起動して変更内容を反映してくれます。<br>
    <br>
  </dd>
  <dt>lombok</dt>
  <dd>
    開発効率向上のためのライブラリです。<br>
    getter/setter/equals等の定型的なメソッドをアノテーションでスッキリ記述できます。<br>
    <br>
  </dd>
  <dt>Spring Configuration Processor</dt>
  <dd>
    開発効率向上のためのライブラリです。<br>
    設定ファイル(application.yml)でコード補完ができるようになり、記述ミスを抑止できます。<br>
    <br>
  </dd>
  <dt>Spring Web</dt>
  <dd>
    Webアプリ/WebAPIのためのライブラリです。<br>
    <br>
  </dd>
  <dt>Thymeleaf</dt>
  <dd>
    HTMLのテンプレートエンジンです。前述の通りViewの役割ですね。<br>
    <br>
  </dd>
  <dt>Spring Data JPA</dt>
  <dd>
    DB関連のライブラリです。JPAベースのDB操作ができます。<br>
    JPAをざっくり説明すると、SQLを書かなくても簡単なDB操作ができる仕組みです。<br>
    <br>
  </dd>
  <dt>PostgreSQL Driver</dt>
  <dd>
    PostgreSQLのJDBCドライバです。これがないとPostgreSQL使えません。<br>
    <br>
  </dd>
</dl>

### 7.1.3 補足
Eclipseのメニューからでも同様に作成できます。
具体的には、「ファイル」「新規」「その他」「Spring Boot」「Spring スターター・プロジェクト」です。

## 7.2 ビルド
![flow2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/67b48231-3b3e-f7f4-6462-9456eaf6fbee.png)

ダウンロードした雛形をEclipseにインポートします。
具体的な手順は以下の通りです。

1. ダウンロードした雛形(zip)を、Eclipseのワークスペースがあるフォルダに解凍。
2. Eclipseを起動。
3. 「ファイル」「インポート」「Gradle」「既存のGradleプロジェクト」「次へ」
4. 「プロジェクト・ルート・ディレクトリ」に1のフォルダを指定して「完了」

インポートすると自動的にビルドされ、build.gradleのdependenciesの通りに、
外部ライブラリ群がダウンロードされます。

## 7.2.1 外部ライブラリ追加
Spring Initializrでは入らない外部ライブラリがあります。
具体的には、bootstrapのjava用ライブラリが入りません。
Eclipseでbuild.gradleファイルを開き、以下の通り追加します。

```groovy:build.gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.webjars:jquery:3.3.1'        // 追加
	implementation 'org.webjars:bootstrap:4.3.1'     // 追加
	implementation 'org.webjars:font-awesome:5.13.0' // 追加
// 省略。完全版はhttps://github.com/tk230to/tksystem
```

### 【補足】追加した外部ライブラリの説明
<dl>
  <dt>org.webjars:jquery</dt>
  <dd>jQueryのjava用ライブラリ。</dd>

  <dt>org.webjars:bootstrap</dt>
  <dd>Bootstrapのjava用ライブラリ。</dd>

  <dt>org.webjars:font-awesome</dt>
  <dd>Font Awesome(アイコンを利用)のjava用ライブラリ。</dd>
</dl>

### 【補足】外部ライブラリの探し方
こういうライブラリは[Maven Repository](https://mvnrepository.com/)で探します。

## 7.3 各クラス作成
![flow3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/06f2e477-bf5c-c25c-036e-148c76732e57.png)

各クラスや設定ファイルを作成します。

### 7.3.1 作成対象
Model/View/Controller/設定ファイルを作成します。
このファイル何だったっけ？という場合は、[5.2 構成](#52-構成)を読み直してください。

### 7.3.2 Model(Entityクラス)作成
#### Customerクラス
[customerテーブル](#63-db設計)に対応するクラスを作成します。
クラス名＝テーブル名、フィールド名＝列名になります。

```java:Customer.java
package com.example.tksystem.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.validation.constraints.NotBlank;

import lombok.Data;

/**
 * 顧客クラス。
 */
@Entity
@Data
public class Customer {

  /** シーケンス名 */
  private static final String SEQUENCE_NAME = "customer_id_seq";

  /** ID */
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE_NAME)
  @SequenceGenerator(name = SEQUENCE_NAME, sequenceName = SEQUENCE_NAME, allocationSize = 1)
  private Long id;

  /** 名前 */
  @NotBlank
  private String name;
}
```

<dl>
<dt><span>@</span>Entity</dt>
<dd>Entityクラスであることを示すアノテーション。</dd>
<dt><span>@</span>Data</dt>
<dd>getter/setter等を自動生成してくれるlombokのアノテーション。</dd>
<dt><span>@</span>Id</dt>
<dd>PK列であることを示すアノテーション。</dd>
<dt><span>@</span>GeneratedValue</dt>
<dd>シーケンスで自動採番することを示すアノテーション。</dd>
<dt><span>@</span>SequenceGenerator</dt>
<dd>シーケンスを作成するためのアノテーション。</dd>
</dl>

### 7.3.3 Model(Repositoryクラス)作成
#### CustomerRepositoryクラス
customerテーブルへのCRUD操作をするクラスを作成します。
JpaRepositoryを継承するだけです。

```java:CustomerRepository.java
package com.example.tksystem.model;

import org.springframework.data.jpa.repository.JpaRepository;

/**
 * 顧客リポジトリクラス。
 */
public interface CustomerRepository extends JpaRepository<Customer, Long> {

}
```

以下のメソッド(抜粋)が使えるようになります。

<dl>
<dt>List&lt;Customer&gt; findAll()</dt>
<dd>テーブルの全レコードを参照(SELECT)し、リストで返す。</dd>
<dt>Customer getOne(Long id)</dt>
<dd>顧客IDに該当するレコードを参照(SELECT)し、結果を返す。</dd>
<dt>Customer save(Customer entity)</dt>
<dd>引数の顧客情報を登録/更新(INSERT/UPDATE)し、結果レコードを返す。</dd>
<dt>void deleteById(Long id)</dt>
<dd>顧客IDに該当するレコードを削除(DELETE)する。</dd>
</dl>

### 7.3.4 View作成
各画面のHTMLテンプレートを作成します。
上部のナビゲーションバーは全画面共通なので、共通レイアウトとして共通化します。

#### 共通レイアウト
```html:layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head th:fragment="head(title)">

  <title th:text="'顧客管理 - ' + ${title}">tksystem</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  <link rel="stylesheet" th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.min.css}" />
  <link rel="stylesheet" th:href="@{/webjars/font-awesome/5.13.0/css/all.min.css}" />

  <script th:src="@{/webjars/jquery/3.3.1/jquery.min.js}"></script>
  <script th:src="@{/webjars/bootstrap/4.3.1/js/bootstrap.min.js}"></script>

</head>

<body>
  <nav class="navbar navbar-dark bg-dark navbar-expand-sm mb-3" th:fragment="navbar">

    <a class="navbar-brand" href="/">顧客管理</a>

    <button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#Navbar" aria-controls="Navbar" aria-expanded="false" aria-label="ナビゲーションの切替">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div id="Navbar" class="collapse navbar-collapse">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link" th:href="@{/customers/create/}">
            <i class="fas fa-plus fa-lg" aria-hidden=”true”></i> 新規登録
          </a>
        </li>
        <li class="nav-item">
          <a class="nav-link" th:href="@{/customers/}">
            <i class="fas fa-list fa-lg" aria-hidden=”true”></i> 一覧
          </a>
        </li>
      </ul>
    </div>

  </nav>
</body>

</html>
```

<dl>
<dt>th:fragment</dt>
<dd>他の画面でth:replaceで差し替えられるようにします。</dd>
<dt>その他のThymeleafの文法</dt>
<dd><a href="https://qiita.com/NagaokaKenichi/items/c6d1b76090ef5ef39482" target="_blank">こちら</a>を参考にしてください。</dd>
<dt>Bootstrapの文法</dt>
<dd><a href="https://getbootstrap.jp/docs/4.3/getting-started/introduction/" target="_blank">公式ドキュメント</a>を参考にしてください。</dd>
</dl>

#### トップ画面
```html:index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head th:replace="fragments/layout :: head('Home')">
</head>

<body>
  <div th:replace="fragments/layout :: navbar"></div>

  <div class="container-fluid">
    <h5>メニュー</h5>
    <hr>

    <div class="row">
      <div class="col-sm-6">
        <div class="card">
          <div class="card-body">
            <h5 class="card-title">
              <a class="nav-link" th:href="@{/customers/create}">
                <i class="fas fa-plus fa-lg" aria-hidden=”true”></i> 新規登録
              </a>
            </h5>
            <p class="card-text">顧客を新規登録します。</p>
          </div>
        </div>
      </div>

      <div class="col-sm-6">
        <div class="card">
          <div class="card-body">
            <h5 class="card-title">
              <a class="nav-link" th:href="@{/customers/}">
                <i class="fas fa-list fa-lg" aria-hidden=”true”></i> 一覧
              </a>
            </h5>
            <p class="card-text">顧客の一覧を表示します。</p>
          </div>
        </div>
      </div>

    </div>
  </div>

</body>

</html>
```

#### 新規登録画面
```html:create.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head th:replace="fragments/layout :: head('新規登録')">
</head>

<body>
  <div th:replace="fragments/layout :: navbar"></div>

  <div class="container-fluid">
    <h5>新規登録</h5>
    <hr>

    <div class="row">
      <div class="col-sm-12">
        <form action="#" th:action="@{/customers/create/}" th:object="${customer}" method="post">
          <div class="form-group">
            <label for="name">名前 <span class="badge badge-danger">必須</span></label>
            <input type="text" id="name" class="form-control" placeholder="(例) 山田　太郎" th:field="*{name}" />
            <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" style="color: red"></span>
          </div>

          <button type="submit" class="btn btn-primary">確定</button>  
        </form>
      </div>
    </div>
  </div>
</body>
</html>
```

#### 更新画面
登録画面とほぼ同様です。完成品は[こちら](https://github.com/tk230to/tksystem)

#### 一覧画面
```html:list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head th:replace="fragments/layout :: head('一覧')">
</head>

<body>
  <div th:replace="fragments/layout :: navbar"></div>

  <div class="container-fluid">
    <h5>一覧</h5>
    <hr>

    <div class="row">
      <div class="col-sm-12">

        <table class="table table-bordered table-hover">
          <thead class="thead-dark">
            <tr>
              <th width="10%">ID</th>
              <th width="80%">名前</th>
              <th width="10%">削除</th>
            </tr>
          </thead>
          <tbody>
            <tr th:each="customer:${customer}">
              <td th:text="${customer.id}"></td>
              <td>
                <a th:text="${customer.name}" th:href="@{/customers/{id}/update/(id=${customer.id})}">
                </a>
              </td>
              <td>
                <a th:href="@{/customers/{id}/delete/(id=${customer.id})}" onClick="return window.confirm('削除してよろしいですか？')">
                  <i class="far fa-trash-alt fa-lg" aria-hidden=”true”></i>
                </a>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</body>

</html>
```

### 7.3.5 Controllerの作成
#### IndexControllerクラス
トップ画面のリクエストを処理するクラスを作成します。

```java:IndexController.java
package com.example.tksystem.controller;

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

/**
 * index画面のコントローラクラス。
 */
@Controller
@RequestMapping("/")
public class IndexController {

  /**
   * index画面
   *
   * @param model モデル
   * @return 遷移先
   */
  @RequestMapping("index")
  public String index(Model model) {
    return "index";
  }
}
```

<dl>
<dt><span>@</span>Controller</dt>
<dd>Controllerクラスであることを示すアノテーションです。<br><br></dd>
<dt><span>@</span>RequestMapping</dt>
<dd>
  HTTPリクエストのURLとクラス/メソッドをマッピングします。<br>
  この例だと、URLが"/index/"のとき、index(Model model)メソッドが呼ばれます。<br>
  <br>
</dd>
<dt>メソッドの返り値</dt>
<dd>
  戻り値の文字列は、次画面のThymeleafのHTMLファイル名を示します。<br>
  "index"の場合、/src/main/resources/templates/index.htmlを示します。<br>
  <br>
</dd>
</dl>

#### CustomerControllerクラス
顧客画面(新規登録/更新/一覧画面)のリクエストを処理するクラスを作成します。

```java:CustomerController.java
package com.example.tksystem.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.tksystem.model.Customer;
import com.example.tksystem.model.CustomerRepository;

/**
 * 顧客画面のコントローラクラス。
 */
@Controller
@RequestMapping("/customers")
public class CustomerController {

  /** 登録/更新/削除完了後のリダイレクト先URL */
  private static final String REDIRECT_URL = "redirect:/customers/";

  /** HTMLパス */
  private static final String PATH_LIST = "customer/list";
  private static final String PATH_CREATE = "customer/create";
  private static final String PATH_UPDATE = "customer/update";

  /** Modelの属性名 */
  private static final String MODEL_ATTRIBUTE_NAME = "customer";

  /** 顧客リポジトリ */
  @Autowired
  private CustomerRepository customerRepository;

  /**
   * 一覧画面を表示。
   *
   * @param model モデル
   * @return 遷移先
   */
  @GetMapping(value = "/")
  public String list(Model model) {
    model.addAttribute(MODEL_ATTRIBUTE_NAME, customerRepository.findAll(Sort.by("id")));
    return PATH_LIST;
  }

  /**
   * 登録画面を表示。
   *
   * @param model モデル
   * @return 遷移先
   */
  @GetMapping(value = "/create")
  public String create(Model model) {
    model.addAttribute(MODEL_ATTRIBUTE_NAME, new Customer());
    return PATH_CREATE;
  }

  /**
   * 登録を実行。
   *
   * @param customer 顧客画面入力値
   * @param result 入力チェック結果
   * @return 遷移先
   */
  @PostMapping(value = "/create")
  public String create(@Validated @ModelAttribute(MODEL_ATTRIBUTE_NAME) Customer customer,
      BindingResult result) {

    if (result.hasErrors()) {
      return PATH_CREATE;
    }

    customerRepository.save(customer);
    return REDIRECT_URL;
  }

  /**
   * 更新画面を表示。
   *
   * @param id 顧客ID
   * @param model モデル
   * @return 遷移先
   */
  @GetMapping(value = "/{id}/update")
  public String update(@PathVariable("id") Long id, Model model) {
    model.addAttribute(MODEL_ATTRIBUTE_NAME, customerRepository.getOne(id));
    return PATH_UPDATE;
  }

  /**
   * 更新を実行。
   *
   * @param customer 顧客画面入力値
   * @param result 入力チェック結果
   * @return 遷移先
   */
  @PostMapping(value = "/{id}/update")
  public String update(@Validated @ModelAttribute(MODEL_ATTRIBUTE_NAME) Customer customer,
      BindingResult result) {

    if (result.hasErrors()) {
      return PATH_UPDATE;
    }

    customerRepository.save(customer);
    return REDIRECT_URL;
  }

  /**
   * 削除を実行。
   *
   * @param id 顧客ID
   * @return 遷移先
   */
  @GetMapping(value = "/{id}/delete")
  public String list(@PathVariable("id") Long id) {
    customerRepository.deleteById(id);
    return REDIRECT_URL;
  }
}
```

<dl>
<dt><span>@</span>GetMapping/<span>@</span>PostMapping</dt>
<dd><span>@</span>RequestMappingと同様です。</dd>
<dt><span>@</span>Validated</dt>
<dd>入力チェックをすることを示すアノテーションです。</dd>
<dt>BindingResult result</dt>
<dd>入力チェック結果が入っています。</dd>
</dl>

### 7.3.6 設定ファイルの作成
#### application.yml
DB接続情報等を設定するファイルです。
url/username/passwordは、PostgreSQLのデフォルトにしてあります。
デフォルトと違う場合は変更してください。

```yml:application.yml
spring:
  datasource:

    # PostgresのIPアドレス/ポート番号/DB名
    url: jdbc:postgresql://localhost:5432/postgres

    # Postgresのユーザ名
    username: postgres

    # Postgresのパスワード
    password: postgres

    # PostgresのJDBCドライバ
    driver-class-name: org.postgresql.Driver

  jpa:
    hibernate:

      # @Entityに対応するテーブルを常にdrop&createする。
      ddl-auto: create-drop
```

<dl>
<dt>spring.jpa.hibernate.ddl-auto</dt>
<dd><a href="https://qiita.com/KenjiOtsuka/items/8450c407ba121fea8151" target="_blank">ここ</a>を参照してください。</dd>
</dl>

#### hibernate.properties
PostgreSQL×Spring JPAで発生する例外を回避する設定です。
無くても動作上問題無いですが、毎回例外出るのが気持ち悪いので設定した方がベターです。
例外の詳細は[こちら](https://qiita.com/bwtakacy/items/be3509e1765546f92184)

```properties:hibernate.properties
# PgConnection.createClob()メソッドの警告を回避
hibernate.jdbc.lob.non_contextual_creation = true
```

#### ValidationMessages.properties
入力チェックエラーメッセージのメッセージ定義です。
まだ日本語メッセージが提供されていないため自作します。
[ここ](https://qiita.com/aoetk/items/415978ee083cff9a3bcb)でプルリクエストが取り込まれているので、そのうち提供されると思います。

```properties:ValidationMessages.properties
javax.validation.constraints.AssertFalse.message     = false にしてください
javax.validation.constraints.AssertTrue.message      = true にしてください
javax.validation.constraints.DecimalMax.message      = {value} ${inclusive == true ? '以下の値にしてください' : 'より小さな値にしてください'}
javax.validation.constraints.DecimalMin.message      = {value} ${inclusive == true ? '以上の値にしてください' : 'より大きな値にしてください'}
javax.validation.constraints.Digits.message          = 値は次の範囲にしてください (<整数 {integer} 桁>.<小数点以下 {fraction} 桁>)
javax.validation.constraints.Email.message           = 電子メールアドレスとして正しい形式にしてください
javax.validation.constraints.Future.message          = 未来の日付にしてください
javax.validation.constraints.FutureOrPresent.message = 現在もしくは未来の日付にしてください
javax.validation.constraints.Max.message             = {value} 以下の値にしてください
javax.validation.constraints.Min.message             = {value} 以上の値にしてください
javax.validation.constraints.Negative.message        = 0 より小さな値にしてください
javax.validation.constraints.NegativeOrZero.message  = 0 以下の値にしてください
javax.validation.constraints.NotBlank.message        = 空白は許可されていません
javax.validation.constraints.NotEmpty.message        = 空要素は許可されていません
javax.validation.constraints.NotNull.message         = null は許可されていません
javax.validation.constraints.Null.message            = null にしてください
javax.validation.constraints.Past.message            = 過去の日付にしてください
javax.validation.constraints.PastOrPresent.message   = 現在もしくは過去の日付にしてください
javax.validation.constraints.Pattern.message         = 正規表現 "{regexp}" にマッチさせてください
javax.validation.constraints.Positive.message        = 0 より大きな値にしてください
javax.validation.constraints.PositiveOrZero.message  = 0 以上の値にしてください
javax.validation.constraints.Size.message            = {min} から {max} の間のサイズにしてください
```

#### WebConfig
上記ValidationMessages.propertiesを使う設定をします。
詳細は[ここ](https://qiita.com/yukina-ge/items/b4303920c60ec3251d37)を参照してください。

```java:WebConfig.java
package com.example.tksystem;

import java.nio.charset.StandardCharsets;

import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Web設定クラス。
 *
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

  @Override
  public Validator getValidator() {

    // ValidationMessages.propertiesをUTF-8で設定できるようにする。
    ReloadableResourceBundleMessageSource messageSource =
        new ReloadableResourceBundleMessageSource();
    messageSource.setBasename(AbstractMessageInterpolator.USER_VALIDATION_MESSAGES);
    messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());

    LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
    validator.setValidationMessageSource(messageSource);

    return validator;
  }
}
```

## 7.4 起動＆テーブル自動生成
![flow4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263069/10862bc8-45cc-342c-27ab-b3f2361cf6f4.png)

全てのファイルが完成しましたので、Eclipseでプロジェクトを実行します。
手順は、プロジェクトを選択して「右クリック」「実行」「Spring Boot アプリケーション」です。

application.ymlで設定したddl-autoの設定により、
作成したEntityクラスの通りにテーブルが自動生成されます。


[http://localhost:8080](http://localhost:8080)にアクセスして動作確認してみてください。
[2. ゴール](#2-ゴール)のように動作すれば成功です。

以上、お疲れ様でした。