#21 Spring Mybatisを利用したデータベース操作[2. INSERT]
今回はMybatisを用いてデータの挿入を行っていきます。
前提条件
この記事はSpringの最低限の知識が必要になります。
また、なるべく分かりやすく書くつもりですが、この記事の目的は自分の勉強のアウトプットであるため所々説明は省略します。
前回まで
前回はMybatisを利用するための環境構築とOracleDBを用いたテーブル作成を行いました。
構築環境
-
各バージョン
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
今回行うこと
今回は以下の流れに沿って進めていきます。
- エンティティとは
- クラス名
- データベースへ値の引き渡し
- データベースから値の取得
- テーブル(M_USER)用のエンティティクラス(MUser.java)の作成
- テーブル(M_USER)用のリポジトリー(UserMapper.java)の作成
1. Mapperとxmlのマッピング
2. SQLとメソッドのマッピング
3. メソッドの引数とSQLパラメータのマッピング - UserMapper.xmlにSQLを記述
- サービスの処理の記述
- UserService.java(インターフェース)
- UserServiceImpl.java(実装クラス)
- ModelMapperをBeanに登録
- コントローラー(SignupController.java)の修正
1. エンティティとは
エンティティはテーブルの1行
に対応するクラスです。エンティティに定義されたフィールドはテーブルのカラム値
に対応します。
1. クラス名
エンティティのクラス名は対応するデータベースのテーブルと同じ名前にすることが多い
2. データベースへ値の引き渡し
データベースへ値の挿入、更新をする際に、エンティティに値を入れ引き渡す
3. データベースから値の取得
データベースから値を取得した場合に、値をエンティティに保持しておく
2. テーブル(M_USER)用のエンティティクラス(MUser.java)の作成
テーブル(M_USER)に対応させるためのエンティティクラス(MUser.Java)を作成します。
package com.example.model;
import java.util.Date;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
@Data
public class MUser {
private String userId;
private Integer phoneNumber;
private Integer postalNumber1;
private Integer postalNumber2;
private String address;
private String userName;
private String password;
private Date birthday1;
private Date birthday2;
private Integer age;
private byte[] accountIcon;
private Integer gender;
}
byte[]:画像を取り込む場合、エンティティクラスのデータ型はbyte[]を使用する
3. テーブル(M_USER)用のリポジトリー(UserMapper.java)の作成
リポジトリーとはデータベースのデータ操作を行うクラス
です。リポジトリーを作成する場合、特定の実装への依存を回避するために必ずインテーフェースを定義した上で実装します。
Mybatisでリポジトリーを作成するには、インターフェースに@Mapper
を付与します。
package com.example.repository;
import org.apache.ibatis.annotations.Mapper;
import com.example.model.MUser;
@Mapper
public interface UserMapper {
/* ユーザー登録 */
public int insertOne(MUser user);
}
4. UserMapper.xmlにSQLを記述
次に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.exeample.repository.UserMapper">
<!-- ユーザー1件登録 -->
<insert id="insertOne">
<!-- テーブル(M_USER)の各項目 -->
insert into m_user(
user_id,
phone_number,
postal_number1,
postal_number2,
address,
user_name,
password,
birthday1,
birthday2,
age,
account_icon,
gender
)
<!-- エンティティクラスに定義した各項目 -->
values(
values(
#{userId},
#{phoneNumber, jdbcType=VARCHAR},
#{postalNumber1, jdbcType=INTEGER},
#{postalNumber2, jdbcType=INTEGER},
#{address, jdbcType=VARCHAR},
#{userName},
#{password},
#{birthday1, jdbcType=DATE},
#{birthday2, jdbcType=DATE},
#{age, jdbcType=INTEGER},
#{accountIcon, jdbcType=BLOB},
#{gender, jdbcType=INTEGER}
)
)
</insert>
</mapper>
ポイントとしては3点あります。
1. Mapperとxmlのマッピング
<!-- Mapperとxmlのマッピング -->
<mapper namespace="com.exeample.repository.UserMapper">
mapperタグを用いて、Mapperとxmlファイルをマッピングします。
mapperタグのnamespace属性
にMapperのインターフェース名をパッケージ名を含めて指定します。
2. SQLとメソッドのマッピング
次にSQLとメソッドのマッピングを行います。
/* UserMapper.javaで記述した抽象メソッド */
public int insertOne(MUser user);
<!-- ユーザー1件登録 -->
<insert id="insertOne">
UserMapper.xml
のid属性にUserMapper.java
で記述した抽象メソッドを指定します。
また、今回はINSERT処理を行うのでタグを<insert></insert>
としておきます。
3. メソッドの引数とSQLパラメータのマッピング
次にMapperメソッドの引数をSQLのパラメータに挿入します。
#{メソッド引き数名}
形式で指定できます。今回はメソッドの引数をMuser
にしているため、2. テーブル(M_USER)用のエンティティクラス(MUser.java)の作成
で作成したエンティティクラスのフィールド名をSQL内で指定します。
JDBCの仕様で、insert
, update
, delete
でnullが許可されている項目を指定する場合、JDBCデータ型(jdbcType) を指定する必要があります。サポートされているJDBCデータ型の一覧は、MyBatisのドキュメントに記載されています。
Nullだとカラムのデータ型がわからないので定義が必要らしいです。
<!-- エンティティクラスに定義した各項目 -->
values(
values(
#{userId},
#{phoneNumber, jdbcType=VARCHAR},
#{postalNumber1, jdbcType=INTEGER},
#{postalNumber2, jdbcType=INTEGER},
#{address, jdbcType=VARCHAR},
#{userName},
#{password},
#{birthday1, jdbcType=DATE},
#{birthday2, jdbcType=DATE},
#{age, jdbcType=INTEGER},
#{accountIcon, jdbcType=BLOB},
#{gender, jdbcType=INTEGER}
)
)
5. サービスの処理の記述
次に先ほど作成したコードを利用して実際に行う処理を記述していきます。
1. UserService.java(インターフェース)
package com.example.service;
import java.util.Locale;
import java.util.Map;
import com.example.model.MUser;
public interface UserService {
/* 性別のMapを生成する */
public Map<String, Integer> getGenderMap(Locale locale);
/* ユーザー登録 */
public void signUp(MUser muser);
}
2. UserServiceImpl.java(実装クラス)
@Autowired
を用いてDIの注入を行い、UserService.java
で記述した抽象メソッドの実装を行います。
package com.example.service.impl;
/* 省略 */
@Service
public class UserServiceImpl implements UserService {
/* 省略 */
/* レポジトリー(UserMapper.java)のDIを注入 */
@Autowired
private UserMapper mapper;
/* データの挿入 */
@Override
public void signUp(MUser user) {
mapper.insertOne(user);
}
}
6. ModelMapperをBeanに登録
前回の記事でダウンロードしたModelMapper
をBeanに登録していきます。
今回のようにライブラリに用意されているクラスをDIコンテナーに登録する場合、Beanのインスタンス(一般的にJavaConfig言われる)を作成します。
JavaConfigには@Configurationアノテーション
を付けます。
package com.example.config;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
7. コントローラー(SignupController.java)の修正
最後にコントローラの作成を行います。
package com.example.controller;
/* 省略 */
@Controller
@RequestMapping("/user")
@Slf4j
public class SignupController {
@Autowired
private UserService userService;
@Autowired
private ModelMapper modelMapper;
/* ユーザー登録画面を表示 */
@GetMapping("/signup")
public String getSignup(Model model, Locale locale, @ModelAttribute SignupForm form) {
// 性別を取得
Map<String, Integer> genderMap = userService.getGenderMap(locale);
model.addAttribute("genderMap", genderMap);
// ユーザー登録画面に遷移
return "user/signup";
}
/* ユーザー登録処理 */
@PostMapping("/signup")
public String postSignup(Model model, Locale locale,
@Validated(GroupOrder.class) @ModelAttribute SignupForm form,
BindingResult bindingResult) {
// 入力チェック
if(bindingResult.hasErrors()) {
// エラーが発生したので登録画面に戻る
return getSignup(model, locale, form);
}
log.info(form.toString());
// formをMUserクラスに変換
MUser user = modelMapper.map(form, MUser.class);
log.info(user.toString());
// ユーザ登録
userService.signUp(user);
// ログイン画面にリダイレクト
return "redirect:/login";
}
}
先ほどJavaConfig
に追加したModelMapper
のmapメソッド
を使用することでフィールドの内容をコピーすることができます。
Formクラス(SignupForm.java)
に格納されたデータをエンティティクラス(MUser.java)
にコピーする。
ただし、コピー元のデータとコピー先のデータのフィールド名は一致している必要がある。
MUser user = modelMapper.map(form, MUser.class);
バリエーションの設定は省略したSignupForm.java
import lombok.Data;
@Data
public class SignupForm {
private String userId;
private Integer phoneNumber;
private Integer postalNumber1;
private Integer postalNumber2;
private String address;
private String userName;
private String password;
private Date birthday1;
private Date birthday2;
private Integer age;
private MultipartFile accountIcon;
private Integer gender;
}
最後に
7. コントローラー(SignupController.java)の修正
の中で、ModelMapperのmapメソッド
を用いてFormクラス(SignupForm.java)
に格納されたデータをエンティティクラス(MUser.java)
にコピーしましたが、これには2つの理由があります。
1. 画面に変更があっても、サービスの修正が不要になる
2. 他の画面からもサービスを再利用できるようになる
よって、サービスを利用する場合はFormクラスを利用しないようにする!
以下のログがlog.info(form.toString())
により出力されたデータです(見やすいように縦書きに直してあります)。
SignupForm(userId=Tokyo@xxx.co.jp,
phoneNumber=0353211111,
postalNumber1=163,
postalNumber2=8001,
address=東京都新宿区西新宿二丁目8番1号,
userName=TokyoTocho,
password=Tokyo_0353211111,
birthday1=null,
birthday2=Mon Apr 01 00:00:00 JST 1991,
age=31,
accountIcon=org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@4028cb11,
gender=null)
以下のログがlog.info(user.toString())
により出力されたデータです(見やすいように縦書きに直してあります)。
MUser(userId=Tokyo@xxx.co.jp,
phoneNumber=0353211111,
postalNumber1=163,
postalNumber2=8001,
address=東京都新宿区西新宿二丁目8番1号,
userName=TokyoTocho,
password=Tokyo_0353211111,
birthday1=null,
birthday2=Mon Apr 01 00:00:00 JST 1991,
age=31,
accountIcon=null,
gender=null)
両方のログを比較すると、画像データであるaccountIcon
がMUser側ではnullになってしまっています。
次回以降はここを修正していきます。