LoginSignup
11
20

More than 3 years have passed since last update.

SpringBoot + MyBatis + Thymeleaf + MySQL で超簡潔なCRUD処理

Last updated at Posted at 2021-04-15

以前に投稿させていただいた記事の続き的なものです。
CRUD処理全て実装できたので、自分用のアウトプットとして投稿させていただきます。
記載が稚拙だったり誤っている箇所等あるかもしれませんがご容赦ください。
誤りについてはご指摘いただけますと大変幸いですm(_ _)m。

環境

Spring Boot 2.4.4
Java 11
OS  : macOS Big Sur
IDE : SpringToolSuite4
Maven

目標

SQLでテーブルusers(カラムはid, name, age)を作成し、以下の4つの処理ができるアプリを作成。
新規ユーザー登録(C)
ユーザー情報表示(R)
ユーザー情報更新(U)
ユーザー情報削除(D)

CRUD処理.gif

ディレクトリ構成

.
├src/main/java/com/example/demo/
│                 ├controller / UController.java
│                 ├model / User.java
│                 ├repository / UMapper.java
│                 ├service / UService.java
│                 └MyBatisPracticeApplication.java
└ src/main/resources/
         ├mapper / UMapper.xml
         ├templates / users
         │         ├change.html
         │         ├details.html
         │         ├list.html
         │         ├register.html
         │         └top.html
         ├application.properties
         ├data.sql
         └schema.sql

各ソースコード

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/[任意のDB名を記載してください]?serverTimezone=Asia/Tokyo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=[MySQLのパスワードを記載してください]

spring.datasource.sql-script-encoding=UTF-8
spring.datasource.initialization-mode=always

spring.datasource.schema=classpath:schema.sql
spring.datasource.data=classpath:data.sql

#MyBatis
mybatis.mapper-locations=classpath*:/mapper/*.xml

#Log Level
logging.level.com.example=debug

MySQLの利用に必要な記載と、アプリ実行時に実行するSQLファイルの記載です。
#MyBatis…読み込むxmlファイルの場所を記載してください。
#LogLevel…コンソールにSQLの処理を出力してくれます(無くてもOKです)。

schema.sql

CREATE TABLE IF NOT EXISTS users (
    id VARCHAR(50) PRIMARY KEY,
    name VARCHAR(50),
    age INT
    );

作成するテーブルの内容を記載します。

data.sql

INSERT IGNORE INTO users (id, name, age)
    values
        ('1', 'Tom', 30),
        ('2', 'Mike', 31);

usersテーブルに予めINSERTしておくユーザー情報を記載します。

list.html

<!DOCTYPE html>
<html
    xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>
    <title>ユーザーList</title>
</head>
<body>
    <table border=1>
    <tr>
        <th>ID</th>
        <th>名前</th>
        <th>年齢</th>
    </tr>
    <tr th:each="u:${users}">
        <td th:text="${u.id}"></td>
        <td th:text="${u.name}"></td>
        <td th:text="${u.age}"></td>
    </tr>
    </table>
</body>
</html>

http://localhost:8080/users/list
で全ユーザー情報を表示する画面です。
私が初めに作成したselect全件処理時のファイルを残しているだけですので、無くても大丈夫です。

top.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー一覧</title>
</head>
<body>

    <h1>ユーザー一覧(top.html)</h1>

    <div th:if="!${users.size()}">
        <p>登録されているユーザーはいません</p>
    </div>

    <a th:href="@{/users/register}"><!-- URL「/users/register」生成 -->
        <button>新しいユーザーを登録</button>
    </a>

    <table th:if="${users.size()}" border=1>
    <!-- DB内のデータがtrue = 0以外の時 -->

        <thead>
            <tr>
                <th>ID(PRIMARY KEY)</th>
                <th>名前</th>
                <th>年齢</th>
                <th></th>
                <th></th>
                <th></th>
            </tr>
        </thead>

        <tbody>
            <tr th:each="users:${users}" th:object="${users}">
                <td th:text="*{id}"></td>
                <td th:text="*{name}"></td>
                <td th:text="*{age}"></td>
                <td><a th:href="@{/users/details/id={id}(id=*{id})}"><button>詳細</button></a></td>
                <td><a th:href="@{/users/change/id={id}(id=*{id})}"><button>変更</button></a></td>
                <td>
                    <form th:method="post" th:action="@{/users/delete/id={id}(id=*{id})}"><button>削除</button></a>
                    </form></td>
            </tr>
        </tbody>

    </table>

</body>

http://localhost:8080/users
で表示されるトップページです。
全ユーザー情報を表示し、各ボタンの機能は以下の通りです。
[新しいユーザーを登録]ボタン→register.htmlへ遷移
[詳細]ボタン→details.htmlへ遷移
[変更]ボタン→change.htmlへ遷移
[削除]ボタン→当該ユーザー情報を削除

details.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー詳細</title>
</head>

<body>

    <h2>ユーザー詳細表示(details.html)</h2>
    <div th:object="${users}">
        <table border="1">
            <tr>
                <th>ID</th>
                <th>名前</th>
                <th>年齢</th>
            </tr>
            <tr>
                <td th:text="*{id}"></td>
                <td th:text="*{name}"></td>
                <td th:text="*{age}"></td>
            </tr>

</body>
</html>

register.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>新規ユーザー登録</title>
</head>

<body>

    <h2>新規ユーザー登録(register.html)</h2>

    <form th:method="post" th:object="${users}" th:action="@{/users/register}">
        <label>ID(VARCHAR):<input type="text" th:field="*{id}"></label><br>
        <label>名前(VARCHAR):<input type="text" th:field="*{name}"></label><br>
        <label>年齢(INT):<input type="text" th:field="*{age}"></label><br>

        <button>新規作成</button>
    </form>
</body>

当初はth:fieldの部分を単なるname属性で記載していてそれでも動いたのですが、th:fieldで動かして見たくて書き換えました。
その時以下のエラーが出て悩んでおりました。

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name ‘users’ available as request attribute

その後th:objectについて調べ、ControllerクラスにModelの記載を追加して解決しました。
(参考にさせていただいたteratailの質問:https://teratail.com/questions/26218)

change.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー変更</title>
</head>

<body>

    <h2>ユーザー変更(change.html)</h2>

    <form th:method="post" th:object="${users}" th:action="@{/users/change/id={id}(id=*{id})}">
        <label>ID(VARCHAR):<input type="text" th:field="*{id}"></label><br>
        <label>名前(VARCHAR):<input type="text" th:field="*{name}"></label><br>
        <label>年齢(INT):<input type="text" th:field="*{age}"></label><br>
        <button>変更</button>
    </form>

</body>
</html>

User.java

package com.example.demo.model;

import lombok.Data;

@Data
public class User {
    private String id;
    private String name;
    private int age;
}

エンティティクラスです。

UMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

  <mapper namespace="com.example.demo.repository.UMapper">

  <resultMap type="com.example.demo.model.User" id="user"><!--  id属性は任意の値 -->
    <!--  columnはSQLのカラム propertyはエンティティクラスのフィールド -->
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
  </resultMap>

  <select id="findOne" resultMap="user">
    select  *
    from    users
    where   id = #{id}
  </select>

  <select id="find" resultType="com.example.demo.model.User">
    select  *
    from    users
  </select>

  <insert id="insertOne">
    insert into users (
        id,
        name,
        age
    )
    values (
        #{id},
        #{name},
        #{age}
    )
  </insert>

  <update id="updateOne">
    update  users
    set     name = #{name},
            age = #{age}
    where   id = #{id}
  </update>

  <delete id="deleteOne">
        delete from
        users
    where
        id = #{id}  
  </delete>

  </mapper>

UMapper.java

package com.example.demo.repository;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.example.demo.model.User;


@Mapper
public interface UMapper {

    //select1件
    public User findOne(String id);

    //select全件
    public List<User> find();

    //insert
    public void insertOne(User u);

    //update
    public void updateOne(@Param("id") String id, @Param("name") String name, @Param("age") int age);

    //delete
    public void deleteOne(User u);

}

UService.java

package com.example.demo.service;

import java.util.List;

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

import com.example.demo.model.User;
import com.example.demo.repository.UMapper;


@Service
public class UService {

    @Autowired
    UMapper mapper;

    //select1件
    public User getUserOne(String id) {
        return mapper.findOne(id);
    }
    //select全件
    public List<User> getList() {
        return mapper.find();
    }

    //insert
    public void insertOne(User u) {
        mapper.insertOne(u);
    }

    //update
    public void updateOne(String id, String name, int age) {
        mapper.updateOne(id, name, age);
    }

    //delete
    public void deleteOne(User u) {
        mapper.deleteOne(u);
    }

}

UController.java

package com.example.demo.controller;

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.validation.annotation.Validated;
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;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.model.User;
import com.example.demo.service.UService;

import java.util.List;


@Controller
@RequestMapping("/users")
public class UController {

    @Autowired
    private UService service;

    //select全件表示
    @GetMapping("/list")
    public String getUserList(Model model) {
        List<User> userList =  service.getList();
        model.addAttribute("users", userList);
        return "users/list";
    }   


    //トップページ top.html表示
    @GetMapping("")
    public String top(Model model, @ModelAttribute User u) {
        model.addAttribute("users", service.getList()) ;
        return "users/top";
    }


    //top→[詳細]押下 select1件
    @GetMapping("details/id={id}")
    public String details(@PathVariable("id") String id, Model model) {
        model.addAttribute("users", service.getUserOne(id));
        return "users/details";
    }


    //top→[新規作成]押下 th:hrefにより生成されたURLをGETで表示
    @GetMapping("/register")
    public String registerUser(Model model, @ModelAttribute User u) {
        model.addAttribute("users", u);
        return "users/register";
    }
    //register.html内の <form method="post"> で↓へ飛ぶ
    @PostMapping("/register")
    public String create(@Validated @ModelAttribute User u, BindingResult result) {
        if (result.hasErrors()) {
            return "users/register";
        }
        service.insertOne(u);
        return "redirect:/users";
    }


    //top→[変更]押下時にchange.htmlを表示するGET
    @GetMapping("change/id={id}")
    public String change(@PathVariable("id") String id, Model model) {
        model.addAttribute("users", service.getUserOne(id));
        return "users/change";
    }
    @PostMapping("change/id={id}")
    public String update(@ModelAttribute User u, Model model) {
        service.updateOne(u.getId(), u.getName(), u.getAge());
        return "redirect:/users";
    }


    //top→[削除]押下時
    @PostMapping("delete/id={id}")
    public String delete(@PathVariable String id, @ModelAttribute User u) {
        service.deleteOne(u);
        return "redirect:/users";
    }

}

あとはブラウザに( http://www.localhost:8080/hello/users )
と入力すればtopページが表示されるはずです。

お疲れ様でした!

参考にさせていただいたQiita記事

前回から引き続き、下記記事を参考にさせていただきました。
ありがとうございました!
https://qiita.com/sumichan/items/bdc2a0e909416ae55162

11
20
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
11
20