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

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

春だから 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 を生成・発行してデータベース側にテーブルを作ってくれる。

詳しい解説はこちら。

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html#howto-initialize-a-database-using-hibernate

続きを読む

春だから 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=これはサンプルで用意したメッセージです。

実際には STS で作業すると自動でエンコード*1されて

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 が使われる。

*1:自動でエンコードされない環境で開発している場合は native2ascii で変換する

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

Gradle を使ってみる

前回作ったものを

  • ビルドツールとして Maven ではなく Gradle を使ってみる
  • データベースは PostgreSQL を使ってみる

で作り直してみる。

プラグインのインストール

Gradle によるビルドを行うためには STSプラグインを入れる必要がある。

http://download.eclipse.org/buildship/updates/e47/releases/2.x

からプラグインをインストール。

スタータープロジェクトの作成

スクリーンショットではプロジェクト名が「SpringWebApp-1」になっているが、実際は「SpringWebApp」で作っています。

f:id:redcat_prog:20180415131217p:plain

f:id:redcat_prog:20180415131218p:plain

ちゃっかり 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 でフォームから送られてきた値を参照できる。

f:id:redcat_prog:20180408192537p:plain

f:id:redcat_prog:20180408192535p:plain

コンソールには直前の値が出ている。

f:id:redcat_prog:20180408192536p:plain