春だから 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 で導入されたようだ。
春だから Spring やろうぜ(その 10)
入力用のフォームを準備して、データを入力・保存できるようにする。
<!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> <form method="post" action="/" th:object="${formModel}"> <table> <tr> <th><label for="name">名前</label></th> <td><input type="text" name="name" th:value="*{name}" /></td> </tr> <tr> <th><label for="age">年齢</label></th> <td><input type="text" name="age" th:value="*{age}" /></td> </tr> <tr> <th><label for="mail">メール</label></th> <td><input type="text" name="mail" th:value="*{mail}" /></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>
CSS は割愛。
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.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; 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 ModelAndView index(@ModelAttribute("formModel") MyData mydata, ModelAndView mav) { mav.setViewName("hello"); List<MyData> list = repository.findAll(); mav.addObject("datalist", list); return mav; } @RequestMapping(value = "/", method = RequestMethod.POST) public ModelAndView form(@ModelAttribute("formModel") MyData mydata, ModelAndView mav) { repository.saveAndFlush(mydata); return new ModelAndView("redirect:/"); } }続きを読む
春だから Spring やろうぜ(その 9)
エンティティから DDL を自動実行
以下のようなエンティティから DDL を自動で実行してデータベースにテーブルを作ってもらいたい。
package jp.mydns.akanekodou.demo.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import lombok.Getter; import lombok.Setter; @Entity @Getter @Setter public class MyData { @Id @GeneratedValue private long id; @Column(length = 50, nullable = false) private String name; @Column(length = 200) private String mail; private Integer age; private String memo; }
H2 などの組み込みデータベースなら特に設定はいらないのだけど、PostgreSQL を使っているのでそのままでは上手く行かない。そこで application.properties に 1 行追加する。
spring.jpa.hibernate.ddl-auto=update
これで必要に応じて DDL を生成・発行してデータベース側にテーブルを作ってくれる。
詳しい解説はこちら。
春だから Spring やろうぜ(その 8)
Thymeleaf で使えるメッセージ式
/src/main/resources フォルダに messages.properties ファイルを作り、以下の内容で保存する。
content.title=Sample page content.message=This is sample message from properties.
こうするとテンプレート内で #{content.title}
のようにして値が取り出せる.
ローカライズ
以下の内容で messages_ja.properties も作っておく。
content.title=サンプルページ content.message=これはサンプルで用意したメッセージです。
content.title=\u30B5\u30F3\u30D7\u30EB\u30DA\u30FC\u30B8 content.message=\u3053\u308C\u306F\u30B5\u30F3\u30D7\u30EB\u3067\u7528\u610F\u3057\u305F\u30E1\u30C3\u30BB\u30FC\u30B8\u3067\u3059\u3002
のように表示される。こうすると日本語(ja)環境では messages_ja.properties が、それ以外では messages.properties が使われる。
春だから Spring やろうぜ(その 7)
Gradle を使ってみる
前回作ったものを
- ビルドツールとして Maven ではなく Gradle を使ってみる
- データベースは PostgreSQL を使ってみる
で作り直してみる。
プラグインのインストール
Gradle によるビルドを行うためには STS にプラグインを入れる必要がある。
http://download.eclipse.org/buildship/updates/e47/releases/2.x
からプラグインをインストール。
スタータープロジェクトの作成
スクリーンショットではプロジェクト名が「SpringWebApp-1」になっているが、実際は「SpringWebApp」で作っています。
ちゃっかり Java 9 にしておいた。そしていつの間にか Spring Boot が 2.0.1 になってる ?
build.gradle はこんな感じになってた。
buildscript { ext { springBootVersion = '2.0.1.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'jp.mydns.akanekodou' version = '0.0.1-SNAPSHOT' sourceCompatibility = 9 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-data-jpa') compile('org.springframework.boot:spring-boot-starter-thymeleaf') compile('org.springframework.boot:spring-boot-starter-web') runtime('org.springframework.boot:spring-boot-devtools') runtime('org.postgresql:postgresql') compileOnly('org.projectlombok:lombok') testCompile('org.springframework.boot:spring-boot-starter-test') }
build.gradle の正体は Groovy のコードらしい。ちなみに Gradle の中に Groovy が内包されているので別途 Groovy をインストールする必要はない(内包された Groovy が単体で使えるわけではない)。
初 Gradle だけど読むと何となく何をやってるのかはわかった。
春だから Spring やろうぜ(その 6)
いよいよデータベースにアクセス
今回は H2 データベースから全件取得して表示するところまで。
Entity クラスの作成
基本的に JPA なのでそんなに難しくない。Lombok を使っているので @Data
アノテーションを使うと楽。
package jp.mydns.akanekodou.demo; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Data @NoArgsConstructor public class MyDataEntity { @Id @GeneratedValue private Integer id; private String name; private String mail; public MyDataEntity(String name, String mail) { this.name = name; this.mail = mail; } }
リポジトリの作成
Spring Boot ならではの機能。データベースアクセス用のリポジトリを作成する。
package jp.mydns.akanekodou.demo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface MyDataEntityRepository extends JpaRepository<MyDataEntity, Integer> { }
何と空っぽのインターフェースである。
コントローラーの修正。
package jp.mydns.akanekodou.demo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class HelloController { @Autowired MyDataEntityRepository repository; @RequestMapping(value="/", method=RequestMethod.GET) public String index(Model model) { model.addAttribute("title", "Hello Page"); model.addAttribute("message", "MyDataEntity List."); List<MyDataEntity> data = repository.findAll(); model.addAttribute("data", data); return "hello"; } }
@Autowired
が Spring らしい。Spring の根幹であった DI によるインスタンス作成をここで行っている。findAll
なんて実装してないぞ、と思われるだろうが、MyDataEntityRepository
の継承元である JpaRepository
から継承されたメソッドである。
春だから Spring やろうぜ(その 5)
フォームを利用してみる
今回は
- フォームから入力を受け付ける
- 入力された値はセッションに保存しておく
ところまでやってみる。
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="${title}"/> <link th:href="@{/css/hello.css}" rel="stylesheet" /> </head> <body> <h1 th:text="${title}"/> <p th:text="${message}"/> <form method="post" action="/"> <input type="text" name="input1" /> <input type="submit" value="Click" /> </form> </body> </html>
フォームを用意する。
package jp.mydns.akanekodou.demo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; @Controller @SessionAttributes("message") public class HelloController { @ModelAttribute("message") String message() { return "これは初期メッセージです。"; } @RequestMapping(value="/", method=RequestMethod.GET) public String index(@ModelAttribute("message")String msg, Model model) { System.out.println("@ModelAttribute(\"message\") : " + msg); model.addAttribute("title", "Hello page"); return "hello"; } @RequestMapping(value="/", method=RequestMethod.POST) public String form(@ModelAttribute("message")String msg, @RequestParam("input1")String input1, Model model) { System.out.println("@ModelAttribute(\"message\") : " + msg); String res = "最後の入力 : " + input1; model.addAttribute("title", "Answer page"); model.addAttribute("message", res); return "hello"; } }
@SessionAttributes("message")
で message がセッション変数であることをコントローラーに教える。コントローラー内では @ModelAttribute("message")
を付けて参照ができるようになる。最初にファクトリメソッドで初期化をしている。
@RequestParam
でフォームから送られてきた値を参照できる。
コンソールには直前の値が出ている。