Spring Boot + Thymeleaf + MySQLでCRUDを実装する

前回、以下の記事を書きました。
[Spring Boot + ThymeleafでWEBアプリを動かす](http://qiita.com/kaikusakari/items/f847c23c3db37bd73ec3)

今回は同様の構成で、REST型でWebアプリにアクセスしてDBのデータを操作します。一番最初に表示する画面でテーブルのレコードを一覧表示しておき、その画面からデータ登録、既存データの更新、既存データの削除を実施するようなアプリになります。
構成は前回の記事のものに加えて、DBにMySQLを利用します。なお本記事ではMySQLの構築手順は省略します。また、DB接続にO/Rマッパは利用せずJDBCのみを使っています。

> Mac OS X 10.12
Java 1.8.0_92
Spring Tool Suite 3.8.4
Spring Boot 1.5.3
thymeleaf 2.1.5

## API一覧
以下の通り5通りのAPIを作成します

| NO |METHOD url         | 動作             |
|:--:|:------------------|:----------------|
| 1  |GET    /sample/    | テーブル全件参照   |
| 2  |GET    /sample/:id | id指定した1件参照 |
| 3  |POST   /sample/    | レコード登録      |
| 4  |PUT    /sample/:id | 1レコード更新      |
| 5  |DELETE /sample/:id | 1レコード削除      |

## 共通部分
前回の記事で利用した「SampleController.java」に上記のAPIを構築します。MySQLにはuserというテーブルを作成して、テーブルに対してRESTでデータを操作します。


**DB**

```sql
CREATE DATABASE mysql CHARACTER SET UTF8;
CREATE TABLE user (
  id int AUTO_INCREMENT,
  name varchar(254)
);
INSERT INTO user (id, name) VALUES (1, "kusakarikai");
```

追加・修正ファイル
![1.png](https://qiita-image-store.s3.amazonaws.com/0/110457/de7fef58-fb5f-f4d9-8369-1f0a012bf533.png)

**userテーブルの定義**

```java:com.example.User.java
package com.example;

public class User {
	private int id;
	private String name;

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
```

**ActionForm**

```java:com.example.UserForm.java
package com.example;

public class UserForm {
	private String name;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
```

**ORマップ定義**
ResultSet.next()をwhileでループさせると、最初の要素が読まれない事象が解決できずループの前に直接書いています。

```java:com.example.UserMapper.java
package com.example;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.jdbc.core.RowMapper;

public class UserMapper implements RowMapper<List<User>> {
	public List<User> mapRow(ResultSet rs, int rowNum)
　　　　　throws SQLException {
		List<User> list = new ArrayList<>();
		User tmp_user = new User();
		tmp_user.setId(rs.getInt("id"));
		tmp_user.setName(rs.getString("name"));
		list.add(tmp_user);
		while (rs.next()) {
			User user = new User();
			user.setId(rs.getInt("id"));
			user.setName(rs.getString("name"));
			list.add(user);
		}
		return list;
	}
}
```

**DB接続の設定**

```src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/DB名
spring.datasource.username=ユーザ名
spring.datasource.password=パスワード
spring.datasource.driver-class-name=org.gjt.mm.mysql.Driver
```

**依存の設定**

```xml:pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
```

## 1　GET /sample/
userテーブルのレコードを全件取得して、一覧表示します。

**Controller**

```java:com.example.SampleController.java
@Autowired
private JdbcTemplate jdbcTemplate;

@RequestMapping(path = "/sample", method = RequestMethod.GET)
String index(Model model) {
	List<User> list = jdbcTemplate.queryForObject("select * from user", new UserMapper());
	model.addAttribute("list", list);
	return "sample/index";
}
```

**template**

```html:src/main/resources/templates/sample/index.html
<table class="list">
  <tr>
    <th>ID</th>
    <th>名前</th>
  </tr>
  <tr th:each="list:${list}">
    <td th:text="${list.id}"></td>
    <td th:text="${list.name}"></td>
  </tr>
</table>
```

## 2　GET /sample/:id
userテーブルのレコードを1件取得します。

**Controller**

```java:com.example.SampleController.java
@Autowired
private JdbcTemplate jdbcTemplate;

@RequestMapping(path = "/sample/{id}", method = RequestMethod.GET)
String show(Model model, @PathVariable("id") int id) {
    List<User> list = jdbcTemplate.queryForObject("select * from user where id = ? ", new Object[] { id }, new UserMapper());
    model.addAttribute("list", list);
    return "sample/index";
}
```

**template**
省略(NO1と同様)

## 3　POST /sample
入力をuserテーブルに登録します。登録後はNO1のAPIにリダイレクトすることで一覧を取得して画面に表示します。

**Controller**

```java:com.example.SampleController.java
@Autowired
private JdbcTemplate jdbcTemplate;

@ModelAttribute
UserForm userForm() {
    return new UserForm();
}

@RequestMapping(path = "/sample", method = RequestMethod.POST)
String create(Model model, @ModelAttribute UserForm userForm) {
  　　jdbcTemplate.update("INSERT INTO user (name) values (?)", userForm.getName());
　　  return "redirect:/sample";
}
```

**template**

```html:src/main/resources/templates/sample/index.html
<form method="post" name="create" action="/sample" accept-charset="UTF8">
    <input type="text" name="name"/>
    <input type="submit" value="新規登録" />
</form>
```

## 4　PUT /sample/:id
既存の登録内容を更新します。登録後はNO1のAPIにリダイレクトすることで一覧を取得して画面に表示します。

**Controller**

```java:com.example.SampleController.java
@Autowired
private JdbcTemplate jdbcTemplate;

@ModelAttribute
UserForm userForm() {
    return new UserForm();
}

@RequestMapping(path = "/sample/{id}", method = RequestMethod.PUT)
String update(Model model, @ModelAttribute UserForm userForm, @PathVariable("id") int id) {
    jdbcTemplate.update("UPDATE user SET name = ? where id = ? ", userForm.getName(), id);
    return "redirect:/sample";
}
```

**template**

```html:src/main/resources/templates/sample/index.html
<form method="post" action="/sample/:id">
    <input type="hidden" name="_method" value="put">
    <input type="text" name="name">
    <input type="submit">
</form>
```

## 5　DELETE /sample/:id
既存の登録内容を削除します。登録後はNO1のAPIにリダイレクトすることで一覧を取得して画面に表示します。

**Controller**

```java:com.example.SampleController.java
@Autowired
private JdbcTemplate jdbcTemplate;

@RequestMapping(path = "/sample/{id}", method = RequestMethod.DELETE)
String destory(Model model, @PathVariable("id") int id) {
    jdbcTemplate.update("delete from user where id = ? ", id);
    return "redirect:/sample";
}
```

**template**

```html:src/main/resources/templates/sample/index.html
<a href="javascript:void(0);" onclick="destory_func();">削除</a>
function destory_func() {
    var form = document.createElement("form");
    var hidden = document.createElement("input");
    form.method = "post";
    form.action = "/sample/" + id;
    hidden.type = "hidden";
    hidden.name = "_method"
    hidden.value = "delete";
    form.appendChild(hidden);
    document.body.appendChild(form);
    form.submit();
}
```

以上、5APIに沿ってControllerとtemplateを実装しました。いくつかの書籍や、フレームワークのプログラムを見ながら進めていますが、実装の意図などからソフトウェアの全体像が少しずつ掴めてきて面白いです。

本記事は動作に必要な部分だけ抜き出していますが、ソースは以下にありますので必要な方はお願いいたします。
[github kaikusakari/spring_crud](https://github.com/kaikusakari/spring_crud)