34
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Spring Bootで簡単なWebアプリを作ってみたい!【CRUD処理を実装】

Last updated at Posted at 2021-12-16

【はじめに】

この記事は【マイスター・ギルド】本物の Advent Calendar 2021の12日目の記事です。
ためになる技術的な記事から、猫ちゃんに癒やされる記事、健康オタクになれる記事まで、バラエティに富んだ記事が投稿されているので、ぜひチェックしてみてください!

【経緯】

こちらの記事は前記事のSpring Bootで簡単なWebアプリを作ってみたい!【準備からGet・Post表示まで】の続きになります。
Javaの入門書である「スッキリわかるJava入門」を読み終わったので、なにかWebアプリを作ってみたいと思ったのがきっかけです。
ちょうど業務でSpring Bootが使われていたので勉強を始めたのですが、チュートリアルが少なく苦労しているので、備忘録的にまとめようと思っています。
この記事だけで、CRUDの実装はできるようになっていますが、前記事で説明した内容は省略しています。
不明点があった場合は前記事も参照していただければと思います。

【対象読者】

Javaの入門書を読み終わって、これからWebアプリを作ってみたいと思っているひと向けです。
説明がわかりにくかったり、間違っている部分があれば、コメントいただければありがたいです。 

【この記事でやること】

ToDoアプリを題材にSpring BootでCRUD処理を実装します。

■テストアプリケーションのイメージ

最小限の機能でToDoアプリを作成します。
下記に動作イメージを示します。
Videotogif.gif

【開発環境】

  • OS: Windows 10 Pro
  • IDE: IntelliJ IDEA 2021.2.2(Community Edition)
  • Java: openjdk 11.0.2 2019-01-15
  • Spring Boot: 2.6.1
  • MySQL: 8.0.26

【準備編】

■MySQLのインストール

下記を参考にMySQLのインストールとユーザー登録、動作確認までします。
サイトに詳しく手順が書いてあるので、ここでは説明を省きます。
ユーザー名とパスワードは後ほど使うので、メモしておきます。

【実装編】

■ToDoアプリを作成して、CRUDしてみよう(MySQL)

- Spring initializrでプロジェクトを作成

今回もSpring initializrでプロジェクトを用意します。
設定を下記のようにして、GENERATEします。
image.png

  • Dependencies
    • Lombok : アノテーションを付けるだけで、GetterやSetterを自動で生成してくれるライブラリ
    • Spring Data JPA : 簡単にDBアクセスができるライブラリ
    • MySQL Driver : MySQLとの接続のためのドライバー(だと思われる)  

GENERATEしたzipを解凍してIDEで起動します。

- Modelの作成

src\main\javacom.example.SpringBootTodomodelパッケージを作成し、Todo.javaを作成します。

Todo.java
package com.example.SpringBootTodo.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Getter // フィールドのgetメソッド(id, content)を自動生成する
@Setter // フィールドのsetメソッド(id, content)を自動生成する
@Entity // データの入れ物であるEntityクラスであることを指定する
public class Todo {
  @Id // Entityの主キーを設定する
  @GeneratedValue // Entityの値を自動採番する
  private Long id;
  private String content;
}
  • モデルは、DBテーブルのカラム名をイメージしながらフィールドとそれぞれのGetter、Setterを用意します
    • Lombokを使えば、@Getter@Setterアノテーションを付けるだけで、自動生成してくれます
  • Entityクラスであること示すために@Entityアノテーションをつけます
    • Entityクラスでモデルを作成することで、クラス名と同じ名前のテーブルをMySQLに作成してくれます
  • idフィールドには@Id@GenerateValueアノテーションを付けておくと、主キー設定され、自動採番されるようになります

- Repositoryの作成

com.example.SpringBootTodorepositoryパッケージを作成し、TodoRepository.javaを作成します。

TodoRepository.java
package com.example.SpringBootTodo.repository;

import com.example.SpringBootTodo.model.Todo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository // Repositoryクラスであることを示す
public interface TodoRepository extends JpaRepository<Todo, Long> { 

}
  • Repositoryインターフェースを作成してJpaRepositoryを継承するだけで、DBのレコード取得や保存などの便利な機能を使えるようになります
    • JpaRepository<Entityクラス名, IDの型>で継承します
  • 各Entityクラス毎に作成します

- Controllerの作成

com.example.SpringBootTodocontrollerパッケージを作成し、TodoController.javaを作成します。

TodoController.java
package com.example.SpringBootTodo.controller;

import com.example.SpringBootTodo.model.Todo;
import com.example.SpringBootTodo.repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

@RequiredArgsConstructor
@Controller
public class TodoController {

  private final TodoRepository repository;

  //  Read
  @GetMapping("/")
  public String showAllTodo(@ModelAttribute Todo todo, Model model) {
    model.addAttribute("todos", repository.findAll()); 
    return "index";
  }

  //  Create
  @PostMapping("/add")
  public String addTodo(@ModelAttribute Todo todo) {
    repository.save(todo);
    return "redirect:/";
  }

  //  Update
  @PostMapping("/edit")
  public String editTodo(@ModelAttribute Todo todo) {
    repository.save(todo);
    return "redirect:/";
  }

  //  Delete
  @GetMapping("/delete/{id}")
  public String deleteTodo(@PathVariable Long id) { 
    repository.deleteById(id);
    return "redirect:/";
  }
}
  • Controllerクラスには、CRUDのそれぞれのメソッドを用意しています
  • フィールドには先程作成したRepositoryを用意します
  • @RequiredArgsConstructorアノテーションをつけることで、下記のようなコンストラクタインジェクションを自動生成してくれます
//省略
@Controller
public class TestController {
  
  private final TodoRepository repository;
  
  public TestController(TodoRepository repository){
    this.repository = repository;
  }
//省略
}

CRUDそれぞれのメソッドを説明します。

Read

  //  Read
  @GetMapping("/")
  public String showAllTodo(@ModelAttribute Todo todo, Model model) {
    model.addAttribute("todos", repository.findAll()); 
    return "index";
  }
  • @ModelAttribute Todo todoでJava側の変数todoとThymeleafの変数${todo}とを紐付けます
    • Thymeleafのコードはこの後に説明しているので、合わせて見ていただければと思います
  • repository.findAll()でDBのtodoテーブルからすべてのデータを取り出します

Create

  //  Create
  @PostMapping("/add")
  public String addTodo(@ModelAttribute Todo todo) {
    repository.save(todo); 
    return "redirect:/";
  }
  • リクエストで受け取った値をrepository.save(todo)でtodoテーブルに保存します
    • saveの引数にはEntity(ここではtodo)を指定します
  • return "redirect:/"は、redirect:のあとに指定したルートを再度呼び出します
    • 今回は"/"なので、再度Readの処理が実行されます

Update

  //  Update
  @PostMapping("/edit")
  public String editTodo(@ModelAttribute Todo todo) {
    repository.save(todo);
    return "redirect:/";
  }
  • Createと全く同じメソッドで実装可能です
    • なぜ同じメソッドで異なる処理が可能かというと、リクエストでidが送られてくるかどうかで挙動が異なるからです
    • Createの場合はEntity(ここでいうtodo)のメンバ変数idはnullで送られてくるため、バインドした際にidが自動採番されて新規に登録されます
    • Updateの場合はEntityのidが指定された状態で送られてくるため、そのidに対応するレコードの修正をするようになっています
    • 今回は説明のためにCreateとUpdateで別のメソッドを用意しましたが、Post先を同じにすれば一つのメソッドで実現可能です

Delete

  //  Delete
  @GetMapping("/delete/{id}")
  public String deleteTodo(@PathVariable Long id) {
    repository.deleteById(id);
    return "redirect:/";
  }
  • @GetMapping("/delete/{id}"){id}でURLにid値を設定することができます
  • @PathVariable Long idでURLの{id}の値が変数idに代入されます
  • repository.deleteById(id)で指定したid値のレコードを削除します
    • ここはEntityを指定するのではなくidを指定します
  • repositoryのメソッドはここで使ったもの以外にもあるので、興味がある方は公式リファレンスを参照ください

- Thymeleafテンプレートの作成

src\main\resources\templatesindex.htmlを作成します。

index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>ToDoApp</title>
</head>
<body>
<h2>ToDo新規追加</h2>
<!--Create-->
<form th:action="@{/add}" th:object="${todo}" method="post">
  <label for="content">ToDo:</label>
  <input type="text" th:field="*{content}">
  <button>登録</button>
</form>

<br>
<h2>ToDo一覧</h2>
<div th:if="${todos.size() == 0}">
  タスクがありません
</div>
<div th:if="${todos.size() > 0}">
  <!--Read & Edit-->
  <div th:each="todo : ${todos}"> <!--th:each 繰り返し処理をおこなう-->
    <form th:action="@{/edit}" th:object="${todo}" method="post" style="display: inline">
      <input type="hidden" name="id" th:value="*{id}">
      <label for="content">[[*{id}]] : </label>
      <input type="text" name="content" th:value="*{content}">
      <button>更新</button>
    </form>
    <!--Delete-->
    <a th:href="@{/delete/{id}(id=${todo.id})}">削除</a>
  </div>
</div>
</body>
</html>
  • th:objectでフォームにバインドするオブジェクトを指定します
    • 今回は、Controllerで指定したtodoを設定しています
  • th:fieldth:objectで指定したオブジェクトのフィールド名を指定します
  • th:ifでif文を作成することができます
  • th:eachで繰り返し処理を行うことができます。拡張for文のような記述をしています
    • ${todos}はControllerのReadのodel.addAttribute("todos", repository.findAll())で指定した"todos"と紐付いています
  • [[]]でモデルの値を表示させることができます
  • <a th:href="@{/delete/{id}(id=${todo.id})}">{id}todo.idを値を代入したリンクを作成できます

- DBの作成

MySQLのDBの作成をします。
下記サイトの「1.データベースの作成」を参照して、好きな名前でDBを作成します。
ここではDB名をspring_boot_todoとしました。

- propertiesの修正

src\main\resourcesapplication.propertiesを修正します。

application.properties
# DBの接続先情報
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_todo
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=create
  • jdbc:mysql://localhost:3306/spring_boot_todoの最後の部分は先程作成したDB名を指定します
  • spring.datasource.username=spring.datasource.password=はMySQLのインストールで作成したアカウントを指定します
  • spring.jpa.hibernate.ddl-auto=createはspring bootの起動のたびにDBをどうするかを指定します
    • createは実行の度に既存のテーブルを削除して新たに作成します

- 動作確認

プログラムを実行して、http://localhost:8080にアクセスすると下のような画面が表示されます。
image.png
新規追加のFormにToDoを入力します。
image.png
登録ボタンを押すと、ToDo一覧に先程の内容が追加されます。(Create & Read)
image.png
一覧のフォームの内容を修正して更新ボタンを押すと、内容が更新されます。(Update)
わかりにくいですが、ブラウザの更新ボタンを押しても更新後の内容が表示されていれば正しく動作しています。
image.png
ToDo一覧の削除リンクを押すと一覧から消えます。(Delete)
image.png

以上で実装完了です!

【まとめ】

今回はSpring BootでToDoアプリを題材にCRUD処理の実装を試してみました。

次回は今回実装したToDoアプリをRestAPIにしたいと思っています。
(フロント側の学習も必要になるので、いつ投稿できるかわかりませんが、ご期待ください。)

【参考文献】

34
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?