メモ > 技術 > フレームワーク: SpringBoot > プログラムの整理
プログラムの整理
■EntityのGetter/Setterを自動作成
【Spring Boot】Lombokの導入
https://b1san-blog.com/post/spring/spring-lombok/
SpringBootのプロジェクトにlombok導入する - Qiita
https://qiita.com/kihara-takahiro/items/f616d01be6bf7384dbfc
pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
src/main/java/com/example/demo/entity/Task.java
package com.example.demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import lombok.Data;
@Entity
@Table(name="tasks")
@Data
public class Task {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String title;
private String text;
}
lombok.Dataを使用すると、初回実行時に「アノテーションを有効にするか」を確認されるみたい
■フォームからの値取得とバリデーション
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
src/main/java/com/example/demo/request/TaskRequest.java
package com.example.demo.request;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
@Data
public class TaskRequest implements Serializable {
@NotEmpty(message = "タイトルを入力してください。")
@Size(max = 80, message = "タイトルは80文字以内で入力してください。")
private String title;
@NotEmpty(message = "テキストを入力してください。")
@Size(max = 80, message = "テキストは80文字以内で入力してください。")
private String text;
}
src/main/java/com/example/demo/service/TaskService.java
public void create(TaskRequest taskRequest) {
Task task = new Task();
task.setId(null);
task.setTitle(taskRequest.getTitle());
task.setText(taskRequest.getText());
taskRepository.save(task);
}
src/main/java/com/example/demo/controller/TaskController.java
@GetMapping(value = "/task/add")
String add(Model model) {
model.addAttribute("taskRequest", new Task());
return "task/add";
}
@PostMapping(value = "/task/create")
String create(@Validated @ModelAttribute TaskRequest taskRequest, BindingResult result) {
if (result.hasErrors()) {
return "task/add";
}
taskService.create(taskRequest);
return "redirect:/task/";
}
src/main/resources/templates/task/add.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Spring Boot</title>
</head>
<body>
<h1>Spring Boot</h1>
<p>タスクを登録します。</p>
<form th:action="@{/task/create}" th:object="${taskRequest}" method="post">
<dl>
<dt>タイトル</dt>
<dd><input type="text" th:field="*{title}"><div th:if="${#fields.hasErrors('title')}" th:errors="*{title}" class="error">Title Error</div></dd>
<dt>テキスト</dt>
<dd><input type="text" th:field="*{text}"><div th:if="${#fields.hasErrors('text')}" th:errors="*{text}" class="error">Text Error</div></dd>
</dl>
<p><input type="submit" value="登録"></p>
</form>
</body>
</html>
「@{}」はURLパスを生成するもの
上の場合は「action="/task/create"」としても同じだが、パラメータを埋め込む場合はシンプルに書くことができる
「th:object」は、その要素内で使用するオブジェクトを設定できる
このオブジェクトからは「*{プロパティ名}」で値を取得できる
ここでは「*{title}」は「taskRequest.getTitle()」と解釈される
「#fields.hasErrors('プロパティ名')」でエラーの有無をチェックできる
「th:errors」はエラーメッセージを取得する。「*{プロパティ名}」の形式で、どのプロパティに対するエラーメッセージなのかを指定できる
http://localhost:8080/task/add にアクセスすると、タスクの登録画面が表示される
http://localhost:8080/task/ にアクセスすると、タスクが一覧表示される
バリデーションは「登録画面用」「編集画面用」など画面ごとに用意する方がいいかと思ったが、
以下によるとバリデーションのグループ化によって「登録時だけ実行するバリデーション」といった制御ができるみたい
【Spring Boot】バリデーション
https://b1san-blog.com/post/spring/spring-validation/
■共通レイアウトの作成
pom.xml
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
src/main/resources/templates/layout/frontend.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>Demo</title>
</head>
<body>
<header th:fragment="header">
<h1>Demo</h1>
</header>
<th:block layout:fragment="content"></th:block>
<footer th:fragment="footer">
<p><small>This is demo site.</small></p>
</footer>
</body>
</html>
src/main/resources/templates/home/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/frontend}">
<head>
<title>Demo</title>
</head>
<body>
<th:block layout:fragment="content">
<main>
<p>ホーム。</p>
</main>
</th:block>
</body>
</html>
src/main/resources/templates/home/now.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/frontend}">
<head>
<title>現在時刻 | Demo</title>
</head>
<body>
<th:block layout:fragment="content">
<main>
<p>現在時刻は<span th:text="${time}"></span>です。</p>
</main>
</th:block>
</body>
</html>
src/main/resources/templates/task/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/frontend}">
<head>
<title>タスク | Demo</title>
</head>
<body>
<th:block layout:fragment="content">
<main>
<p>タスクは以下のとおりです。</p>
<table>
<tr>
<th>ID</th>
<th>タイトル</th>
<th>テキスト</th>
</tr>
<tr th:each="task : ${tasks}">
<td th:text="${task.id}"></td>
<td th:text="${task.title}"></td>
<td th:text="${task.text}"></td>
</tr>
</table>
</main>
</th:block>
</body>
</html>
src/main/resources/templates/task/add.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/frontend}">
<head>
<title>タスク登録 | Demo</title>
</head>
<body>
<th:block layout:fragment="content">
<main>
<p>タスクを登録します。</p>
<form th:action="@{/task/create}" th:object="${taskRequest}" method="post">
<dl>
<dt>タイトル</dt>
<dd><input type="text" th:field="*{title}"><div th:if="${#fields.hasErrors('title')}" th:errors="*{title}" class="error">Title Error</div></dd>
<dt>テキスト</dt>
<dd><input type="text" th:field="*{text}"><div th:if="${#fields.hasErrors('text')}" th:errors="*{text}" class="error">Text Error</div></dd>
</dl>
<p><input type="submit" value="登録"></p>
</form>
</main>
</th:block>
</body>
</html>
共通レイアウトの詳細は、後述の「テンプレート(Thymeleaf)」を参照