SIerに入って８年ぐらい経ったのでいろいろ棚卸しをしてみる。【vol.003 MyBatis】

## 出会い
[vol.002](https://qiita.com/TsJazz27Sumin/items/44d5e09fe4cf4ee98bfb)に書いた通り、自分にとっては初のコーディングPJで出会った。
自分が出会った当時は、iBATISと呼ばれていて後に後継としてMyBatisになっている。

## MyBatisとは
>MyBatis(マイバティス)とは、iBATIS(アイバティス)の後継プロジェクトで、マッピングファイルにSQL文を直接記述し「オブジェクトとSQL実行結果との間」でマッピングを行うという特徴を持つ、Javaおよび.NET Frameworkを対象とするORマッピングライブラリです。[オープンソースのフレームワーク／MyBatisとは](https://www.ossnews.jp/oss_info/MyBatis)

- JDBCだとSQLをJavaのクラスに書くことになるため、その点、密結合な状態だった。
- また、ResultSetから取得結果を取り出して、クラスにマッピングする分、手間があった。
- MyBatisだとSQLを外部xmlファイルに記述することで、クラスとSQLを疎結合な状態にできる。
- また、クラスへのマッピングはルールに基づいて記述すれば、自動マッピングしてくれる。
- つまり、JDBCに比べるとMyBatisはちょっと気の利くやつである。

## 振り返ってみて
２０１２年頃の経験に基づくが、SQLを外部xmlファイルに記述しているメリットもあったが、デメリットもあったように思う。

【メリット】
使用するSQLが特定のxmlにまとまっているため、SQLのみを一括修正したりするときに楽だった。
当時の現場では、機能単位ごとにxmlを分割していたので、調査・修正の際にSQLにたどり着くのは早かった気がする。

【デメリット】
動的にSQLを生成しないので検索パターンごとにSelectのクエリーを用意することになっていた。
検索条件のパターンが多いものほど不毛さを感じた。※[現在、MyBatisでは動的SQLをサポートしている。](http://www.mybatis.org/mybatis-3/ja/dynamic-sql.html)
当時のPJの先輩が「設計書から自動でSQLの外部ファイル生成」するExcelマクロを用意していたのを思い出した。

静的にSQLをxmlに定義しておいて、それを利用するのがMyBatisの良い使い方だと思う。
「動的にSQLを生成する」ことと「静的にSQLを定義しておく」ことを天秤にかけて、機能要件次第で採用判断になるのかなと思う。

経験上、Webアプリの検索機能でよっぽどシンプルな検索条件でもない限り、多数の検索クエリーに対応することになる。パターンごとにSQLを静的に用意しておくと、SQLが爆裂するのでどうしても動的にSQLを生成することを検討せざるをえない。

個人的には、SQLが外部ファイル化されているメリットってそんなに感じないので、動的SQLに対応していたとしてもMyBatisは使わない気がする。ただ、よっぽど大きなPJで「SQLだけ書くDB寄りのチーム」と「Javaでコーディングするチーム」で分けた方が生産性が高くなるなら採用するのかもしれない。

## サンプルで実装してみる。
以下を参考にさせていただきました。

 - [公式ドキュメント](http://www.mybatis.org/mybatis-3/ja/getting-started.html)
 - [MyBatis 使い方メモ](https://qiita.com/opengl-8080/items/a9b5d4038f19d4813ee6)
 - [絶対分かるMyBatis！MyBatisで覚えるべきチェックルール25（前半）](https://qiita.com/5zm/items/e1faac61a1d00998fb1f)

```java
package sample.mybatis.java.dao;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import sample.mybatis.java.dao.mapper.WorkerMapper;
import sample.mybatis.java.model.Worker;

public class SampleDao {

	public static SampleDao instance = new SampleDao();

	private SampleDao () {

	}

	public List<Worker> select() throws SQLException, IOException {

		List<Worker> workerList = null;

		try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
            	var mapper = session.getMapper(WorkerMapper.class);
                workerList = mapper.selectAll();
            }
        }

        return workerList;
	}

	public void insert() throws SQLException, IOException {

		try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
            	var mapper = session.getMapper(WorkerMapper.class);
            	var worker = new Worker("0001", "k.jarrett", 73, "music");
            	mapper.insert(worker);
            	session.commit();
            }
        }
	}

	public void update() throws SQLException, IOException {

		try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
            	var mapper = session.getMapper(WorkerMapper.class);
            	var worker = new Worker("0001", "k.jarrett", 74, "music");
            	mapper.update(worker);
            	session.commit();
            }
        }
	}

	public void delete() throws SQLException, IOException {

		try (var in = Resources.getResourceAsStream("mybatis-config.xml")) {
            var factory = new SqlSessionFactoryBuilder().build(in);

            try (var session = factory.openSession()) {
            	var mapper = session.getMapper(WorkerMapper.class);
            	var worker = new Worker("0001", "k.jarrett", 74, "music");
            	mapper.delete(worker);
            	session.commit();
            }
        }
	}
}

```

```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.mybatis.java.dao.mapper.WorkerMapper">
  <resultMap id="workerResultMap" type="sample.mybatis.java.model.Worker">
  	<result property="id" column="id" />
  	<result property="name" column="name" />
 	<result property="age" column="age" />
 	<result property="department" column="department" />
  </resultMap>
  <select id="selectAll" resultMap="workerResultMap">
    select * from worker
  </select>
  <insert id="insert" parameterType="sample.mybatis.java.model.Worker">
    insert into worker
    values (
      #{id},
      #{name},
      #{age},
      #{department}
    )
  </insert>
  <update id="update" parameterType="sample.mybatis.java.model.Worker">
    update worker
    set name = #{name},
        age = #{age},
        department = #{department}
    where id =  #{id}
  </update>
  <delete id="delete" parameterType="sample.mybatis.java.model.Worker">
    delete from worker
    where id = #{id}
  </delete>
</mapper>
```

```java
package sample.mybatis.java.dao.mapper;

import java.util.List;

import sample.mybatis.java.model.Worker;

public interface WorkerMapper {

	List<Worker> selectAll();
	void insert(Worker worker);
	void update(Worker worker);
	void delete(Worker worker);
}
```

サンプルプロジェクトごとGitHubにプッシュしています。
https://github.com/TsJazz27Sumin/mybatisSample/tree/master/src

## サンプルで実装してみて感想。

 - Javaのクラスとxmlを行ったり来たりするのがめんどくさい。
 - 一度、設定すれば変更の手間はそんなにかからないのだろうけど、やっぱり設定多い。
 - だいぶアノテーションを使って楽にできるように進化しているみたいだけど、これならJPAで良い気がする。（※[MyBatis Mapper アノテーションの使い方](https://qiita.com/d-yosh/items/a2bec9718bccfe15a6ae)）
 - リフレクションのエラー：`java.lang.reflect.InaccessibleObjectException: Unable to make ~`にハマッた。
  - `module-info.java`に`exports xxx;`を追加すればいいだけだけど、すぐ分からなかった。。