プロジェクトの作成
| 名前 | DbSample |
| タイプ | Gradle-Groovy |
| パッケージング | Jar |
| Java バージョン | 21 |
| 言語 | Java |
依存関係で選択する項目は以下を選択します。
- Spring Boot DevTools
- Lombok
- MyBatis Framework
- H2 Database
- Thymeleaf
- Spring Web
application.properties
src/main/resourcesフォルダの配下にapplication.propertiesファイルはアプリケーションの設定やプロパティを設定するファイルです。
src/main/resources/application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.dirver-class-name=org.h2.Driver
spring.datasource.username=user
spring.datasource.password=
spring.h2.console.enabled=true schema.sql と data.sql
特定の名前をもつ SQLファイルをsrc/main/resourcesフォルダ配下に配置するとアプリケーションの起動時に自動的にデータベースの初期化綾データの投入を行うことができます。
- schema.sql(データベースの構造に関連するSQL文を記述)
- data.sql(データ登録などの初期データのセットアップに関連するSQL文を記述)
H2などの組み込みデータベースを使用する場合は、自動初期化の設定は有効になっています。
schema.sql
CREATE TABLE books (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
author VARCHAR(255) NOT NULL
); data.sql
INSERT INTO books (title, author) VALUES ('book A', '太郎');
INSERT INTO books (title, author) VALUES ('book B', '花子');
INSERT INTO books (title, author) VALUES ('book C', '次郎'); H2 コンソールの起動
H2 コンソールを起動するには、まずアプリケーションを起動します。ブラウザからhttp://localhost:8080/h2-consoleを入力し起動します。

「接続」をクリックすると、管理画面が表示されます。

エンティティの作成
データベースの1レコード = 1オブジェクト として扱う必要があり、その役割を担うのが エンティティ です。
- entity(エンティティ):実在物
src/main/javaフォルダに以下のクラスを作成します。
| パッケージ | com.example.demo.entity |
| 名前 | Book |
Book.java
package com.example.demo.entity;
import lombok.Data;
@Data
public class Book {
private int id;
private String title;
private String author;
}
マッパーインターフェースの作成
src/main/javaフォルダにインターフェースを作成します。
| パッケージ名 | com.example.demo.mapper |
| 名前 | BookMapper |
BookMapper.java
package com.example.demo.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.entity.Book;
@Mapper
public interface BookMapper {
List<Book> getAllBooks();
Book getBookById(int id);
void insertBook(Book book);
void updateBook(Book book);
void deleteBookById(int id);
} マッパーファイルの作成
マッパーファイルとは、SQL と Java のエンティティや Mapperインターフェースを結びつけるための設定ファイル です。
xmlファイルを開き、以下のような表示だとデザインモードです。デザインモードとソースモードを赤色で囲っているタブで切り替えることができます。

ソースモードにした場合、以下のような表示になり、xmlファイルを編集することができます。
BookMapper.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.mapper.BookMapper">
<!-- 【SELECT】全ての書籍を取得するためのSQLを定義 -->
<select id="getAllBooks" resultType="com.example.demo.entity.Book">
SELECT id, title, author FROM books ORDER BY id
</select>
<!-- 【SELECT】特定のIDを持つ書籍を取得するためのSQLを定義 -->
<select id="getBookById" resultType="com.example.demo.entity.Book">
SELECT id, title, author FROM books WHERE id = #{id}
</select>
<!-- 【INSERT】新しい書籍をデータベースに追加するSQLを定義 -->
<insert id="insertBook" parameterType="com.example.demo.entity.Book">
INSERT INTO books (title, author) VALUES (#{title}, #{author})
</insert>
<!-- 【UPDATE】特定のIDを持つ書籍の情報を更新するSQLを定義 -->
<update id="updateBook" parameterType="com.example.demo.entity.Book">
UPDATE books SET title = #{title}, author = #{author} WHERE id = #{id}
</update>
<!-- 【DELETE】特定のIDを持つ書籍の情報を削除するSQLを定義 -->
<delete id="deleteBookById" parameterType="int">
DELETE FROM books WHERE id = #{id}
</delete>
</mapper> コントローラ
BookController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.demo.entity.Book;
import com.example.demo.mapper.BookMapper;
import lombok.RequiredArgsConstructor;
@Controller
@RequestMapping("/books")
@RequiredArgsConstructor
public class BookController {
// DI
private final BookMapper bookMapper;
// メニュー画面を表示する
@GetMapping
public String showIndex(Model model) {
model.addAttribute("books", bookMapper.getAllBooks());
return "book/index";
}
// 特定のIDを持つ書籍を取得する
@GetMapping("/{id}")
public String detail(@PathVariable("id") int id, Model model, RedirectAttributes redirectAttributes) {
Book book = bookMapper.getBookById(id);
if (book == null) {
redirectAttributes.addFlashAttribute("message", "指定の書籍が存在しません");
return "redirect:/books";
}
model.addAttribute("book", book);
return "book/detail";
}
// create登録処理(GET)
@GetMapping("/create")
public String showCreate(Model model) {
model.addAttribute("book", new Book());
return "book/create";
}
// create登録処理(POST)
@PostMapping
public String create(@RequestParam("title") String title, @RequestParam("author") String author, RedirectAttributes redirectAttributes) {
Book book = new Book();
book.setTitle(title);
book.setAuthor(author);
bookMapper.insertBook(book);
redirectAttributes.addFlashAttribute("message", "新規登録しました");
return "redirect:/books";
}
// 更新画面を表示(GET)
@GetMapping("/update/{id}")
public String showUpdate(@PathVariable("id") int id, Model model, RedirectAttributes redirectAttributes) {
Book book = bookMapper.getBookById(id);
if (book == null) {
redirectAttributes.addFlashAttribute("message", "指定の書籍が存在しません");
return "book/index";
}
model.addAttribute("book", book);
return "book/update";
}
// 更新処理(POST)
@PostMapping("/update")
public String update(@RequestParam("id") int id,
@RequestParam("title") String title,
@RequestParam("author") String author,
RedirectAttributes redirectAttributes) {
Book book = bookMapper.getBookById(id);
if (book == null) {
redirectAttributes.addFlashAttribute("message", "指定の書籍が存在しません");
return "redirect:/books";
}
book.setTitle(title);
book.setAuthor(author);
bookMapper.updateBook(book);
redirectAttributes.addFlashAttribute("message", "更新しました");
return "redirect:/books";
}
@GetMapping("/delete/{id}")
public String showDelete(@PathVariable("id") int id, Model model, RedirectAttributes redirectAttributes) {
Book book = bookMapper.getBookById(id);
if (book == null) {
redirectAttributes.addFlashAttribute("message", "指定の書籍が存在しません");
return "book/index";
}
model.addAttribute("book", book);
return "book/delete";
}
@PostMapping("/delete")
public String delete(@RequestParam("id") int id, RedirectAttributes redirectAttributes) {
Book book = bookMapper.getBookById(id);
if (book == null) {
redirectAttributes.addFlashAttribute("message", "指定の書籍が存在しません");
return "redirect:/books";
}
bookMapper.deleteBookById(id);
redirectAttributes.addFlashAttribute("message", "削除しました");
return "redirect:/books";
}
} @RequiredArgsConstructor
@RequiredArgsConstructor は Lombok のアノテーションで、final が付いたフィールド または @NonNull が付いたフィールド を対象にしたコンストラクタを自動生成するためのものです。
@RequestMapping
@RequestMapping は Spring MVC / Spring Boot のコントローラで使われるアノテーションで、クラスに付けると共通URLになります。
@Controller
@Controller は、HTMLなどのビューを返すWebコントローラであることをSpringに知らせるためのアノテーションです。
ビュー
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>メニュー画面</title>
</head>
<body>
<h1>書籍管理メニュー</h1>
<p th:if="${message}" th:text="${message}"></p>
<ul>
<li><a th:href="@{/books/create}">新しい書籍を作成</a></li>
</ul>
<table th:if="${books}" border="1">
<thead>
<tr>
<th>ID</th>
<th>タイトル</th>
<th>著者</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="book : ${books}">
<td th:text="${book.id}">ID</td>
<td th:text="${book.title}">タイトル</td>
<td th:text="${book.author}">著者</td>
<td>
<a th:href="@{/books/{id}(id=${book.id})}">詳細</a>
<a th:href="@{/books/update/{id}(id=${book.id})}">編集</a>
<a th:href="@{/books/delete/{id}(id=${book.id})}">削除</a>
</td>
</tr>
</tbody>
</table>
</body>
</html> detail.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<h2 th:text="${message}">メッセージ</h2>
<h3>書籍の詳細</h3>
<table border="1">
<tr>
<th>ID</th>
<td th:text="${book.id}">1</td>
</tr>
<tr>
<th>タイトル</th>
<td th:text="${book.title}">タイトル</td>
</tr>
<tr>
<th>著者</th>
<td th:text="${book.author}">著者</td>
</tr>
</table>
<p><a th:href="@{/books}">一覧へ戻る</a></p>
</body>
</html> create.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>書籍登録</title>
</head>
<body>
<h2>書籍登録</h2>
<form th:action="@{/books}" th:object="${book}" method="post">
<label>タイトル:</label>
<input type="text" th:field="*{title}" /><br/>
<label>著者:</label>
<input type="text" th:field="*{author}" /><br/>
<button type="submit">登録</button>
</form>
<p><a th:href="@{/books}">一覧へ戻る</a></p>
</body>
</html> update.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>書籍更新</title>
</head>
<body>
<h2>書籍更新</h2>
<form th:action="@{/books/update}" th:object="${book}" method="post">
<!-- IDはhiddenで渡す -->
<input type="hidden" th:field="*{id}">
<label>タイトル:</label>
<input type="text" th:field="*{title}"><br>
<label>著者:</label>
<input type="text" th:field="*{author}"><br>
<button type="submit">更新</button>
</form>
<p><a th:href="@{/books}">一覧へ戻る</a></p>
</body>
</html> delete.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<h1>削除</h1>
<table border="1">
<tr>
<th>ID</th>
<td th:text="${book.id}">1</td>
</tr>
<tr>
<th>タイトル</th>
<td th:text="${book.title}">タイトル</td>
</tr>
<tr>
<th>著者</th>
<td th:text="${book.author}">著者</td>
</tr>
</table>
<form th:action="@{/books/delete}" method="post">
<input type="hidden" name="id" th:value="${book.id}" />
<button type="submit">削除する</button>
</form>
<p><a th:href="@{/books}">一覧へ戻る</a></p>
</body>
</html>