Spring Boot 1.3.5 + Thymeleaf + PostgreSQL 9.6 導入メモ (2)

# はじめに
記事を書いていたら思いのほか長くなったので、

* Spring Boot導入～フォームの利用 http://qiita.com/sashim1343/items/065c2d72f772da55069f
* 入力のバリデーション、JPAを利用したデータの永続化（本記事）

の2つに分割しました。

前回の記事はこちら→ http://qiita.com/sashim1343/items/065c2d72f772da55069f

# 入力値のバリデーション
各種アノテーションを利用することで、手軽にバリデーションを行える。
例として、前回作成した`PersonForm`の`name`と`age`に対して次の制約を設ける。

* name: 空でなく、かつ31文字以下
* age: 0以上の整数

これをコードにしたものが下記の`PersonForm`クラスである。

```java:PersonForm.java
package com.example.springbootsample.controller;

import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;

public class PersonForm {
    @NotBlank
    @Size(max = 31)
    private String name;
    
    @Min(0)
    private Integer age;
    // setter/getter略
}
```

これでバリデーションを行えるようになった。
次に、バリデーションエラー時にはエラー内容を表示し、エラーがなければ名前と年齢を表示するようにビューとコントローラを修正する。

```html:sample.html
<!-- 前略 -->
<form method="POST" th:action="@{/}">
    <div th:if="${personForm != null}" th:object="${personForm}">
        <ul>
            <li th:each="e : ${#fields.detailedErrors()}" th:text="${e}"></li>
        </ul>
    </div>
    <label for="name">Your name: </label><input type="text" name="name" />
    <label for="age">Your age: </label><input type="text" name="age" />
    <input type="submit" value="submit" />
</form>
<!-- 後略 -->
```

```java:SampleController.java
package com.example.springbootsample.controller;

import org.springframework.stereotype.*;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/")
public class SampleController {
    @RequestMapping(method = RequestMethod.GET)
    String get(Model model) {
        model.addAttribute("message", "Hello, Spring Boot!");
        return "sample";
    }

    @RequestMapping(method = RequestMethod.POST)
    String post(@ModelAttribute("personForm") @Valid PersonForm personForm, BindingResult result, Model model) {
        if (result.hasErrors()) {
            model.addAttribute("message", "Error");
        } else {
            String name = personForm.getName();
            Integer age = personForm.getAge();
            model.addAttribute("message", "your name: " + name + ", your age: " + age);
        }
        return "sample";
    }
}
```

`@Valid`アノテーションを付与することでバリデーションが有効になる。
結果は`BindingResult`インスタンスに格納される。
プログラムを実行し、フォーム入力が有効な場合と無効な場合で挙動が変わることを確認する。

# JPAの利用 (Entity, Repository作成)
ひとまずDB抜きでSpring Data JPAを利用してみる。
例によって`pom.xml`に依存関係を追加する。

```xml:pom.xml
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- JPAをインメモリで利用するために追加 -->
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>runtime</scope>
</dependency>
```

次にモデルとその永続化ロジックを実装する。
まずはモデルから。
`com.example.springbootsample.model`パッケージを作成し、その中に`Person`クラスを作成する。

```java:Person.java
package com.example.springbootsample.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    private Integer age;
    
    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "name: " + name + ", age: " + age;
    }
}
```

`@Entity`アノテーションを付与したクラスはDomain-Driven DesignにおけるEntityとなる。
このエンティティの識別子として使うフィールドには`@Id`アノテーションを付ける。
また、idの値は`@GeneratedValue`により自動で割り振られる。

あとはこのエンティティを永続化させるためのRepositoryを用意すれば良い。
`com.example.springbootsample.repository`パッケージを作成し、その中に`PersonRepository`インターフェイスを定義する。

```java:PersonRepository.java
package com.example.springbootsample.repository;

import com.example.springbootsample.model.Person;
import org.springframework.data.repository.RepositoryDefinition;

@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonRepository {
    Person save(Person person);
    Iterable<Person> findAll();
}
```

Spring Data JPAを利用すると、上記のような命名規則に従ったメソッドを宣言するだけで良い感じに使えるようになる。
今回はPersonを保存/更新するためのメソッドと、保存されているすべてのPersonを取得するメソッドの2つを用意した。
ここまでで、Personを永続化する準備が整った。

# Entity/Repositoryの利用
では、いよいよ先ほど作成したEntity/Repositoryをコントローラから利用する。
コントローラは、下記2点の処理を行う。

* 保存されているすべてのPersonの情報を表示する
* フォームから送信された値が有効ならば、新しいPersonインスタンスを生成・保存する

これを実現するためのコントローラのコードは次のようになる。

```java:SampleController.java
package com.example.springbootsample.controller;

import com.example.qiitaspringdemo.model.Person;
import com.example.qiitaspringdemo.repository.PersonRepository;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.*;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/")
public class SampleController {
    @Autowired
    PersonRepository repository;
    
    @RequestMapping(method = RequestMethod.GET)
    String get(Model model) {
        model.addAttribute("persons", repository.findAll());
        return "sample";
    }

    @RequestMapping(method = RequestMethod.POST)
    String post(@ModelAttribute @Valid PersonForm personForm, BindingResult result, Model model) {
        if (!result.hasErrors()) {
            String name = personForm.getName();
            Integer age = personForm.getAge();
            repository.save(new Person(name, age));
        }
        model.addAttribute("persons", repository.findAll());
        return "sample";
    }
}
```

ポイントは15, 16行目の

```java
@Autowired
PersonRepository repository;
```

である。
`@Autowired`アノテーションを指定することで、Repositoryを自動的にインジェクションする。

Person一覧をビューに渡すようにコントローラを変更したので、それに合わせてビューも修正する。

```html:sample.html
<!-- 前略 -->
<body>
    <h1>People</h1>
    <ul>
        <li th:each="e : ${persons}" th:text="${e}" />
    </ul>
    <hr />
    <form method="POST" th:action="@{/}">
        <div th:if="${personForm != null}" th:object="${personForm}">
            <ul>
                <li th:each="e : ${#fields.detailedErrors()}" th:text="${e}"></li>
            </ul>
        </div>
        <label for="name">Your name: </label><input type="text" name="name" />
        <label for="age">Your age: </label><input type="text" name="age" />
        <input type="submit" value="submit" />
    </form>
</body>
<!-- 後略 -->
```

これを実行してみる。
フォームに名前と年齢を入力して送信するたびにPersonが増えていけば成功である。
保存されている値はSpring Bootアプリケーションを実行している間は保持されるので、タブを開き直しても入力した情報が残っていることが分かる。
プログラムを終了しても値を残したい場合には、DBを用いて永続化する必要がある。

# PostgreSQLを用いたデータの永続化

まず、PostgreSQLをインストールする。
http://www.postgresql.jp/download から自身の環境用のインストーラを手に入れ、適当にインストールする。
細かい設定については割愛。Spring Bootとの連携をテストするために`sample`データベースを作成した。

`pom.xml`にPostgreSQL関連の依存関係を追加する。

```xml:pom.xml
<!-- https://mvnrepository.com/artifact/postgresql/postgresql -->
<dependency>
    <groupId>postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.1-901-1.jdbc4</version>
</dependency>
```

また、`application.yml`にDB周りの設定を記述する。

```yaml:application.yml
## YAML Template.
---
spring:
    thymeleaf:
        cache: false
    datasource:
        url: jdbc:postgresql://localhost/sample
        username: postgres
        password: ##password goes here
    jpa:
        database-platform: org.hibernate.dialect.PostgreSQLDialect
        generate-ddl: true
```

設定はこれで終わり。
再びアプリケーションを実行し、DBにデータが格納されることを確認する。

# さいごに
今回は説明を簡単にするため、ControllerがRepositoryを直接利用しましたが、綺麗な設計という観点では、Controller→Service→Repositoryとする方が良いかと思います。

この記事は、Spring Boot初心者が手探りで勉強したメモですので、内容が間違っている可能性があります。
「ここがおかしい」という点などございましたらぜひご指摘下さい。

# 参考
http://ziqoo.com/wiki/index.php?Spring%20Boot%20%C6%FE%CE%CF%A5%C1%A5%A7%A5%C3%A5%AF
http://www.riem.nagoya-u.ac.jp/~ohta/etc/springboot-3.html
http://projects.spring.io/spring-data-jpa/
https://gist.github.com/ryo-murai/3741340
https://www.postgresql.jp/