春だから 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
を使ったりいろいろ浮気中。ポイントは @Validated
と BindingResult
型の追加の引数。エラーが無ければ 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>
テンプレートの変更。各フィールドの下にそれぞれのエラーを表示させる。
エラー表示例。この時点ではまだデフォルトの英語メッセージになっている。(続く)