似非プログラマのうんちく

「似非プログラマの覚え書き」出張版

春だから Spring やろうぜ(その 11)

エンティティのバリデーション

JPA ではエンティティの各フィールドをバリデーションチェックするためのアノテーションが用意されている。

package jp.mydns.akanekodou.demo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity @Data @NoArgsConstructor
public class MyData {
    @Id @GeneratedValue
    @NotNull
    private long id;
    @Column(length = 50, nullable = false)
    @NotBlank
    private String name;
    @Column(length = 200)
    @Email
    private String mail;
    @Min(0) @Max(200)
    private Integer age;
    private String memo;

    public MyData(String name, String mail, Integer age, String memo) {
        super();
        this.name = name;
        this.mail = mail;
        this.age = age;
        this.memo = memo;
    }
}

@Email@NotBlank は元々 Hibernate Validator のオリジナルだったが Java EE 8 で導入されたようだ。


コントローラーにバリデーションの結果を伝えるため、書き換える。

package jp.mydns.akanekodou.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.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import jp.mydns.akanekodou.demo.entity.MyData;
import jp.mydns.akanekodou.demo.repositories.MyDataRepository;

@Controller
public class HelloController {
    @Autowired
    MyDataRepository repository;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(@ModelAttribute("formModel") MyData mydata, Model model) {
        model.addAttribute("msg", "This is sample content.");
        model.addAttribute("formModel", mydata);
        List<MyData> list = repository.findAll();
        model.addAttribute("datalist", list);
        return "hello";
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public String form(@ModelAttribute("formModel") @Validated MyData mydata, BindingResult result, Model model) {
        if(!result.hasErrors()) {
            repository.saveAndFlush(mydata);
            return "redirect:/";
        } else {
            model.addAttribute("msg", "Sorry, error is occured...");
            List<MyData> list = repository.findAll();
            model.addAttribute("datalist", list);
            return "hello";
        }
    }
}

ModelAndView を使ったり Model を使ったりいろいろ浮気中。ポイントは @ValidatedBindingResult 型の追加の引数。エラーが無ければ saveAndFlush でデータベースに値を保存し、そうでない場合はエラーが起きたことを知らせる。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Top page</title>
    <link th:href="@{/css/hello.css}" rel="stylesheet" />
  </head>
  <body>
    <h1 th:text="#{content.title}">Top page</h1>
    <p th:text="${msg}"></p>
    <form method="post" action="/" th:object="${formModel}">
      <table>
        <tr>
          <th><label for="name">名前</label></th>
          <td>
            <input type="text" name="name" th:value="*{name}" />
            <div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" th:errorclass="err"></div>
          </td>
        </tr>
        <tr>
          <th><label for="age">年齢</label></th>
          <td>
            <input type="text" name="age" th:value="*{age}" />
            <div th:if="${#fields.hasErrors('age')}" th:errors="*{age}" th:errorclass="err"></div>
          </td>
        </tr>
        <tr>
          <th><label for="mail">メール</label></th>
          <td>
            <input type="text" name="mail" th:value="*{mail}" />
            <div th:if="${#fields.hasErrors('mail')}" th:errors="*{mail}" th:errorclass="err"></div>
          </td>
        </tr>
        <tr>
          <th><label for="memo">メモ</label></th>
          <td><textarea name="memo" th:text="*{memo}" cols="20" rows="5"></textarea></td>
        </tr>
        <tr>
          <th></th>
          <td><input type="submit" /></td>
        </tr>
      </table>
    </form>
    <hr />
    <table>
      <tr><th>ID</th><th>名前</th></tr>
      <tr th:each="obj : ${datalist}">
        <td th:text="${obj.id}"></td>
        <td th:text="${obj.name}"></td>
      </tr>
    </table>
  </body>
</html>

テンプレートの変更。各フィールドの下にそれぞれのエラーを表示させる。

f:id:redcat_prog:20180504163254p:plain

エラー表示例。この時点ではまだデフォルトの英語メッセージになっている。(続く)