#26 Spring Mybatisを利用したデータベース操作[3. Update, Delete]

# 26 Spring Mybatisを利用したデータベース操作[3. SELECT 1件]
今回はMybatisを用いてUpdate, Delete文を実行していきます。

# 前提条件
この記事はSpringの最低限の知識が必要になります。
また、なるべく分かりやすく書くつもりですが、この記事の目的は自分の勉強のアウトプットであるため所々説明は省略します。

# 前回まで
前回はMybatisを利用してデータを1件取得しました。

https://qiita.com/Jackoguro/items/a3f90527aadd2857c0b1

# 構築環境
1. 各バージョン
Spring Boot ver 2.7.5
mybatis-spring-boot-starter ver 2.2.2
Model Mapper ver 3.1.0
jquery ver 3.6.1
bootstrap ver 5.2.2
webjars-locator ver 0.46
thymeleaf-layout-dialect ver 3.0.0

2. 依存関係
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/58b53ccd-7cf2-2d82-4214-9f0c08fced2e.png)

# 成果物
1. 更新
ユーザー名(TokyoTochoDayo⇒TokyoTochoDewanaiyo)
　1. 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/fca8a674-ff25-8135-ccd8-418165d64093.png)
　
　2. 変更後 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/cdeabcf0-f522-4d78-447d-7f2519dbd1c2.png)

2. 削除
　1. 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/c1675408-8ef7-8fda-2a6c-8645e1d347d9.png)

　2. 変更後 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/da4cfaea-3dce-88e9-8c8d-9c4160519312.png)




# 今回行うこと(手順)
今回は以下の流れに沿って進めていきます。
1. SQLの記述
　1. マッパー(UserMapper.java)にメソッドを追加
　2. UserMapper.xmlにUPDATE, DELETE文を追加
　3. 別の書き方(引数が(@Param("user") MUser user)の場合) 
1. サービスの記述
　1. UserService.java
　2. UserServiceImpl.java
1. コントローラー(UserDetailController.java)の修正
　1. params属性
　2. 別の書き方(引数が(@Param("user") MUser user)の場合)
1. HTML(detail.html)の修正

## 1. SQLの記述
### 1. マッパー(UserMapper.java)にメソッドを追加
```java:UserMapper
package com.example.repository;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.example.model.MUser;

@Mapper
public interface UserMapper {
	
	/* ユーザー登録 */
	public int insertOne(MUser user);
	
	/* ユーザー表示(複数件の場合はリスト型で返す) */
	public List<MUser> findAllMUser();
	
	/* ユーザー取得(1件) */
	public MUser findOneMUser(String UserId);
	
	/* ユーザー更新(1件) */
	public void updateOneMUser(@Param("userId") String userId,
			@Param("password") String password,
			@Param("userName") String userName);

	/* ユーザー削除(1件) */
	public int deleteOneMUser(@Param("userId") String userId);
}
```
<br>

``@Param``アノテーションを付けることで指定した値をSQL内に埋め込むことができます。
```java
	/* ユーザー更新(1件) */
	public void updateOneMUser(@Param("userId") String userId,
			@Param("password") String password,
			@Param("userName") String userName);

	/* ユーザー削除(1件) */
	public int deleteOneMUser(@Param("userId") String userId);
```

### 2. UserMapper.xmlにUPDATE, DELETE文を追加
```xml:UserMapper.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とxmlのマッピング -->
<mapper namespace="com.example.repository.UserMapper">
<!-- マッピング定義(ユーザー) -->
<resultMap type="com.example.model.MUser" id="muser">
	<id column="user_id" property="userId"/>
	<result column="phone_number" property="phoneNumber"/>
	<result column="postal_number1" property="postalNumber1"/>
	<result column="postal_number2" property="postalNumber2"/>
	<result column="address" property="address"/>
	<result column="user_name" property="userName"/>
	<result column="birthday1" property="birthday1"/>
	<result column="birthday2" property="birthday2"/>
	<result column="age" property="age"/>
	<result column="account_icon" property="accountIcon"/>
	<result column="gender" property="gender"/>
</resultMap>
	
	<!-- ユーザー1件登録 -->
	<insert id="insertOne">
        <!-- 省略 -->
	</insert>
	
	<!-- ユーザー情報全件取得 -->
	<select id="findAllMUser" resultType="MUser">
		SELECT * FROM M_USER
	</select>

	<!-- ユーザー情報1件取得 -->	
	<select id="findOneMUser" resultMap="muser">
		SELECT * FROM M_USER WHERE user_id = #{userId}
	</select>
	
	<!-- ユーザー情報1件更新 -->
	<update id="updateOneMUser">
		UPDATE M_USER SET password = #{password}, user_name = #{userName} WHERE user_id = #{userId}
	</update>
	
	<!-- ユーザー情報1件削除 -->
	<delete id="deleteOneMUser">
		DELETE FROM M_USER WHERE user_id = #{userId}
	</delete>
</mapper>
```
```xml
	<!-- ユーザー情報1件更新 -->
	<update id="updateOneMUser">
		UPDATE M_USER SET password = #{password}, user_name = #{userName} WHERE user_id = #{userId}
	</update>
	
	<!-- ユーザー情報1件削除 -->
	<delete id="deleteOneMUser">
		DELETE FROM M_USER WHERE user_id = #{userId}
	</delete>
```

### 3. 別の書き方
また、``UserMapper.java``で記述したメソッド``updateOneMUser``と``deleteOneMUser``は以下のような書き方でも可能です。
```java
    /* 引数をMUser型に変更 */
    public void updateOneMUser(@Param("user") MUser user);

    public int deleteOneMUser(@Param("user") MUser user);
```
<br>

上記のように書き換えた場合、``UserMapper.xml``ファイルは以下のように書き換えます。
```xml
	<!-- ユーザー情報1件更新 -->
	<update id="updateOneMUser">
		UPDATE M_USER SET password = #{user.password}, user_name = #{user.userName} WHERE user_id = #{user.userId}
	</update>
	
	<!-- ユーザー情報1件削除 -->
	<delete id="deleteOneMUser">
		DELETE FROM M_USER WHERE user_id = #{user.userId}
	</delete>
```

## 2. サービスの記述
次にサービスに内容を記述していきます。

### 1. UserService.java
```java:UserService.java
package com.example.service;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.springframework.web.multipart.MultipartFile;

import com.example.model.MUser;

public interface UserService {
	
	/* 性別のMapを生成する */
	public Map<String, Integer> getGenderMap(Locale locale);
	
	/* ユーザー登録 */
	public void signUp(MUser muser);
	
	/* ユーザー取得 */
	public List<MUser> getAllMUser();
	
	/* ユーザー取得(1件) */
	public MUser getOneMUser(String UserId);
	
	/* ユーザー更新(1件) */
	public void updateMUserOne(String userId, String password, String userName);
//  public void updateMUserOne(MUser user); // 「3. 別の書き方」で書いた場合
	
	/* ユーザー削除(1件) */
	public void deleteMUserOne(String userId);
//  public void deleteMUserOne(MUser user); // 「3. 別の書き方」で書いた場合
```
```java
	/* ユーザー更新(1件) */
	public void updateMUserOne(String userId, String password, String userName);
//  public void updateMUserOne(MUser user); // 「3. 別の書き方」で書いた場合
	
	/* ユーザー削除(1件) */
	public void deleteMUserOne(String userId);
//  public void deleteMUserOne(MUser user); // 「3. 別の書き方」で書いた場合
```
抽象メソッドを記述します。

### 2. UserServiceImpl.java
```java:UserServiceImpl.java
package com.example.service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.example.model.MUser;
import com.example.repository.UserMapper;
import com.example.service.UserService;

@Service
public class UserServiceImpl implements UserService {

	/* messages.propertiesのDIを注入 */
	@Autowired
	private MessageSource messagesource;
	
	/* レポジトリー(UserMapper.java)のDIを注入 */
	@Autowired
	private UserMapper mapper;
	
	/* 省略 */

	/* ユーザー情報取得 */
	@Override
	public List<MUser> getAllMUser() {
		return mapper.findAllMUser();
	}

	/* ユーザー情報取得(1件) */
	@Override
	public MUser getOneMUser(String UserId) {
		return mapper.findOneMUser(UserId);
	}

    /* 省略 */

	/* ユーザー更新(1件) */
	@Override
	public void updateMUserOne(String userId, String password, String userName) {
		mapper.updateOneMUser(userId, password, userName);
	}

    /* 「3. 別の書き方」で書いた場合(ユーザー更新(1件)) */
//  @Override
//	public void updateMUserOne(MUser muser) {
//		mapper.updateOneMUser(muser);
//  }

    /* ユーザー削除(1件) */
	@Override
	public void deleteMUserOne(String userId) {
		int count = mapper.deleteOneMUser(userId);
	}

    /* 「3. 別の書き方」で書いた場合(ユーザー削除(1件)) */
//  @Override
//	public void updateMUserOne(MUser muser) {
//		mapper.updateOneMUser(muser);
//  }
}
```
先ほど記述した抽象メソッドを実装します。メソッドの戻り値は``1. SQLの記述``で記述した``findOneMUser``メソッドを使用します。

## 3. コントローラー(UserDetailController.java)の修正
```UserDetailController.java
package com.example.controller;

import org.apache.tomcat.util.codec.binary.Base64;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.form.UserDetailForm;
import com.example.model.MUser;
import com.example.service.UserService;

import lombok.extern.slf4j.Slf4j;

@Controller
@RequestMapping("/user")
@Slf4j
public class UserDetailController {

	@Autowired
	private UserService userservice;
	
	@Autowired
	private ModelMapper modelMapper;
	
	/* 省略 */
	
	/* ユーザー更新情報 */
	@PostMapping(value="/detail", params="update")
	public String updateUser(UserDetailForm form, Model model/*, MUser user*/) {
		
		// formをMUserクラスに変換
        // 「3. 別の書き方」で書いた場合
//		MUser user = modelMapper.map(form, MUser.class);
		
		// ユーザー情報を更新
		userservice.updateMUserOne(form.getUserId(), form.getPassword(), form.getUserName());

        /* 「3. 別の書き方」で書いた場合(ユーザー更新(1件)) */
//		userservice.updateMUserOne(user);
		
		// ユーザー一覧画面にリダイレクト
		return "redirect:/user/list";
	}
	
	/* ユーザー削除処理 */
	@PostMapping(value="/detail", params="delete")
	public String deleteUser(UserDetailForm form, Model model/*, MUser user*/) {
		// ユーザー情報を削除
		userservice.deleteMUserOne(form.getUserId());

        /* 「3. 別の書き方」で書いた場合(ユーザー削除(1件)) */
//		userservice.deleteMUserOne(user);
		
		// ユーザー一覧画面にリダイレクト
		return "redirect:/user/list";
	}
}
```

### 1. params属性
``@PostMapping``内にparams属性を設定します。この属性には、``button``タグのname属性と同じ値を設定します。
こうすることで同一のformタグ内のボタンであっても、コントローラーで受けるメソッドを変更することができます。
```java
// 更新処理
@PostMapping(value="/detail", params="update")

// 削除処理
@PostMapping(value="/detail", params="delete")
```

### 2. 別の書き方(引数が(@Param("user") MUser user)の場合)
``UserMapper.java``の``updateupdateOneMUser``と``deleteOneMUser``メソッドの引数を``@Param("user") MUser user``にした場合、``UserDetailController.java``の``updateUser``と``deleteUser``メソッドの引数に``MUser user``を追加するか、``MUser user = modelMapper.map(form, MUser.class);``でFormクラスからコピーする必要があります。

```java
/* 引数にMUser userを加える */
public String updateUser(UserDetailForm form, Model model , MUser user) 
public String deleteUser(UserDetailForm form, Model model , MUser user)

/* Formクラスからコピーする */
MUser user = modelMapper.map(form, MUser.class);
```

## 4. HTML(detail.html)の修正
```html:detail.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<head>
<title>ユーザー詳細</title>
<!-- CSS読込 -->
<link rel="stylesheet" th:href="@{/css/user/list.css}">
</head>
<body>
	<div layout:fragment="content">
		<div class="header border-bottom">
			<h1 class="h2">ユーザー詳細</h1>
		</div>
		<form id="user-detail-form" method="post" th:action="@{/user/detail}" class="form-signup" th:object="${userDetailForm}">
			<input type="hidden" th:field="*{userId}">
			<!-- ユーザー詳細情報 -->
			<table class="table table-striped table-bordered table-hover">
				<tbody>
					<tr>
						<th class="w-25">ユーザーID</th>
						<td th:text="*{userId}"></td>
					</tr>
					<tr>
						<th>電話番号</th>
						<td th:text="*{phoneNumber} == null ? '登録されていません' : *{phoneNumber}"></td>
					</tr>
					<tr>
						<th>郵便番号</th>
						<td th:text="*{postalNumber1} == null ? '登録されていません' : |*{postalNumber1} - *{postalNumber2}|"></td>
					</tr>
					<tr> 
						<th>パスワード</th>
						<td>
							<input type="text" class="form-control" th:field="*{password}"/>
						</td>
					</tr> 
					<tr>
						<th>ユーザー名</th> 
						<td>
							<input type="text" class="form-control" th:field="*{userName}"/> 
						</td>
					</tr>
					<tr>
						<th>誕生日</th> 
						<td th:text="*{birthday2} == null ? '登録されていません' : *{#dates.format(birthday2, 'YYYY/MM/dd')}"></td> 
					</tr>
					<tr> 
						<th>年齢</th>
						<td th:text="*{age} == null ? '登録されていません' : *{age}"></td>
					</tr>
					<tr>
						<th>性別</th> 
						<div th:switch="*{gender}">
							<td th:case= 0 th:text='男性'>
							<td th:case= 1 th:text='女性'>
							<td th:case= "*" th:text='登録されていません'>
						</div>
					</tr>
				</tbody>
			</table>
			<!-- ボタンエリア -->
			<div class="text-center">
				<!-- 更新ボタン -->
				<button class="btn btn-danger" type="submit" name="update">更新</button>
				<!-- 削除ボタン -->
				<button class="btn btn-primary" type="submit" name="delete">削除</button>
			</div>	
		</form>
	</div>
</body>
</html>
```
<br>

``button``タグの``name``属性とコントローラーの``params``属性の値を合わせることで、メソッドを指定することができる
```html
            <!-- ボタンエリア -->
			<div class="text-center">
				<!-- 更新ボタン -->
				<button class="btn btn-danger" type="submit" name="update">更新</button>
				<!-- 削除ボタン -->
				<button class="btn btn-primary" type="submit" name="delete">削除</button>
			</div>	
```

# 最後に

1. 更新
ユーザー名(TokyoTochoDayo⇒TokyoTochoDewanaiyo)
　1. 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/fca8a674-ff25-8135-ccd8-418165d64093.png)
　
　2. 変更後 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/cdeabcf0-f522-4d78-447d-7f2519dbd1c2.png)

2. 削除
　1. 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/c1675408-8ef7-8fda-2a6c-8645e1d347d9.png)

　2. 変更後 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/da4cfaea-3dce-88e9-8c8d-9c4160519312.png)