はじめに
記事を書いていたら思いのほか長くなったので、
- Spring Boot導入~フォームの利用 http://qiita.com/sashim1343/items/065c2d72f772da55069f
- 入力のバリデーション、JPAを利用したデータの永続化(本記事)
の2つに分割しました。
前回の記事はこちら→ http://qiita.com/sashim1343/items/065c2d72f772da55069f
入力値のバリデーション
各種アノテーションを利用することで、手軽にバリデーションを行える。
例として、前回作成したPersonForm
のname
とage
に対して次の制約を設ける。
- name: 空でなく、かつ31文字以下
- age: 0以上の整数
これをコードにしたものが下記のPersonForm
クラスである。
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略
}
これでバリデーションを行えるようになった。
次に、バリデーションエラー時にはエラー内容を表示し、エラーがなければ名前と年齢を表示するようにビューとコントローラを修正する。
<!-- 前略 -->
<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>
<!-- 後略 -->
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
に依存関係を追加する。
<!-- 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
クラスを作成する。
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
インターフェイスを定義する。
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インスタンスを生成・保存する
これを実現するためのコントローラのコードは次のようになる。
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行目の
@Autowired
PersonRepository repository;
である。
@Autowired
アノテーションを指定することで、Repositoryを自動的にインジェクションする。
Person一覧をビューに渡すようにコントローラを変更したので、それに合わせてビューも修正する。
<!-- 前略 -->
<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関連の依存関係を追加する。
<!-- 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 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/