はじめに
Spring Boot のデータの流れをイメージで捉えるために、次のような骨組みだけのサンプルアプリを作って、データの受け渡しを一つ一つ確認していきます。
サンプルアプリには、Create(登録)、Read(閲覧)、Update(更新)、Delete(削除)の主要4機能を全て入れるようにしますが、本記事の趣旨とは関係のない余計なコードは排除して、最小限のコード量にします。
Service や Repository は作らず、データの処理は Controller のみで手短に済ませます。
そして、次のように簡略化した MVC のイメージ図に当てはめて、CRUD のそれぞれの機能を確認していきます(下図の詳細は後述)。
実際に動かして確認できるように、全てのソースコードを掲載してから、各機能におけるデータの流れを追っていきますので、ソースコードが不要な方は、そのあたりは適当に読み飛ばしてください。
使用する環境は次のとおりです。
項目 | 内容 |
---|---|
IDE | Spring Tool Suite4 |
OS | Windows 10 |
データベース | MySQL |
DB接続のAPI | JDBC API |
ビルドツール | Gradle |
学習のアウトプットとして書いている記事なので、記述内容に間違いなどがあるかもしれません。
お気づきのことなどあれば、ご指摘などいただけると幸いです。
<目次>
1. データベースの用意(MySQL)
2. プロジェクト作成時の設定内容
3. 作成するファイルとソースコード
4. Spring MVC のイメージ
5. 機能ごとのデータの流れのイメージ
1. データベースの用意(MySQL)
先に、MySQL のデータベースを用意しておきます。
MySQL の部分は要点のみを書きますので、詳細については MySQLの使い方 という記事を参考にしてください。
<データベースの作成>
CREATE DATABASE 文で spring_test
という名前のデータベースを作成しておきます。
mysql> create database spring_test;
Query OK, 1 row affected (0.33 sec)
<ユーザーの作成>
CREATE USER 文で yama3@localhost
という名前のデータベースを作成しておきます(ユーザー名とパスワードは何でもいいです)。
mysql> create user 'yama3'@'localhost' identified by '123456';
Query OK, 0 rows affected (0.32 sec)
GRANT 文で、データベース(spring_test
)に対する全ての権限を設定しておきます。
mysql> grant all on spring_test.* to 'yama3'@'localhost';
Query OK, 0 rows affected (0.23 sec)
2. プロジェクト作成時の設定内容
プロジェクト作成については、設定内容を中心に書いていきます。
細かいことは前回記事に書きましたので、必要に応じて参照してみてください。
Spring Tool Sweet(STS)を使用してプロジェクトを作成していきます。
メニューから「File」→「New」→「Spring Starter Project」の順で選択して、次の画面を表示します。
ここでは、ビルドツール(Type)は Gradle を、Java バージョン(Java Version)は 11 を選択しています。
プジェクト名(Name)は適当に付けておきます。
「Next」ボタンを押すと、下図のライブラリを選択する画面になります。
選択するのは、「JDBC API」「MySQL Driver」「Spring Boot DevTools」「Spring Web」「Thymeleaf」の5つとなります(細かいことはこちらを参照)。
「Finish」をクリックすれば、プロジェクトが作成されます。
3. 作成するファイルとソースコード
以下、作成するファイルの全体図と、ソースコードの全体を一通り掲載しておきます。
個々のメソッドにおけるデータの受け渡しの流れについては、後から「5. 機能ごとのデータの流れのイメージ」のところで確認していきます。
3-1. ファイルのディレクトリ
プロジェクトのディレクトリ構成は、下図のとおりです。
青枠の2つのファイルは、ファイルもコードも自動生成されるので、そのまま使います。
緑枠の1つのファイルは、ファイルのみ作成されているので、そこにコードを書き込みます。
赤枠の7つのファイルは、所定のディレクトリにファイルを作成の上、コードを書いていきます。
※注意点 3つのHTMLファイルは、src/main/resources/templates
の下にsample
というフォルダを作成した上で、その中に格納していますのでご注意ください。
3-2. アプリケーションクラス(自動生成)
プロジェクト作成時に、CrudSampleApplication.java
というファイルが自動生成されています(プロジェクト名によって名前は異なります)。
特に変える必要もないので、このままにしておきます。
mainメソッドの中身は SpringApplication の run メソッドが記載されているだけです。
これにより、アプリケーションが起動されます。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CrudSampleApplication {
public static void main(String[] args) {
SpringApplication.run(CrudSampleApplication.class, args);
}
}
3-3. ビルドスクリプト(自動生成)
ビルドスクリプト(build.gradle
)も自動生成されます。
プロジェクトで設定したライブラリなどが反映されています。
これも、そもままにしておきます。
plugins {
id 'org.springframework.boot' version '2.4.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
3-4. アプリケーションプロパティ
ここにデータベースに関する設定を、以下のように記述します(ファイルは src/main/resources
のフォルダに入っています)。
spring.datasource.url=jdbc:mysql://localhost:3306/spring_test
spring.datasource.username=yama3
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.initialization-mode=always
spring.datasource.schema=classpath:schema.sql
spring.datasource.data=classpath:data.sql
spring.datasource.sql-script-encoding=utf-8
以下、個別の設定が必要なところに触れておきます。
spring.datasource.url の末尾の spring_test
は MySQL のデータベース名です。
spring.datasource.username には MySQL のユーザー名を記載します。
spring.datasource.password には MySQL のユーザーパスワードを記載します。
その他は、上記のままで大丈夫だと思います。設定内容の詳細はこちらを参照してください。
3-5. SQLスクリプト
SQLスクリプトには、プロジェクト起動時に実行されるSQL文を記載します。
schema.sql
およびdata.sql
には、次のような初期設定を書いておきます。
(細かいことはこちらを参照してください)
3-5-1. schema.sql
DROP TABLE IF EXISTS test_table;
CREATE TABLE test_table
(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
old INT NOT NULL,
PRIMARY KEY(id)
);
3-5-2. data.sql
INSERT INTO test_table(name, old)
VALUES('Taro', 30), ('Jiro', 25), ('Saburo', 22);
3-6. コントローラー
コントローラーには、データベースからのデータの取得も含めた処理を書いておきます(邪道ですが)。
こうすることで、データベースとのデータのやりとりが一目瞭然になります。
本来は、ビジネスロジック(Service)とデータアクセス(Repository)を作成して分担して処理すべきところですが、ここでは作成していません。
(コントローラー作成に関する細かいことはこちらを参照してください)
package com.example.demo;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
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;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/sample")
public class TestController {
private JdbcTemplate jdbcTemplate;
//コンストラクタ
@Autowired
public TestController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//一覧画面の表示
@GetMapping
public String index(Model model) {
String sql = "SELECT * FROM test_table";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
model.addAttribute("testList", list);
return "sample/index";
}
//新規入力フォームの表示
@GetMapping("/form")
public String form(@ModelAttribute TestForm testForm) {
return "sample/form";
}
//新規入力データの保存
@PostMapping("/form")
public String create(TestForm testForm) {
String sql = "INSERT INTO test_table(name, old) VALUES(?, ?);";
jdbcTemplate.update(sql, testForm.getName(), testForm.getOld());
return "redirect:/sample";
}
//編集フォームの表示
@GetMapping("/edit/{id}")
public String edit(@ModelAttribute TestForm testForm, @PathVariable int id) {
String sql = "SELECT * FROM test_table WHERE id = " + id;
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
testForm.setId((int)map.get("id"));
testForm.setName((String)map.get("name"));
testForm.setOld((int)map.get("old"));
return "sample/edit";
}
//編集データの保存
@PostMapping("/edit/{id}")
public String update(TestForm testForm, @PathVariable int id) {
String sql = "UPDATE test_table SET name = ?, old = ? WHERE id = " + id;
jdbcTemplate.update(sql, testForm.getName(), testForm.getOld());
return "redirect:/sample";
}
//データの削除
@PostMapping("/delete/{id}")
public String delete(@PathVariable int id) {
String sql = "DELETE from test_table WHERE id = " + id;
jdbcTemplate.update(sql);
return "redirect:/sample";
}
}
3-6-1. コンストラクタについての補足
上記では、コンストラクタを書いています。
コンストラクタの上にあるアノテーション@Autowired
がなくとも、このサンプルアプリは動きます。
なお、フィールド部分を下記のように書けば、コンストラクタは無くとも動きます。
@Autowired
private JdbcTemplate jdbcTemplate;
<参考サイト>
・Spring FrameworkでDIする3つの方法
3-6-2. リクエストマッピングについて
リクエストマッピング(RequestMapping)には、クライアントから受け付けたリクエスト URL 及び HTTP メソッドによって、Controller 内のどのメソッドに処理を渡すかを決める役割があります。
上記の TestController
の create メソッドを例に、ルールを見てみます。
まず、クラスの頭に付けているアノテーション @RequestMapping("/sample") では、ベースパスを /sample
と指定しています。
そして、create メソッドの頭に付いている @PostMapping("/form") では、HTTPメソッドは Post
、相対パスは /form
という指定をしています。
この場合、リクエストURLが /sample/form
で、HTTPメソッドが POST
であれば、update メソッドに処理が渡されるということになります。
以上のルールに基づいて、クライアントからのリクエストと、それに対応する TestController
のメソッドをまとめると次のようになります。
HTTPメソッド | リクエストURL | Controller | 処理内容 |
---|---|---|---|
Get | /sample | index | 一覧画面表示 |
Get | /sample/form | form | 入力画面表示 |
Post | /sample/form | create | 入力データ保存 |
Get | /sample/edit/{id} | edit | 編集画面表示 |
Post | /sample/edit/{id} | update | 編集データ保存 |
Post | /sample/delete/{id} | delete | データ削除 |
3-7. フォームクラス
入力値を保持するためのフォームクラスを作成します。
このフォームクラスを入れ物にして、ビュー、コントローラー、クライアントの間のデータのやり取りを行います。
次のように、入力値(ここでは test_table
のカラム)と対応させたフィールドを作成します。
フォームクラスには、コンストラクタがなくても大丈夫ですが、getter
と setter
は必須となりますので、作成しておきます。
package com.example.demo;
public class TestForm {
private Integer id;
private String name;
private Integer old;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getOld() {
return old;
}
public void setOld(Integer old) {
this.old = old;
}
}
3-8. HTMLファイル
HTMLファイルは、最低限の内容を書いておきました。
3つのHTMLファイルは、src/main/resources/templates
の下にsample
というフォルダを作成した上で、その中に格納しています。
なお、オブジェクト(Map)から要素を取り出すのに "${testForm.name}"
という書き方をしていますが、これは "*{name}"
というように簡単に書くこともできます。
ここでは、イメージのし易さを優先して、あえて前者の書き方をしています。
3-8-1. index.html
これは一覧表示の画面です。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>CRUD Sample</title>
</head>
<body>
<h2>一覧ページ</h2>
<table>
<tr><th>id</th><th>name</th><th>old</th></tr>
<tr th:each="test : ${testList}">
<td th:text="${test.id}"></td>
<td th:text="${test.name}"></td>
<td th:text="${test.old}"></td>
<td>
<a href="#" th:href="@{'/sample/edit/' + ${test.id}}"><button>編集</button></a>
</td>
<td>
<form action="#" th:action="@{'/sample/delete/' + ${test.id}}" method="post">
<button>削除</button>
</form>
</td>
</tr>
</table>
<div><a href="#" th:href="@{/sample/form}">入力フォーム</a></div>
</body>
</html>
3-8-2. form.html
新規入力フォームの画面です。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>CRUD Sample</title>
</head>
<body>
<h2>入力フォーム</h2>
<form method="post" action="#" th:action="@{/sample/form}" th:object="${testForm}">
<label for="name">名前</label>
<input type="text" id="name" name="name" th:value="${testForm.name}"><br>
<label for="old">年齢</label>
<input type="text" id="old" name="old" th:value="${testForm.old}"><br>
<input type="submit" value="送信">
</form>
<div><a href="#" th:href="@{/sample}">トップページ</a></div>
</body>
</html>
3-8-3. edit.html
編集フォームの画面です。新規入力フォームとほとんど同じ内容です。
共通化する方法もありますが、分かりやすさのために、別途作成しています。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>CRUD Sample</title>
</head>
<body>
<h2>編集フォーム</h2>
<form method="post" action="#" th:action="@{'/sample/edit/' + ${testForm.id}}" th:object="${testForm}">
<label for="name">名前</label>
<input type="text" id="name" name="name" th:value="${testForm.name}"><br>
<label for="old">年齢</label>
<input type="text" id="old" name="old" th:value="${testForm.old}"><br>
<input type="submit" value="送信">
</form>
<div><a href="#" th:href="@{/sample}">トップページ</a></div>
</body>
</html>
3-9. アプリケーションの起動
アプリケーションを起動してみます(起動方法の細かいことはこちらを参照してください)。
Package Explorer上のプロジェクト名(ここではCRUDSample
)を右クリックして、「Run As」→「Spring Boot App」を選択します。
Console 画面に次のような表示がされていきます。
アプリケーションが起動したら http://localhost:8080/sample にアクセスします。
次のような画面が表示されたら成功です。
4. Spring MVC のイメージ
データの流れを見るには、Spring MVC の構造を何となく把握しておく必要がありますが、「Spring MVC」という文言で画像検索すると、多種多様な画像が出てきます。
それぞれ内容が異なるので、なかなかイメージが掴みにくいところです。
4-1. 理論上のMVC
Spring MVC の図を確認する前に、まず、理論上のMVCの図を見てみます。
一般的な MVC はだいたい次のような構造として説明されています。
項目 | 説明 |
---|---|
Model | データの管理・処理を行う(DBへのアクセスなども含む)。 |
View | ユーザーへのへの表示・出力を行う。 |
Controller | ユーザーからのリクエストを受け付け、Model 及び View に伝達する。 |
4-2. Spring MVC
数多のサイトに掲載されている Spring MVC の図が多種多様なのは、結局、簡単に図式化できるような構造ではないからだろうと思います。
とはいえ、Spring MVC のイメージを掴まないとデータの流れが見えてこないので、簡単に作図しておきます。
(このあたりのサイトを参考にしています)
図で見ると、Model の存在がいまいちよく分かりません。
個人的には、右側の Business Class と Data Access が Model に見えて仕方ないのですが、あくまでイメージの問題なので、深く考えないようにします。
上記のモデル図のままだと、データの流れのイメージを把握するのが難しくなってしまうため、Front Controller と Controller を一体のものとみなして、次のようなシンプルなモデルとして捉えておきます。
以下では、上記の構造を更に簡略化して、データの流れを追っていきます。
<参考サイト>
・Web MVC framework
・Spring Framework Learning 05
5. 機能ごとのデータの流れのイメージ
前置きが長くなりましたが、ここからが本題です。
データの流れがイメージできると、コードを書く際にも迷いが少なくなります。
大事なのは、次のところだと思います。
① Controller と View の間のデータのやり取りは、Model を介して行われていること
② やり取りするデータは、Controller にあるメソッドの引数に入れて、取得したり更新したりすること
このあたりに注目していただけると、分かりやすいのではないかと思います。
5-1. 一覧表示機能(READ)
まずは、CRUD の Read(読み込み)から確認していきます。
次の画面を表示するには、データベースから、登録されているデータの一覧を取得する必要があります。
関係する Controller と View のコードは次のとおりです。
① Controller のソースコード(抜粋)
一覧表示をする場合は、ユーザーは、リクエストURLは「/sample」、HTTPメソッドは「GET」でアクセスしてきます。
この場合は、index メソッドに処理が渡されます(対応関係のルールは「3-6-2. リクエストマッピングについて」に書いたとおりです)。
@Controller
@RequestMapping("/sample")
public class TestController {
//中略
@GetMapping
public String index(Model model) {
String sql = "SELECT * FROM test_table";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
model.addAttribute("testList", list);
return "sample/index";
}
}
index メソッド の中身を見てみると、最初の2行で次の処理を行っています。
・JdbcTemplateクラスを使用してデータベースに接続(使用メソッドは queryForList)
・SQL文の実行により取得した一覧データを、List オブジェクトである list に格納(参考)
次の1行で、list に testList
という名前を付けた上で、Model に格納しています(Model は引数に設定するだけで使えます)。
この Model が View にデータを渡してくれます。
最後の return
では、一覧表示に使用するテンプレートを sample/index.html
と指定しています。
② View のソースコード(抜粋)
Controller から命令を受けた View は、/src/main/resources/templates
フォルダから sample/index.html を探し出します。
データベースから取得している一覧データ list は、Model に testList
という名前で格納されてますので、View 側ではこれを ${testList}
という記述で呼び出します(Thymeleaf を使用)。
<table>
<tr th:each="test : ${testList}">
<td th:text="${test.id}"></td>
<td th:text="${test.name}"></td>
<td th:text="${test.old}"></td>
</tr>
</table>
th:each
の記述で、for each 文のように1レコード分ずつデータを取り出して、画面を作成します。
③ データの流れのイメージ
一覧表示(READ)におけるデータの流れを図にすると、次のようになります。
ここでは、index メソッドの引数である Model がデータ受け渡しの役割を担っています。
Controller が直接 View にデータを渡しているのではなく、 Controller が Model にデータを格納して、View は Model にデータを取りに行くというイメージです。
5-2. 登録機能(CREATE)
次に、CRUD の Create(登録)についてです。
まず、最初に入力フォームを表示する必要があるので、その部分から確認していきます。
5-2-1. 入力フォームの表示
次の入力フォームの表示では、データベースからデータを取得する必要はありません。
ここでは、フォームの入力データを保持するためのフォームクラス(TestForm.java
)の使用が必要になります。
① Controller のソースコード(抜粋)
入力フォームを表示する場合、ユーザーは、リクエストURL「/sample/form」、HTTPメソッド「GETメソッド」でアクセスすることになります。
この場合は、form メソッドに処理が渡されます。
@Controller
@RequestMapping("/sample")
public class TestController {
//中略
@GetMapping("/form")
public String form(@ModelAttribute TestForm testForm) {
return "sample/form";
}
}
form メソッド の中身は、return
の1行のみです。
この1行で、View に対して一覧表示に使用するテンプレートを sample/form.html
と指定しています。
なお、引数に指定しているフォームクラスには、@ModelAttribute
というアノテーションが付いています。
これにより、Model にフォームクラスのインスタンス testForm
が追加されることになります(厳密なことはこちらの記事を参照してください)。
testForm
には何らの値を入れていませんので、中身は空っぽのままです。View は、この空の testForm
を Model を介して取得することになります。
なお、@ModelAttribute
を付けずに、次のように記載しても動作します。
public String form(TestForm testForm) {
return "sample/form";
}
これは、「ハンドラメソッドの引数に、フレームワーク側がサポートしていないクラスがあると自動的に @RequestParam または @odelAttribute とみなされるため」とのことです(「ModelAttributeでフォームパラメータをオブジェクトにマッピングする」より)。
<参考サイト>
・Spring MVC についてまとめてみるよ!!!
・はじめてのSpring MVCアプリケーション
・ModelAttributeでフォームパラメータをオブジェクトにマッピングする
・@ModelAttribute を使う
② View のソースコード(抜粋)
Controller から命令を受けた View は、/src/main/resources/templates
フォルダから sample/form.html を探し出します。
Model から取得した、フォームクラスのインスタンス testForm
を、以下のようにセットして画面を作成します(Thymeleaf を使用)。
<form method="post" action="#" th:action="@{/sample/form}" th:object="${testForm}">
<label for="name">名前</label>
<input type="text" id="name" name="name" th:value="${testForm.name}"><br>
<label for="old">年齢</label>
<input type="text" id="old" name="old" th:value="${testForm.old}"><br>
<input type="submit" value="送信">
</form>
③ データの流れのイメージ
データの流れを図にすると、次のようになります。
ここでは、form メソッドの引数である testForm が受け渡しされています。
5-2-2. データベースへの登録
次に、ユーザーからの新規登録のリクエストを受けた場合の処理になります。つまり、CRUD の Create の部分となります。
ユーザーが、フォームにデータを入力して送信ボタンを押すと、次のように、リクエストURLは「/sample/form」、HTTP メソッドは「POST」でデータが送信されます。
<form method="post" action="#" th:action="@{/sample/form}" th:object="${testForm}">
① Controller のソースコード(抜粋)
データの送信を受けると、Controller の create メソッドに処理が渡されます。
@Controller
@RequestMapping("/sample")
public class TestController {
//中略
@PostMapping("/form")
public String create(TestForm testForm) {
String sql = "INSERT INTO test_table(name, old) VALUES(?, ?);";
jdbcTemplate.update(sql, testForm.getName(), testForm.getOld());
return "redirect:/sample";
}
}
create メソッド の中身を見てみると、最初の2行で、次の処理を行っています。
・JdbcTemplateクラスを使用してデータベースに接続(使用メソッドは update)
・Client から受け取った testForm のデータを元に SQL 文を実行して、データベースに新たなレコードを登録
どういう仕組みで Client から送信されたデータが testForm に格納されるのかはいまいち分かりませんが、引数に設定することでデータの取得ができます。
最後の return
では、リダイレクト先として /sample
を指定しています。
これにより、クライアントから自動的にリクエストURL /sample
(HTTP メソッドは GET) へのアクセスが実行され、Controller の index メソッドが実行され、一覧ページが表示されることになります。
② データの流れのイメージ
登録(CREATE)におけるデータの流れを図にすると、次のようになります。
ここでは、create メソッドの引数である testForm がデータ受け渡しの役割を担っています。
5-3. 更新機能(UPDATE)
次に、CRUD の Update(更新)についてです。
まず、最初に編集フォームを表示する必要があるので、その部分から確認していきます。
5-3-1. 編集フォームの表示
編集フォームの表示するには、ユーザーが編集を行うデータを、データベースから取得する必要があります。
データを特定するために、リクエスト URL の一部(id
の部分)を使用し、フォームの表示するデータを受け渡すために、フォームクラス(TestForm.java
)を使用します。
① Controller のソースコード(抜粋)
編集フォームを表示する場合、ユーザーは、リクエストURL「/sample/edit/{id}」、HTTPメソッド「GETメソッド」でアクセスすることになります。
この場合は、edit メソッドに処理が渡されます。
@Controller
@RequestMapping("/sample")
public class TestController {
//編集フォームの表示
@GetMapping("/edit/{id}")
public String edit(@ModelAttribute TestForm testForm, @PathVariable int id) {
String sql = "SELECT * FROM test_table WHERE id = " + id;
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
testForm.setId((int)map.get("id"));
testForm.setName((String)map.get("name"));
testForm.setOld((int)map.get("old"));
return "sample/edit";
}
}
アノテーション@GetMapping("/edit/{id}")
のパスのうち、波括弧で囲んだ {id} の部分は、@PathVariable
を使用することで変数として受け取ることができます。
引数に指定しているフォームクラス testForm には、アノテーション@ModelAttribute
を付けて Model に追加しています。
edit メソッド の中身を見てみると、最初の2行で次の処理を行っています。
・JdbcTemplateクラスを使用してデータベースに接続(使用メソッドは queryForMap)
・リクエスト URL に付された {id} を元に編集対象となるレコードを取得し map に格納
次の3行で、map のデータの要素を1つずつ testForm に格納しています。
この testForm に格納されたデータが、Model を介して View に渡されることになります。
最後の return
では、一覧表示に使用するテンプレートを sample/edit.html
と指定しています。
② View のソースコード(抜粋)
Controller から命令を受けた View は、/src/main/resources/templates
フォルダから sample/edit.html を探し出します。
Model から取得したフォームクラスのインスタンス testForm
を、以下のようにセットして画面を作成します(Thymeleaf を使用)。
<form method="post" action="#" th:action="@{'/sample/edit/' + ${testForm.id}}" th:object="${testForm}">
<label for="name">名前</label>
<input type="text" id="name" name="name" th:value="${testForm.name}"><br>
<label for="old">年齢</label>
<input type="text" id="old" name="old" th:value="${testForm.old}"><br>
<input type="submit" value="送信">
</form>
③ データの流れのイメージ
データの流れを図にすると、次のようになります。
ここでは、edit メソッドの引数である id 及び testForm が、データの受け渡しを担っています。
5-3-2. データベースの更新
次に、ユーザーからのデータ更新のリクエストを受けた場合の処理になります。これは、CRUD の Update の部分となります。
ユーザーが、フォーム上でデータを修正して送信ボタンを押すと、次のように、リクエストURLは「/sample/edit/{id}」、HTTP メソッドは「POST」でデータが送信されます。
<form method="post" action="#" th:action="@{'/sample/edit/' + ${testForm.id}}" th:object="${testForm}">
① Controller のソースコード(抜粋)
データの送信を受けると、Controller の update メソッドに処理が渡されます。
@Controller
@RequestMapping("/sample")
public class TestController {
//編集データの保存
@PostMapping("/edit/{id}")
public String update(TestForm testForm, @PathVariable int id) {
String sql = "UPDATE test_table SET name = ?, old = ? WHERE id = " + id;
jdbcTemplate.update(sql, testForm.getName(), testForm.getOld());
return "redirect:/sample";
}
}
update メソッド の中身を見てみると、最初の2行で次の処理を行っています。
・JdbcTemplateクラスを使用してデータベースに接続(使用メソッドは update)
・Client から受け取った id 及び testForm のデータを元に SQL 文を実行してデータベースのレコードを更新
最後の return
では、リダイレクト先として /sample
を指定しています。
これにより、一覧ページが表示されることになります。
② データの流れのイメージ
更新(UPDATE)におけるデータの流れを図にすると、次のようになります。
ここでは、update メソッドの引数である id 及び testForm がデータ受け渡しの役割を担っています。
5-4. 削除機能(DELETE)
最後に、ユーザーからのデータ削除のリクエストを受けた場合の処理になります。これは、CRUD の Delete の部分となります。
ユーザーが、一覧表示画面(/sample
)の削除ボタンを押すと、次のように、リクエストURLは「/sample/delete/{id}」、HTTP メソッドは「POST」でリクエストが送信されます。
<form action="#" th:action="@{'/sample/delete/' + ${test.id}}" method="post">
<button>削除</button>
</form>
① Controller のソースコード(抜粋)
データの送信を受けると、Controller の delete メソッドに処理が渡されます。
@Controller
@RequestMapping("/sample")
public class TestController {
//データの削除
@PostMapping("/delete/{id}")
public String delete(@PathVariable int id) {
String sql = "DELETE from test_table WHERE id = " + id;
jdbcTemplate.update(sql);
return "redirect:/sample";
}
}
delete メソッド の中身を見てみると、最初の2行で次の処理を行っています。
・JdbcTemplateクラスを使用してデータベースに接続(使用メソッドは update)
・リクエスト URL から取得した id を元に SQL 文を実行して、データの削除を実行
最後の return
では、リダイレクト先として /sample
を指定しています。
これにより、一覧ページが表示されることになります。
② データの流れのイメージ
削除(DELETE)におけるデータの流れを図にすると、次のようになります。
ここでは、delete メソッドの引数である id がデータ受け渡しの役割を担っています。
さいごに
以上、学習のアウトプットとして書いてみましたが、謎は深まるばかりです。
お気づきのことがあれば、ご教示いただけると幸いです。