【メモ】MyBatis

SpringBoot・Maven・MySQLで、MyBatisを使ったときのメモです。

# MyBatisとは
JavaなどのDBアクセス用オープンソースソフトウェアである、O/Rマッピングフレームワーク。
他のO/Rマッピングフレームワークとは異なり、データベースとオブジェクトをマッピングするのではなく、SQL文とオブジェクトのマッピングを行う。

* SQLをXMLに記述し、JavaのMapperクラス（interfaceクラス）を実行すると、メソッド名に応じたSQLが実行。
* 引数や戻り値は、JavaのオブジェクトとSQL間でマッピングされる。


### O/Rマッピングフレームワークとは
Javaなどの「オブジェクト」と「リレーショナルデータベース（RDS）」をマッピング（対応付け）する。
データベース操作にかかわる実装の煩雑さ[^煩雑さ]を軽減し、オブジェクト指向とデータベースの設計思想の違いを解決する。

[^煩雑さ]: DB接続処理、SQL文の組み立て、オブジェクトとDBデータ間のデータ詰め替えなど

* [聞いたことはあるけどよく知らないMyBatisにちょっと近づいてみる - ponsuke_tarou’s blog](https://ponsuke-tarou.hatenablog.com/entry/2018/01/19/140436)
* [O/Rマッピングとは？ | Think IT（シンクイット）](https://thinkit.co.jp/free/article/0606/13/1)


# MyBatisの使い方

## pomの定義
`mybatis-spring-boot-starter`を依存関係に追加する。
利用可能なバージョンは以下から確認。

* [mybatis-spring-boot-autoconfigure – Introduction](http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/)

```xml:pom.xml
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
```

## applocation.propertiesの定義
RDSへの接続情報を定義する。

```properties:applocation.properties
spring.datasource.url=(RDSのURL)
spring.datasource.username=(RDSのユーザー名)
spring.datasource.password=(RDSのパスワード)

# SQLのカラム名＝スネークケース(例:user_id)で、java＝キャメルケース(例:userId)の場合、
# mybatisで自動的に変換してくれる設定。
mybatis.configuration.map-underscore-to-camel-case=true
```

## Mapperクラス作成
`@Mapper`のアノテーションで、Mapperクラスを作成する。

### SQL文をXMLで記述する場合
`@Param`でパラメータのみ記述する。
パラメータが1つの場合は、`@Param`の記述は不要。
2つ以上の場合は、SQLクエリ内のパラメータとメソッド引数の対応関係の設定のために記述必須。

```java:Mapperクラス
@Mapper
public interface SampleMapper {
  public void selectUser(@Param("id") int no);
  public void insertUser(@Param("id") int no, @Param("username") String username, @Param("password") String password);
}
```

* [mybatis – MyBatis 3 | Java API](https://mybatis.org/mybatis-3/java-api.html)
* [Spring Boot から MyBatis を利用するための設定 (Gradle/MySQL) - Qoosky](https://www.qoosky.io/techs/b6c5688848)

### SQL文をJavaで記述する場合
XMLを使ってSQLを定義しない場合は、Java上にSQLを記述することも可能。
その場合、`@Insert`, `@Update`, `@Select`, `@Delete`等のアノテーションを使う。

```java:Mapperクラス
@Mapper
public interface SampleMapper {
  @Select("SELECT * FROM USER WHERE id = #{id}")
  User selectUser(@Param("id") int id);
}
```

* [MyBatis Mapper アノテーションの使い方 - Qiita](https://qiita.com/d-yosh/items/a2bec9718bccfe15a6ae)

## Mapper.xmlの作成

:::note warn
XMLとMapperクラスは、同一パッケージ名・ファイル名にする必要あり。
:::
XMLはresourcesに配置するが、同一パッケージ名・ファイル名でないと、単純な紐付けができない。
また、XML側がない場合は起動時にエラーとなるが、Mapperクラスがない場合は起動時にエラーにならない。


### mapperタグ

```xml:Mapper.xml
<mapper namespace ="sample.SampleMapper">
  # ここにCRUDに対応するXMLタグを記述
</mapper>
```

| 属性 | 説明 |
|:-----|:-----|
|namespace|Rインタフェースの完全修飾クラス名(FQCN)を指定。<br>ここでXMLとインターフフェースクラスの紐付けする。|


### select
```xml:Mapper.xml
<mapper namespace ="sample.SampleMapper">
  <select id="selectUser" parameterType="int" resultType="hashmap">
    SELECT * FROM USER WHERE ID = #{id}
  </select>
</mapper>
```

| 属性 | 説明 |
|:-----|:-----|
| id |このネームスペース内で固有な識別子。ステートメント参照に使用。|
| parameterType |このステートメントに渡される引数の型。<br>完全修飾クラス名またはエイリアス。<br>TypeHandler は実際の引数に応じて自動的に導出されるため省略可。<br>＜デフォルト値：未設定＞|
|resultType|このステートメントから返されるオブジェクトの型。<br>完全修飾クラス名またはエイリアス。<br>ステートメントがコレクションを返す場合は、コレクションの型ではなくコレクションに含まれるオブジェクトの型を指定する必要あり。<br>`resultType` と resultMap は、どちらか一方のみ指定可。|
|resultMap|別の場所で定義されている resultMap を参照する。<br>複雑なマッピングが必要となる様々なケースに対応可能。<br>resultType と resultMap は、どちらか一方のみ指定可。|

### insert ／ update ／ delete
```xml:Mapper.xml
<mapper namespace ="sample.SampleMapper">
  <insert id="insertUser" parameterType="domain.User">

  <update id="updateUser">
    update User set
      username = #{username}, password = #{password}
      where id = #{id}
  </update>

  <delete id="deleteUser">
    delete from User where id = #{id}
  </delete>
</mapper>
```
| 属性 | 説明 |
|:-----|:-----|
| id |このネームスペース内で固有な識別子。ステートメント参照に使用。|
| parameterType |このステートメントに渡される引数の型。<br>完全修飾クラス名またはエイリアス。<br>TypeHandler は実際の引数に応じて自動的に導出されるため省略可。<br>＜デフォルト値：未設定＞|


* [MyBatis – MyBatis 3 | Mapper XML ファイル](https://mybatis.org/mybatis-3/ja/sqlmap-xml.html)

# サンプルプログラム
insertの機能をお試しで作成したときの内容。
お試しのため、MyBatis以外の部分は省略しています。

### pom.xml

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>sample</groupId>
	<artifactId>sample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sample</name>
	<description>sample</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
```

### applocation.properties

```properties:applocation.properties
mybatis.configuration.map-underscore-to-camel-case=true
spring.datasource.url=(RDSのURL)
spring.datasource.username=(RDSのユーザー名)
spring.datasource.password=(RDSのパスワード)
```

### Applicationクラス
```java:SampleApplication.java
package sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SampleApplication {
	public static void main(String[] args) {
		SpringApplication.run(SampleApplication.class, args);
	}
}
```

### Controllerクラス
お試しのため、Serviceクラスは実装せずに直接Mapperクラスを呼び出し。
登録内容も固定値で登録。

```java:SampleController.java
package sample;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class SampleController {

  // Mapperクラスを生成
  @Autowired
  private final SampleMapper sampleMapper;

  @PostMapping(value = "/userId", produces = "application/json")
  public void getUserId() {

    int no = 1;
    String UserId = "1234567890";

    // ユーザー登録：Mapperクラスのinsertメソッドを呼び出してSQL実行。
    sampleMapper.insertUserId(no, UserId);
	}
}
```

### Mapperクラス

```java:SampleMapper.java
package sample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface SampleMapper {
  public void insertUserId(@Param("no") int no, @Param("UserId") String UserId);
}
```

### XMLファイル
```xml:SampleMapper.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 ="sample.SampleMapper">
  <insert id="insertUserId">
    INSERT INTO test (no, UserId) 
    VALUES (#{no}, #{UserId})
  </insert>
</mapper>
```


# 参考サイト
* [The MyBatis Blog（公式サイト）](https://blog.mybatis.org/)
* [MyBatis – MyBatis 3 | イントロダクション](https://mybatis.org/mybatis-3/ja/index.html)
* [mybatis-spring](https://mybatis.org/spring/ja/index.html)
* [オープンソースのフレームワーク / MyBatisとは](https://www.ossnews.jp/oss_info/MyBatis)
* [6.2. データベースアクセス（MyBatis3編） — TERASOLUNA Server Framework for Java](http://terasolunaorg.github.io/guideline/5.7.0.RELEASE/ja/ArchitectureInDetail/DataAccessDetail/DataAccessMyBatis3.html)
* [絶対分かるMyBatis！MyBatisで覚えるべきチェックルール25（前半） - Qiita](https://qiita.com/5zm/items/e1faac61a1d00998fb1f)