以前に投稿させていただいた記事の続き的なものです。
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)
ディレクトリ構成
.
├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