SpringBoot + MySQL でTodoリストを作る！CRUDの実装(Create編)

#はじめに

前回は、DBから取得したデータを画面に表示させるところまで実装しました。

![スクリーンショット 2021-03-13 11.27.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/9783d90d-4cf3-95d7-9f51-5921892caabe.png)


今回は入力フォームと送信ボタンを用意して、**「送信ボタンが押された時、フォームで入力された値をMySQLに登録する」**をゴールとしています。

SpringBootを学び始めた方、復習をしたい方に向けて学んだ事を共有します。



前回の[Read編はこちら]()。


##開発環境

```
OS: macOS Mojave(バージョン 10.14.6)
```

```
$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
```

```
$ mvn -version
Maven home: /usr/local/Cellar/maven/3.6.3_1/libexec
Java version: 15.0.2, vendor: N/A, runtime: /usr/local/Cellar/openjdk/15.0.2/libexec/openjdk.jdk/Contents/Home
Default locale: ja_JP, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"
```

```
$ spring --version
Spring CLI v2.3.1.RELEASE
```

```
$ mysql --version
mysql  Ver 8.0.23 for osx10.14 on x86_64 (Homebrew)
```



```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>todolist</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>todolist</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	
	<dependencies>

		<!-- テンプレートエンジン -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- RESTfulを含んだWebアプリケーションライブラリの利用 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<!-- ファイルの変更を検知すると自動で再起動する -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>

		<!-- MySQL -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<!-- @Dataアノテーションを付与する事で,getter,setter等を自動生成してくれる -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<!-- テスト用 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!-- DBとJavaオブジェクトやり取りを行う。 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

```

#依存関係の編集(pom.xml)

フォームで入力値の検証をしたいため、pom.xmlに新たに依存関係を追加します。

```xml:pom.xml
<!-- バリデーション用 -->
<dependency>
	<groupId>org.springframework.boot</groupId>	
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
```

#バリデーションの設定

Todo.javaに`@NotNull`、`@NotBlank`というアノテーションを追加します。

**null**と**空白**がDBに登録出来ないようにするためです。

```java:Todo.java
package com.example.todolist.Model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

import lombok.Data;

// データベースから取得したデータを格納するクラス(登録、更新時の値を保持する)
// データベースの1行に対応した形になっている。
@Entity
@Data
@Table(name = "todolist")
public class Todo {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  // 追加
  @NotNull
  @NotBlank
  private String content;

  private boolean done;
}
```


#フォームを作成

新規登録用のフォームを作ります。

`th:object`はTodoオブジェクトをControllerから受け取っています。

`th:field`は、th:objectで受け取ったTodoオブジェクトの変数を指しています。

`th:errors`は、フォームの入力値にエラーがあった場合に表示させるエラーメッセージです。


```html:home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" type="text/css" th:href="@{css/home.css}">
  <title>SpringTodoList</title>
</head>
<body>

  <!-- 追加 -->
  <div class="create-todo">
    <!-- th:objectでControllerから送られてきたTodoオブジェクトを受け取っている -->
    <form method="POST" th:action="@{/}" th:object="${todo}">
      <!-- 入力値にエラーがあった時のみ表示 -->
      <p th:errors="*{content}" class="todo-error-message"></p>

       <!-- th:fieldでHTMLのid,name属性を作ってくれる -->
      <input type="text" th:field="*{content}">
      <button type="submit">登録</button>
    </form>
  </div>
  <!-- ここまで -->

  <h1>やること</h1>
  <div class="incomplete" th:each="todo : ${allTodo}">
    <p th:text="${todo.content}"></p>
  </div>

</body>

</html>
```



#Serviceにメソッドを追加する


DBに新規登録するためにメソッドを追加します。

`JpaRepository`には**saveメソッド**が用意されており、引数で受け取ったエンティティを保存します。
`TodoRepository`は`JpaRepository`を継承しているので使えます。

```java:TodoService.java
package com.example.todolist.Service;

import java.util.List;

import com.example.todolist.Model.Todo;
import com.example.todolist.Repository.TodoRepository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

// DBとの具体的な処理(データの取得、新規作成など)を記述するクラス
@Service
public class TodoService {
  
  @Autowired
  TodoRepository todoRepository;

  public List<Todo> searchAll() {
    return todoRepository.findAll();
  }

  // 追加(Todoを新たにDBに登録する)
  public void addTodo(Todo todo) {
    todoRepository.save(todo);
  }
}
```

#Controllerでフォームから値を受け取る

登録ボタンが押された時にDBへ登録するためのメソッドを追加します。
`@PostMapping("/")`は先ほどhtmlのformに付与した、`th:action="@{/}"`と結びついています。


`@Valid`は入力されたデータの検証をするためのアノテーションで、`BindingResult`は、エラーメッセージを格納するクラスです。

**bindingResult.hasErrors()**でエラーの有無で処理を分けています。

```java:TodoController.java
package com.example.todolist.Controller;

import java.util.List;

import javax.validation.Valid;

import com.example.todolist.Model.Todo;
import com.example.todolist.Service.TodoService;

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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;


@Controller
public class TodoController {

  @Autowired
  TodoService todoService;
  
  @GetMapping("/")
  public String home(Model model) {
    List<Todo> allTodo = todoService.searchAll();
    model.addAttribute("allTodo", allTodo);
    model.addAttribute("todo", new Todo()); // 追加：新しいTodoオブジェクトをviewに引き渡す(th:objectで受け取る)
    return "home";
  }

  // 追加(formのth:actionと結びついている)
  @PostMapping("/")
  public String createTodo(@Valid Todo todo, BindingResult bindingResult,Model model) {
    // 入力値にエラーがあった時
    if(bindingResult.hasErrors()) {
      List<Todo> allTodo = todoService.searchAll();
      model.addAttribute("allTodo", allTodo);
      model.addAttribute("todo", todo);
      return "home";
    }

    // エラーがなかった時は、Todoを新規登録する
    todoService.addTodo(todo);
    return "redirect:/";
  }  
}
```

#動作確認

**localhost:8080**にアクセスすると、フォームと登録ボタンが表示されているはずです。(CSSは勝手に記述しました)

![スクリーンショット 2021-03-16 13.06.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/b4e1c95d-885d-db73-4ea8-3befd1b50ef7.png)


適当に文字を入力して...

![スクリーンショット 2021-03-16 13.10.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/9509bf14-a2a2-f4ec-e1c0-94b43670482e.png)

登録ボタンを押下すると...

![スクリーンショット 2021-03-16 13.10.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/4b1d2e72-4ce0-13e6-3f5a-5d70d53c2d35.png)


新規登録され、画面に表示されました！！！

また、**何も入力せずにorスペースだけ入力して**登録ボタンを押すと...

![スクリーンショット 2021-03-16 13.11.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555244/070e4f16-fa87-2afd-b742-eb8ca920a729.png)

エラーメッセージが表示され、DBには登録されていません。

#終わりに

SpringBootでサクッと入力値の検証、エラーメッセージの表示、フォームの値をDBに新規登録することが出来ました。


次は更新機能の実装をしてみたいと思います。