MyBatis 使い方メモ

# MyBatis とは
SQL と Java オブジェクトを紐付ける永続化フレームワーク。

以前は iBATIS という名前で Apache プロジェクトの１つとして開発されていた。
しかし、 2010年6月に Apache ソフトウェア財団での開発が中止され、現在は MyBatis という名前で開発されている。

SQL 文を完全にコントロールしたい場合に使いやすいらしい。

# 環境
## OS
Windows 7 64bit

## Java
1.8.0_65

## データベース
MySQL 5.5.28

# Hello World
## インストール
```groovy:build.gradle
    compile 'org.mybatis:mybatis:3.3.0'
    compile 'mysql:mysql-connector-java:5.1.38'
```

## データベースの準備
![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cb00c03b-e57d-7ad2-6d9e-0b0b6da74cef.jpeg)

## フォルダ構成
```text:
|-src/main/
|  |-java/
|  |  `-sample/mybatis/
|  |     `-Main.java
|  `-resources/
|     |-mybatis-config.xml
|     `-sample_mapper.xml
`-build.gradle
```

## 設定ファイル
```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="sample_id">
    <environment id="sample_id">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/test"/>
        <property name="username" value="test"/>
        <property name="password" value="test"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="sample_mapper.xml"/>
  </mappers>
</configuration>
```

```xml:sample_mapper.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">
  <select id="selectTest" resultType="map">
    select * from test_table
  </select>
</mapper>
```

## 実装
```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        // ★ルートとなる設定ファイルを読み込む
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            // ★設定ファイルを元に、 SqlSessionFactory を作成する
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            // ★SqlSessionFactory から SqlSession を生成する
            try (SqlSession session = factory.openSession()) {
                // ★SqlSession を使って SQL を実行する
                List<Map<String, Object>> result = session.selectList("sample.mybatis.selectTest");
                
                result.forEach(row -> {
                    System.out.println("---------------");
                    row.forEach((columnName, value) -> {
                        System.out.printf("columnName=%s, value=%s%n", columnName, value);
                    });
                });
            }
        }
    }
}
```

```text:実行結果
---------------
columnName=id, value=1
columnName=value, value=hoge
---------------
columnName=id, value=2
columnName=value, value=fuga
---------------
columnName=id, value=3
columnName=value, value=piyo
```

## 説明
- 設定ファイル
    - まず、データベースとの接続情報などを定義したメインとなる設定ファイルを用意する（`mybatis-config.xml`）。
    - 次に、 SQL 文や検索結果のマッピング方法などを定義したファイルを用意する（`sample_mapper.xml`）。
- 実装
    - まず、 `SqlSessionFactoryBuilder` を使って、設定ファイル（`mybatis-config.xml`）を読み込む。
    - `SqlSessionFactory` を使って、 `SqlSession` を生成する。
    - `SqlSession` に用意されている SQL 実行用のメソッドを使って SQL を実行する（`selectList()`）。
    - 第一引数には、実行する SQL を識別するための ID （ステートメントID）を指定する。
    - ステートメント ID や戻り値の型は、 `sample_mapper.xml` で定義された情報が元になっている。

### 各インスタンスの推奨スコープ
|        インスタンス        |                             説明                            |       推奨スコープ       |
|----------------------------|-------------------------------------------------------------|--------------------------|
| `SqlSessionFactoryBuilder` | `SqlSessionFactory` を作ったら用済み。                      | メソッドスコープ         |
| `SqlSessionFactory`        | 一度作ったら、それを使い回すべき。                          | アプリケーションスコープ |
| `SqlSession`               | 非スレッドセーフ。一連の SQL 実行が終わったらクローズする。 | リクエストスコープ       |
| `Mapper` インスタンス※    | `SqlSession` と同調すべきだが、より短いスコープが良い。     | メソッドスコープ         |

※Mapper については [こちら](#mapper) を参照。

- メソッドスコープ
    - メソッド内だけで使い捨てる。
- リクエストスコープ
    - Web アプリで、１つのリクエストの間だけ同じインスタンスを使い回す。
    - 他のリクエストからは参照させないようにする。
- アプリケーションスコープ
    - アプリケーションが動いている間ずっと同じインスタンスを使い回す。


# 設定ファイル
## 外部のプロパティファイルを読み込む
```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <properties resource="hoge.properties">
    <property name="hoge" value="property tag"/>
    <property name="fuga" value="property tag"/>
    <property name="piyo" value="property tag"/>
  </properties>
</configuration>
```

```properties:hoge.properties
fuga=property file
piyo=property file
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.Properties;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            Properties prop = new Properties();
            prop.put("piyo", "Properties Class");
            
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in, prop);
            Properties properties = factory.getConfiguration().getVariables();
            
            properties.forEach((key, value) -> {
                System.out.printf("%s=%s%n", key, value);
            });
        }
    }
}
```

```text:実行結果
hoge=property tag
fuga=property file
piyo=Properties Class
```

- 以下の場所でプロパティを定義できる。
    1. `<properties>` タグ内の `<property>` タグ。
    1. `<properties>` タグで読み込んだプロパティファイル。
    1. `SqlSessionFactory` を生成するときに `Properties` インスタンスを渡す。
- 各定義は上述の順序で読み込まれ、後で読み込まれたもので上書きされる。
- `<properties>` タグで別ファイルを読み込む場合は、 `resource` 属性でリソースファイルを、 `url` 属性で URL 指定でファイルを読み込める。
    - `url` 属性で指定する場合は `file:///C:/foo/bar/hoge.properties` みたいな感じで指定する。
- 読み込んだプロパティは、 `${...}` という形で参照できる。

## 複数のデータベース接続を定義する
データベースの接続先情報は、 `environment` という単位で定義する。
`SqlSessionFactory` は、この `environment` 単位でインスタンスを生成することになる。

デフォルトでは `<environments>` タグの `default` 属性で指定した `environment` が使用される。
しかし、 `SqlSessionFactory` を生成するときに任意の `environment` を指定することもできる。

**test_table @ hoge スキーマ**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ce770d83-c7c5-1e40-388d-d0bc227eeac5.jpeg)

**test_table @ fuga スキーマ**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/888fb8fc-26da-af4a-7e12-a8abf5e62428.jpeg)

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="hoge"> <!-- ★hoge をデフォルト指定 -->
    <environment id="hoge">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/hoge"/>
        <property name="username" value="test"/>
        <property name="password" value="test"/>
      </dataSource>
    </environment>
    
    <environment id="fuga">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/fuga"/>
        <property name="username" value="test"/>
        <property name="password" value="test"/>
      </dataSource>
    </environment>
  </environments>
  
  <mappers>
    <mapper resource="sample_mapper.xml"/>
  </mappers>
</configuration>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.function.Function;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        execute(in -> new SqlSessionFactoryBuilder().build(in)); // ★environment を指定しない
        execute(in -> new SqlSessionFactoryBuilder().build(in, "fuga")); // ★environment を指定
    }
    
    private static void execute(Function<InputStream, SqlSessionFactory> sqlSessionFactorySupplier) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = sqlSessionFactorySupplier.apply(in);
            
            try (SqlSession session = factory.openSession()) {
                System.out.println(session.selectList("sample.mybatis.selectTest"));
            }
        }
        System.out.println();
    }
}
```

```text:実行結果
[{id=1, value=fizz}, {id=2, value=buzz}]

[{id=1, value=hoge}, {id=2, value=fuga}, {id=3, value=piyo}]
```

- `SqlSessionFactoryBuilder#build()` の第二引数で、 `environment` を指定することができる。

## Java EE サーバーに管理されたデータソースを使用する
Java EE サーバー上で動かす場合、データソースはサーバーに登録しておいて、トランザクション制御は Java EE コンテナに任せることができる。

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="test">
    <environment id="test">
      <transactionManager type="MANAGED"/> <!-- ★type を MANAGED にする -->
      
      <dataSource type="JNDI"> <!-- ★JNDI にする -->
        <!-- ★data_source で JNDI からルックアップするための名を指定する -->
        <property name="data_source" value="java:app/sampleDS"/>
      </dataSource>
    </environment>
  </environments>
  
  <mappers>
    <mapper resource="sample_mapper.xml"/>
  </mappers>
</configuration>
```

- `<transactionManager>` タグの `type` を `MANAGED` にする。
- `<datasource>` タグの `type` を `JNDI` にする。
- `<property>` タグで、 `data_source` を設定する。
    - 値は JNDI ルックアップするための名前にする。

# 実行される SQL をログに出力する
logback の依存関係を追加すれば、 DEBUG レベルで出力される。

```groovy:build.gradle
    compile 'ch.qos.logback:logback-classic:1.1.3'
```

```text:ログ出力
18:10:01.123 [main] DEBUG sample.mybatis.selectTest - ==>  Preparing: select * from test_table where id=? and id=? 
18:10:01.151 [main] DEBUG sample.mybatis.selectTest - ==> Parameters: 1(Integer), 1(Integer)
18:10:01.165 [main] DEBUG sample.mybatis.selectTest - <==      Total: 1
```

実行された SQL と、バインドされたパラメータが出力されている。

実行した SQL のステートメント ID （`sample.mybatis.selectTest`）がロガー名になっているので、ステートメント ID を利用したログ出力の制御ができるようになっている。

# 検索
## 複数件の結果を取得する
```xml:sample_mapper.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">
  <select id="selectTest" resultType="map">
    select * from test_table
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                List<Map<String, Object>> result = session.selectList("sample.mybatis.selectTest");
                System.out.println(result);
            }
        }
    }
}
```

```text:実行結果
[{id=1, value=fizz}, {id=2, value=buzz}]
```

- 検索用の SQL は、 `<select>` タグで宣言する。
    - `id` 属性で、その SQL を参照するための識別名を設定する。
    - `resultType` 属性で、検索結果のレコードをどの型にマッピングするかを定義する。
    - ここでは `map` と指定しているので、 `Map` 型に変換される。
- 検索結果が複数のレコードを返す場合、 `SqlSession` の `selectList()` メソッドを使用する。
    - 戻り値は `List` 型になる。

## 単一の結果を取得する
```xml:sample_mapper.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">
  <select id="selectTest" resultType="map">
    select * from test_table limit 1
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                Map<String, Object> result = session.selectOne("sample.mybatis.selectTest");
                System.out.println(result);
            }
        }
    }
}
```

```text:実行結果
{id=1, value=fizz}
```

- 検索結果が１件だけで単一のオブジェクトで取得したい場合は、 `selectOne()` メソッドを使用する。
- もし検索結果が複数のレコードを返した場合、 `TooManyResultsException` がスローされる。
- 検索結果が 0 件の場合は `null` を返す。


## resultType で指定できる値
`resultType` には、検索結果を Java のどの型に変換するかを設定する。

通常は `java.util.Map` のように変換先クラスの FQCN を指定する。

しかし、一部のよく利用する型については予めエイリアスが定義されており、短い名前で指定できる。

以下、 `org.apache.ibatis.type.TypeAliasRegistry` より抽出。

|  エイリアス  |    型      |
|--------------|--------------|
| string       | String       |
| date         | Date         |
| map          | Map          |
| hashmap      | HashMap      |
| list         | List         |
| arraylist    | ArrayList    |
| decimal      | BigDecimal   |
| bigdecimal   | BigDecimal   |
| biginteger   | BigInteger   |
| _byte        | byte         |
| _long        | long         |
| _short       | short        |
| _int         | int          |
| _integer     | int          |
| _double      | double       |
| _float       | float        |
| _boolean     | boolean      |
| _byte[]      | byte[]       |
| _long[]      | long[]       |
| _short[]     | short[]      |
| _int[]       | int[]        |
| _integer[]   | int[]        |
| _double[]    | double[]     |
| _float[]     | float[]      |
| _boolean[]   | boolean[]    |
| byte         | Byte         |
| long         | Long         |
| short        | Short        |
| int          | Integer      |
| integer      | Integer      |
| double       | Double       |
| float        | Float        |
| boolean      | Boolean      |
| byte[]       | Byte[]       |
| long[]       | Long[]       |
| short[]      | Short[]      |
| int[]        | Integer[]    |
| integer[]    | Integer[]    |
| double[]     | Double[]     |
| float[]      | Float[]      |
| boolean[]    | Boolean[]    |
| object       | Object       |
| date[]       | Date[]       |
| decimal[]    | BigDecimal[] |
| bigdecimal[] | BigDecimal[] |
| biginteger[] | BigInteger[] |
| object[]     | Object[]     |
| collection   | Collection   |
| iterator     | Iterator     |
| ResultSet    | ResultSet    |

プリミティブ型の場合は `_` を前に付ける。付けない場合はラッパークラスになるので注意。

### エイリアスを定義する
エイリアスは任意に定義できる。

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <typeAliases>
    <typeAlias type="foo.bar.Hoge" alias="Hoge" />
  </typeAliases>

  ...
</configuration>
```

- メインの方の設定ファイル（`mybatis-config.xml`）に `<typeAlias>` タグで宣言できる。

### パッケージを指定してエイリアスを定義する
```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <typeAliases>
    <package name="foo.bar"/>
  </typeAliases>

  ...
</configuration>
```

- `<package>` タグを使うことで、パッケージ単位でエイリアスを定義できる。
- この場合、クラス名がそのままエイリアスになる。

## パラメータ
### 単一の場合
```xml:sample_mapping.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">
  <select id="selectTest" resultType="map">
    select * from test_table where id=#{id}
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                Map<String, Object> result = session.selectOne("sample.mybatis.selectTest", 1);
                System.out.println(result);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=? 
[DEBUG] s.m.selectTest  - ==> Parameters: 1(Integer)
[DEBUG] s.m.selectTest  - <==      Total: 1
{id=1, value=fizz}
```

- `#{...}` という形式でパラメータを宣言できる。
- パラメータは、 `SqlSession` で SQL を実行するときに、ステートメント ID （`sample.mybatis.selectTest`）の次に引数で渡す。

### 複数の場合
```xml:sample_mapper.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">
                                           <!-- ★パラメータの型を指定する -->
  <select id="selectTest" resultType="map" parameterType="sample.mybatis.MyParameter">
    select *
      from test_table
     where id=#{id}
       and value=#{value}
  </select>
</mapper>
```

```java:MyParameter.java
package sample.mybatis;

public class MyParameter {
    
    private int id;
    private String value;
    
    public MyParameter(int id, String value) {
        this.id = id;
        this.value = value;
    }

    public int getId() {
        System.out.println("get id");
        return id;
    }
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                MyParameter param = new MyParameter(2, "aaa");

                Map<String, Object> result = session.selectOne("sample.mybatis.selectTest", param);
                System.out.println(result);
            }
        }
    }
}
```

```text:実行結果
get id
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=? and value=? 
get id
[DEBUG] s.m.selectTest  - ==> Parameters: 2(Integer), aaa(String)
```

- 複数のパラメータを埋め込む場合、何かしらのクラスのインスタンスに値を入れて、それを渡すようにする。
- 入れ物となるクラスは、 `<select>` タグの `parameterType` 属性で型を指定しておく。
- 宣言したパラメータの名前と一致するフィールド（プロパティ）の値がインスタンスから取得される。
- 値は、 Getter メソッドがあればそれ経由で、なければフィールドから直接取得される（`private` でも取得される模様）。

### Map でパラメータを渡す
複数のパラメータを渡すには、 Map を使う方法も用意されている。

```xml:sample_mapper.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">
  <select id="selectTest" resultType="map">
    select *
      from test_table
     where id=#{id}
       and value=#{value}
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                Map<String, Object> param = new HashMap<>();
                param.put("id", 10);
                param.put("value", "hogeeee");
                
                Map<String, Object> result = session.selectOne("sample.mybatis.selectTest", param);
                System.out.println(result);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=? and value=? 
[DEBUG] s.m.selectTest  - ==> Parameters: 10(Integer), hogeeee(String)
```

- パラメータは `Map` でも渡すことができる。
- その場合は、 `<select>` タグの `parameterType` の設定は無くても動く。

### 特殊なパラメータ名
以下のパラメータ名は、 MyBatis によって自動で登録される特殊なパラメータ名で、暗黙的に利用できる。

| パラメータ名 |                          条件                          |
|--------------|--------------------------------------------------------|
| `_parameter` | （たぶん）常に参照可能                                 |
| `collection` | パラメータが `java.util.Collection` を実装している場合 |
| `list`       | パラメータが `java.util.List` を実装している場合       |
| `array`      | パラメータが配列の場合                                 |

- `_parameter` を設定している場所。
    - `org.apache.ibatis.scripting.xmltags.DynamicContext`
- `collection`, `list`, `arrary` を設定している場所。
    - `org.apache.ibatis.session.defaults.DefaultSqlSession#wrapCollection(Object)`


## 検索結果を任意の Java オブジェクトにマッピングする
### 基本
![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6c6d9aca-9552-8924-363c-9933ed6145ae.jpeg)

```xml:sample_mapper.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">
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select * from test_table
  </select>
</mapper>
```

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    
    private int id;
    private String value;
    
    public void setValue(String value) {
        System.out.println("setValue(" + value + ")");
        this.value = value;
    }

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", value=" + value + "]";
    }
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                List<TestTable> result = session.selectList("sample.mybatis.selectTest");
                System.out.println(result);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table 
[DEBUG] s.m.selectTest  - ==> Parameters: 
setValue(fizz)
setValue(buzz)
[DEBUG] s.m.selectTest  - <==      Total: 2
[TestTable [id=1, value=fizz], TestTable [id=2, value=buzz]]
```

- `resultType` に検索結果をマッピングしたい Java クラスを指定する。
- カラム名と一致するフィールドに値がセットされる。
- 値のセットは、 Setter メソッドがあればそれ経由で、なければフィールドに直接行われる。

### スネークケースとキャメルケースのマッピングを自動化させる

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/48e5cf19-8e6c-612b-1d65-f469082f56d3.jpeg)

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    private int id;
    private String testValue;

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", testValue=" + testValue + "]";
    }
}
```

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
  </settings>

  ...
</configuration>
```

```xml:sample_mapper.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">

  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select * from test_table
  </select>
</mapper>
```

```text:selectTestの実行結果
TestTable [id=1, testValue=hoge]
TestTable [id=2, testValue=fuga]
TestTable [id=3, testValue=piyo]
```

- `mapUnderscoreToCamelCase` に `true` を設定することで、スネークケースとキャメルケースの自動変換をしてくれるようになる。
- デフォルトは `false` になっている。

### クラスごとにマッピングを定義する
```xml:sample_mapper.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">
  <resultMap id="testTableResultMap" type="sample.mybatis.TestTable">
    <id property="id" column="id" />
    <result property="value" column="value" />
  </resultMap>

  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>
```

- `<resultMap>` タグを使って、検索結果と Java クラスとのマッピングを定義できる。
- `<id>` タグで、識別子プロパティを定義する。
    - 識別子プロパティは、インスタンスを識別するときに使用するプロパティを指す。
    - 識別子プロパティを指定しておくことで、キャッシュや JOIN マッピングのときにパフォーマンスが向上するらしい。
    - `property` 属性で、 Java 側のプロパティ（フィールド）名を指定する。
    - `column` 属性で、データベース側の列名を指定する。
- `<result>` タグで、各列のマッピングを定義する。
    - こちらも `property` と `column` で名前をマッピングする。
- `<resultMap>` の `id` 属性でマッピングを一意に特定するための名前を定義する（`testTableResultMap`）。
    - この名前を、 `<select>` タグの `resultMap` 属性に指定する。

### 単一のオブジェクトをマッピングする
#### 基本
**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/df1a7bf6-821b-ed53-d7f2-dc88a6bb6bc8.jpeg)

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    
    private int id;
    private String string;
    private Embedded embedded;
    
    @Override
    public String toString() {
        return "TestTable [id=" + id + ", string=" + string + ", embedded=" + embedded + "]";
    }
}
```

```java:Embedded.java
package sample.mybatis;

public class Embedded {
    
    private int number;

    @Override
    public String toString() {
        return "Embedded [number=" + number + "]";
    }
}
```

```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.TestTable" id="testTableResultMap">
    <id property="id" column="id" />
    <result property="string" column="string" />

    <!-- ★association タグで定義 -->
    <association property="embedded" javaType="sample.mybatis.Embedded">
      <result property="number" column="number" />
    </association>
  </resultMap>


  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                session
                    .selectList("sample.mybatis.selectTest")
                    .forEach(System.out::println);
            }
        }
    }
}
```

```text:実行結果
TestTable [id=1, string=hoge, embedded=Embedded [number=100]]
TestTable [id=2, string=fuga, embedded=Embedded [number=200]]
TestTable [id=3, string=piyo, embedded=Embedded [number=300]]
```

- `<association>` タグで、単一のクラスの埋め込みをマッピングできる。
- `javaType` 属性で、埋め込むクラスを指定する（エイリアス可）。
- 子要素で、埋め込みクラスのマッピングを定義する。

#### 埋め込むクラスの定義を外出しする
```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.TestTable" id="testTableResultMap">
    <id property="id" column="id" />
    <result property="string" column="string" />
    
    <!-- ★resutlMap を指定 -->
    <association property="embedded" resultMap="embeddedResultMap" />
  </resultMap>
  
  <!-- ★別途 resultMap で定義 -->
  <resultMap type="sample.mybatis.Embedded" id="embeddedResultMap">
    <result property="number" column="number" />
  </resultMap>


  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>
```

- `<resultMap>` で別途定義できる。

#### カラム名にプレフィックスを指定する
**foo_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/250f99b7-7c1c-f090-1cc4-a6fe252fbd1a.jpeg)

**bar_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/32c94698-4e52-c283-3363-868b46cff430.jpeg)

```java:Foo.java
package sample.mybatis;

public class Foo {
    private int id;
    private Bar bar;
    
    @Override
    public String toString() {
        return "Foo [id=" + id + ", bar=" + bar + "]";
    }
}
```

```java:Bar.java
package sample.mybatis;

public class Bar {
    private int id;

    @Override
    public String toString() {
        return "Bar [id=" + id + "]";
    }
}
```

```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
    <!-- ★columnPrefix で共通するプレフィックスを指定する -->
    <association property="bar" columnPrefix="bar_" resultMap="barResultMap" />
  </resultMap>
  
  <resultMap type="sample.mybatis.Bar" id="barResultMap">
    <id property="id" column="id" />
  </resultMap>

  <select id="selectFoo" resultMap="fooResultMap">
    select foo.id
          ,bar.id bar_id -- ★"bar_" をプレフィックスとして付ける
      from foo_table foo
          ,bar_table bar
     where bar.id = foo.bar_id
  order by foo.id asc
  </select>
</mapper>
```

```text:selectFooの実行結果
Foo [id=1, bar=Bar [id=3]]
Foo [id=2, bar=Bar [id=2]]
Foo [id=3, bar=Bar [id=1]]
```

- JOIN とかしたときに別テーブルの同名カラムと区別するために、よくプレフィックス（`bar_`）を付けると思う。
- そういうときでも `resultMap` を使い回すことができるよう、 `columnPrefix` という属性が用意されている。
- `columnPrefix` を指定すれば、 `column` の設定値にプレフィックスを付けた名称でマッピングが行われるようになる。

#### SELECT を分けて実行する
```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
    <association property="bar" column="bar_id" select="selectBar" />
  </resultMap>
  
  <select id="selectFoo" resultMap="fooResultMap">
    select * from foo_table
  </select>
  
  <select id="selectBar" resultType="sample.mybatis.Bar">
    select * from bar_table where id = #{id}
  </select>
</mapper>
```

```text:selectFooの実行結果
[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 2(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 3(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectFoo   - <==      Total: 3
Foo [id=3, bar=Bar [id=1]]
Foo [id=2, bar=Bar [id=2]]
Foo [id=1, bar=Bar [id=3]]
```

- `foo_table` を検索したあと、各列に対して `bar_table` の検索を別途実行している。
- `<association>` タグの `select` 属性で、 `<select>` タグのステートメントID を指定することで、こういう検索にできる。
- `column` 属性には、子テーブルの JOIN に使用するカラムを指定する。

#### JOIN キーが複数ある場合
**foo_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d9a80959-7e96-35ba-6b75-b7b4134a23ce.jpeg)

**bar_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6b2f2eb3-3be3-db16-0364-c529b6ca7063.jpeg)

```java:Bar.java
package sample.mybatis;

public class Bar {
    private int key1;
    private String key2;

    @Override
    public String toString() {
        return "Bar [key1=" + key1 + ", key2=" + key2 + "]";
    }
}
```

```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
                                <!-- ★キーが複数ある場合の指定方法 -->
    <association property="bar" column="{key1=bar_key1,key2=bar_key2}" select="selectBar" />
  </resultMap>
  
  <select id="selectFoo" resultMap="fooResultMap">
    select * from foo_table
  </select>
  
  <select id="selectBar" resultType="sample.mybatis.Bar">
    select *
      from bar_table
     where key1 = #{key1}
       and key2 = #{key2}
  </select>
</mapper>
```

```text:selectFooの実行結果
[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer), fuga(String)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer), hoge(String)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 2(Integer), hoge(String)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectFoo   - <==      Total: 3
Foo [id=2, bar=Bar [key1=1, key2=fuga]]
Foo [id=3, bar=Bar [key1=1, key2=hoge]]
Foo [id=1, bar=Bar [key1=2, key2=hoge]]
```

- JOIN するときのカラムが複数存在する場合は、 `{prop1=column1,prop2=column2}` という形で `column` 属性を指定する。

#### 関連するオブジェクトの読み込みを遅延させる
```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
    <association property="bar" column="{key1=bar_key1,key2=bar_key2}" select="selectBar"
                 fetchType="lazy" /> <!-- ★fetchType に lazy を指定 -->
  </resultMap>
  
  <select id="selectFoo" resultMap="fooResultMap">
    select * from foo_table
  </select>
  
  <select id="selectBar" resultType="sample.mybatis.Bar">
    select *
      from bar_table
     where key1 = #{key1}
       and key2 = #{key2}
  </select>
</mapper>
```

```java:Foo.java
package sample.mybatis;

public class Foo {
    private int id;
    public Bar bar;
    
    public void method() {
        System.out.println("Foo.method() is called");
    }
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                System.out.println("@selectList()");
                List<Foo> result = session.selectList("sample.mybatis.selectFoo");
                
                System.out.println("@result.get(0)");
                Foo foo = result.get(0);
                
                System.out.println("** foo.class = " + foo.getClass() + " **");
                
                System.out.println("@foo.bar 1");
                System.out.println("<< foo.bar = " + foo.bar + " >>");

                System.out.println("@foo.method()");
                foo.method();

                System.out.println("@foo.bar 2");
                System.out.println("<< foo.bar = " + foo.bar + " >>");
            }
        }
    }
}
```

```text:実行結果
@selectList()
[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectFoo   - <==      Total: 3
@result.get(0)
** foo.class = class sample.mybatis.Foo_$$_jvst80f_0 **
@foo.bar 1
<< foo.bar = null >>
@foo.method()
[DEBUG] s.m.selectBar   - ==>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ==> Parameters: 1(Integer), fuga(String)
[DEBUG] s.m.selectBar   - <==      Total: 1
Foo.method() is called
@foo.bar 2
<< foo.bar = Bar [key1=1, key2=fuga] >>
```

- `<association>` の `fetchType` 属性に `lazy` とセットすると、関連するオブジェクトの読み込みを遅延させることができる。
    - デフォルトは即時ロード（`eager`）。
- 遅延が設定されたクラスのインスタンスは、 MyBatis によって生成されたプロキシになる（`class sample.mybatis.Foo_$$_jvst80f_0`）。
    - 遅延ロードでない場合は、普通のインスタンスが渡される。
- 遅延ロードが実行されるタイミングは、「プロキシのいずれかのメソッドが実行されたときの先頭」の模様。
- フィールドが読み取られたときではないようなので、メソッド実行前に参照すると `null` になっている（まぁ、直接参照することはないと思うけど、一応注意）。

全ての関連を遅延ロードさせたい場合は、ルートの設定で `lazyLoadingEnabled` を設定する方法がある。

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="lazyLoadingEnabled" value="true"/> <!-- ★true にする -->
  </settings>

  ...
</configuration>
```

この場合でも、 `<association>` タグの `fetchType` の方が優先されるので、特定の読み込みだけ即時ロードにしたい場合は、設定を上書きできる。

### コレクションをマッピングする
#### 基本
**foo_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/dfd9b494-17f2-48c8-43c6-33df314bdb14.jpeg)

**bar_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/84977609-0910-e257-4f1a-e3c5f98b1f16.jpeg)

```java:Foo.java
package sample.mybatis;

import java.util.List;

public class Foo {
    private int id;
    private List<Bar> barList;
    
    @Override
    public String toString() {
        return "Foo [id=" + id + ", barList=" + barList + "]";
    }
}
```

```java:Bar.java
package sample.mybatis;

public class Bar {
    private int id;

    @Override
    public String toString() {
        return "Bar [id=" + id + "]";
    }
}
```

```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
    <!-- ★collection タグを使用する -->
    <collection property="barList" ofType="sample.mybatis.Bar">
      <id property="id" column="bar_id" />
    </collection>
  </resultMap>
  
  <select id="selectFoo" resultMap="fooResultMap">
    select foo.id
          ,bar.id bar_id
      from foo_table foo
          ,bar_table bar
     where bar.foo_id = foo.id
  </select>
</mapper>
```

```text:selectFooの実行結果
[DEBUG] s.m.selectFoo   - ==>  Preparing: select foo.id ,bar.id bar_id from foo_table foo ,bar_table bar where bar.foo_id = foo.id 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectFoo   - <==      Total: 6
Foo [id=1, barList=[Bar [id=1], Bar [id=2], Bar [id=3]]]
Foo [id=2, barList=[Bar [id=4], Bar [id=5]]]
Foo [id=3, barList=[Bar [id=6]]]
```

- コレクションをマッピングする場合は、 `<collection>` タグを使用する。
- 各要素の型は `ofType` 属性で指定する（エイリアス可）。
- それ以外は、 `<association>` タグのときと同じノリでいける。

#### SELECT を分割して実行する
```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
    <collection property="barList" column="id" select="selectBar" />
  </resultMap>
  
  <select id="selectFoo" resultMap="fooResultMap">
    select * from foo_table
  </select>
  
  <select id="selectBar" resultType="sample.mybatis.Bar">
    select * from bar_table where foo_id = #{id}
  </select>
</mapper>
```

```text:selectFooの実行結果
[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where foo_id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 3
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where foo_id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 2(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 2
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where foo_id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 3(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectFoo   - <==      Total: 3
Foo [id=1, barList=[Bar [id=1], Bar [id=2], Bar [id=3]]]
Foo [id=2, barList=[Bar [id=4], Bar [id=5]]]
Foo [id=3, barList=[Bar [id=6]]]
```

- `<association>` のときと同じノリでできる。
- 遅延ロードも同じく可能。

### 条件によって異なるクラスにマッピングする
主に継承関係にあるクラスをマッピングするときに使用する方法。

**base_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/676f5ee0-a915-4610-460f-f9578313dd3b.jpeg)

```java:Base.java
package sample.mybatis;

public class Base {
    protected int id;
    protected String commonValue;

    @Override
    public String toString() {
        return "Base [id=" + id + ", commonValue=" + commonValue + "]";
    }
}
```

```java:Hoge.java
package sample.mybatis;

public class Hoge extends Base {
    private String value;

    @Override
    public String toString() {
        return "Hoge [value=" + value + ", id=" + id + ", commonValue=" + commonValue + "]";
    }
}
```

```java:Fuga.java
package sample.mybatis;

public class Fuga extends Base {
    private String value;

    @Override
    public String toString() {
        return "Fuga [value=" + value + ", id=" + id + ", commonValue=" + commonValue + "]";
    }
}
```

```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Base" id="baseResultMap">
    <id property="id" column="id" />
    <result property="commonValue" column="common_value" />
    
    <discriminator javaType="int" column="type">
      <case value="1" resultType="sample.mybatis.Hoge">
        <result property="value" column="hoge_value" />
      </case>
      <case value="2" resultType="sample.mybatis.Fuga">
        <result property="value" column="fuga_value" />
      </case>
    </discriminator>
  </resultMap>
  
  <select id="selectBase" resultMap="baseResultMap">
    select * from base_table
  </select>
</mapper>
```

```text:selectBaseの実行結果
Hoge [value=hoge1, id=1, commonValue=common1]
Fuga [value=fuga2, id=2, commonValue=common2]
Base [id=3, commonValue=common3]
```

- `<discriminator>` タグを使うことで、条件によって異なるクラスにマッピングさせることができるようになる。
    - `column` 属性で、条件値を持つカラムを指定する。
- `<case>` タグで、条件値ごとにどういうマッピングを行うかを定義する。
    - ここでは、 `1` の場合は `Hoge` クラスに、 `2` の場合は `Fuga` クラスにマッピングするように定義している。
- `<case>` 式に存在しない条件値の場合は、もともとの `<resultMap>` タグで指定していた `type` にマッピングされる。

#### ケースごとのマッピングを外出しする
```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Base" id="baseResultMap">
    <id property="id" column="id" />
    <result property="commonValue" column="common_value" />
    
    <discriminator javaType="int" column="type">
      <case value="1" resultMap="hogeResultMap" />
      <case value="2" resultMap="fugaResultMap" />
    </discriminator>
  </resultMap>
  
  <resultMap type="sample.mybatis.Hoge" id="hogeResultMap">
    <result property="value" column="hoge_value" />
  </resultMap>
  
  <resultMap type="sample.mybatis.Fuga" id="fugaResultMap">
    <result property="value" column="fuga_value" />
    <result property="commonValue" column="common_value" />
  </resultMap>
  
  <select id="selectBase" resultMap="baseResultMap">
    select * from base_table
  </select>
</mapper>
```

```text:実行結果
Hoge [value=hoge1, id=1, commonValue=null]
Fuga [value=fuga2, id=2, commonValue=common2]
Base [id=3, commonValue=common3]
```

- `<case>` ごとの定義は、 `<resultMap>` で抽出できる。
- ただし、その場合共通部分で定義していたカラムは `<id>` 以外はマッピングされないので、個々の `<resultMap>` で定義しないといけない。
    - `hogeResultMap` は、 `commonValue` を定義していないので `null` になっている。
- いちいち個々の `<resultMap>` で共通項目を定義しなおすのがシンドイ場合は、 `extends` 属性を使う方法が用意されている。

**extends 属性で共通部分を継承する**

```xml:sample_mapper.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">

  <resultMap type="sample.mybatis.Base" id="baseResultMap">
    <id property="id" column="id" />
    <result property="commonValue" column="common_value" />
    
    <discriminator javaType="int" column="type">
      <case value="1" resultMap="hogeResultMap" />
      <case value="2" resultMap="fugaResultMap" />
    </discriminator>
  </resultMap>
                                                           <!-- ★extends で baseResultMap を継承 -->
  <resultMap type="sample.mybatis.Hoge" id="hogeResultMap" extends="baseResultMap">
    <result property="value" column="hoge_value" />
  </resultMap>
  
  <resultMap type="sample.mybatis.Fuga" id="fugaResultMap" extends="baseResultMap">
    <result property="value" column="fuga_value" />
  </resultMap>
  
  <select id="selectBase" resultMap="baseResultMap">
    select * from base_table
  </select>
</mapper>
```

```text:実行結果
Hoge [value=hoge1, id=1, commonValue=common1]
Fuga [value=fuga2, id=2, commonValue=common2]
Base [id=3, commonValue=common3]
```

## キャッシュ
```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session1 = factory.openSession();
                 SqlSession session2 = factory.openSession();) {
                
                TestTable testTable = selectAndPrintln("session1", session1);
                testTable.setValue("update");
                
                selectAndPrintln("session1", session1);
                
                selectAndPrintln("session2", session2);
            }
        }
    }
    
    private static TestTable selectAndPrintln(String tag, SqlSession session) {
        TestTable result = session.selectOne("sample.mybatis.selectTest");
        System.out.printf("<<%s>> %s%n", tag, result);
        return result;
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=1 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 1
<<session1>> TestTable [id=1, value=hoge]
<<session1>> TestTable [id=1, value=update]
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=1 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 1
<<session2>> TestTable [id=1, value=hoge]
```

- 検索結果はキャッシュされる。
- 同じ検索を再度実行すると、キャッシュされているオブジェクトが返される。
- キャッシュはセッションごとに保持されるため、他のセッションでの変更の影響は受けない。
- デフォルトは、以下のような動作をするらしい。
    - 1024 個のオブジェクトをキャッシュする。
    - 最も使われていないキャッシュから削除されていく。
    - 時間経過では削除されない。
    - `insert`, `update`, `delete` が実行されるとキャッシュはクリアされる。
- このデフォルトの動作は設定ファイルで上書きすることができる。
- 設定値の詳細は [こちら](http://mybatis.org/mybatis-3/ja/sqlmap-xml.html#cache) を参照。

# 更新系処理
## 登録
### 基本
```xml:sample_mapper.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">

  <insert id="insertTest">
    insert into test_table (
        value
    ) values (
        #{value}
    )
  </insert>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                session.insert("sample.mybatis.insertTest", "hoge");
                session.commit();
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table ( value ) values ( ? ) 
[DEBUG] s.m.insertTest  - ==> Parameters: hoge(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
```

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/b9cffbd0-1e8d-90a2-335e-9d02ab569085.jpeg)

- `<insert>` タグで `INSERT` 文を定義できる。
- だいたい `<select>` のときと同じノリで定義できる。

### オブジェクトをパラメータとして渡す
```java:TestTable.java
package sample.mybatis;

public class TestTable {
    private int id;
    private String value;
    
    public TestTable(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", value=" + value + "]";
    }
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTable table = new TestTable("fuga");
                
                session.insert("sample.mybatis.insertTest", table);
                session.commit();
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table ( value ) values ( ? ) 
[DEBUG] s.m.insertTest  - ==> Parameters: fuga(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
```

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/945899fb-3997-c03c-95ac-a2e77069984a.jpeg)

- こちらも `<select>` のときと同じ感じ。
- `parameterType` は省略可能。

### 自動生成されたキーを取得する
```xml:sample_mapper.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">
  
  <insert id="insertTest" useGeneratedKeys="true" keyProperty="id">
    insert into test_table (
        value
    ) values (
        #{value}
    )
  </insert>
  
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTable table = new TestTable("piyo");
                
                session.insert("sample.mybatis.insertTest", table);
                session.commit();
                
                System.out.println(table);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table ( value ) values ( ? ) 
[DEBUG] s.m.insertTest  - ==> Parameters: fuga(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
TestTable [id=7, value=piyo]
```

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/b1be224a-dc89-9248-047c-c9a6ef3e306d.jpeg)

- `<insert>` タグに `useGeneratedKeys` と `keyProperty` を指定する。
    - `useGeneratedKeys` は `true` を指定。
    - `keyProperty` にはキー値を設定する Java 側のフィールド（プロパティ）名を指定する。
- すると、 `INSERT` 時にデータベースによって生成された ID が自動で Java のインスタンスにセットされるようになる。

#### Oracle のシーケンスオブジェクトの場合
ID をシーケンスオブジェクトから取得する場合は、以下のようにする。

```xml:sample_mapper.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">
  
  <insert id="insertTest">
    <selectKey keyProperty="id" resultType="_int" order="BEFORE">
      select test_table_seq.nextval
        from dual
    </selectKey>
    insert into test_table (
        id
        ,value
    ) values (
        #{id}
        ,#{value}
    )
  </insert>
</mapper>
```

```text:実行結果
[DEBUG] s.m.insertTest!selectKey - ==>  Preparing: select test_table_seq.nextval from dual 
[DEBUG] s.m.insertTest!selectKey - ==> Parameters: 
[DEBUG] s.m.insertTest!selectKey - <==      Total: 1
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table ( id ,value ) values ( ? ,? ) 
[DEBUG] s.m.insertTest  - ==> Parameters: 11(Integer), aaa(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
TestTable [id=11, value=aaa]
```

```text:確認
SQL> select * from test_table;

        ID VALUE
---------- -----
        11 aaa
```

- `<insert>` タグの中に `<selectKey>` タグを宣言する。
- `keyProperty` 属性で、生成された ID をセットする Java フィールド（プロパティ）名を指定する。
- `order` 属性に `BEFORE` をセットすることで、まずシーケンスから値を取得してから `INSERT` が実行されるようになる。

## 更新
**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/18d54c18-639a-7c84-f0b2-5eb22118d9ad.jpeg)

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    private int id;
    private String value;
    
    public TestTable(int id, String value) {
        this.id = id;
        this.value = value;
    }
}
```

```xml:sample_mapper.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">
  
  <update id="updateTest">
    update test_table
       set value = #{value}
     where id = #{id}
  </update>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTable table = new TestTable(1, "update");
                
                session.insert("sample.mybatis.updateTest", table);
                session.commit();
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.updateTest  - ==>  Preparing: update test_table set value = ? where id = ? 
[DEBUG] s.m.updateTest  - ==> Parameters: update(String), 1(Integer)
[DEBUG] s.m.updateTest  - <==    Updates: 1
```

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/23ac8760-156a-1919-69e2-e58fcd83e1eb.jpeg)

- `<update>` タグで `UPDATE` を定義できる。

## 削除

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c00fcd11-5fb1-ed5d-1e34-a56c0c6faaa2.jpeg)

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    private int id;
    private String value;
    
    public TestTable(int id) {
        this.id = id;
    }
}
```

```xml:sample_mapper.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">
  
  <delete id="deleteTest">
    delete from test_table where id = #{id}
  </delete>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTable table = new TestTable(2);
                
                session.insert("sample.mybatis.deleteTest", table);
                session.commit();
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.deleteTest  - ==>  Preparing: delete from test_table where id = ? 
[DEBUG] s.m.deleteTest  - ==> Parameters: 2(Integer)
[DEBUG] s.m.deleteTest  - <==    Updates: 1
```

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/f7c8ce53-9588-d5b7-fa7f-888449785f33.jpeg)

- `<delete>` タグで `DELETE` 文を定義できる。

## バッチ更新
デフォルトでは、 SQL を実行するたびに `PreparedStatement` を生成して SQL をデータベースに対して発行している。

更新件数が多くなる場合、これは非常に効率が悪い。

MyBatis では、設定により `PreparedStatement` の使い回しや、バッチ更新ができるようになっている。

```xml:sample_mapper.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">

  <insert id="insertTest">
    insert into test_table (value) values (#{value})
  </insert>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                for (int i=0; i<3; i++) {
                    TestTable t = new TestTable("value-" + i);
                    int updateCount = session.insert("sample.mybatis.insertTest", t);
                    System.out.println("updateCount = " + updateCount);
                }
                
                session.commit();
            }
        }
    }
}
```

- 3回ループして、 `INSERT` を実行する実装。

### デフォルトの動作
```text:実行結果
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table (value) values (?) 
[DEBUG] s.m.insertTest  - ==> Parameters: value-0(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
updateCount = 1
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table (value) values (?) 
[DEBUG] s.m.insertTest  - ==> Parameters: value-1(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
updateCount = 1
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table (value) values (?) 
[DEBUG] s.m.insertTest  - ==> Parameters: value-2(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
updateCount = 1
```

- 毎回 `PreparedStatement` が生成されて、 SQL が発行されている。

### PreparedStatement を再利用する
```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="defaultExecutorType" value="REUSE"/>
  </settings>
  
  ...
</configuration>
```

```text:実行結果
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table (value) values (?) 
[DEBUG] s.m.insertTest  - ==> Parameters: value-0(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
updateCount = 1
[DEBUG] s.m.insertTest  - ==> Parameters: value-1(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
updateCount = 1
[DEBUG] s.m.insertTest  - ==> Parameters: value-2(String)
[DEBUG] s.m.insertTest  - <==    Updates: 1
updateCount = 1
```

- 設定ファイルで `defaultExecutorType` に `REUSE` を設定する。
- `PreparedStatement` の生成は１回だけになる。
- SQL の発行は毎回行われている。

### バッチ更新にする
```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="defaultExecutorType" value="BATCH"/>
  </settings>
  
  ...
</configuration>
```

```text:実行結果
[DEBUG] s.m.insertTest  - ==>  Preparing: insert into test_table (value) values (?) 
[DEBUG] s.m.insertTest  - ==> Parameters: value-0(String)
updateCount = -2147482646
[DEBUG] s.m.insertTest  - ==> Parameters: value-1(String)
updateCount = -2147482646
[DEBUG] s.m.insertTest  - ==> Parameters: value-2(String)
updateCount = -2147482646
```

- `defaultExecutorType` を `BATCH` にするとバッチ更新になる。
- SQL の発行は最後にまとめて行われる。
- なので、更新件数は取得できなくなっている。


### SqlSession を取得するときに指定する
バッチ更新にするかどうかの設定は、 `SqlSession` を取得するときの引数でも指定できる。

```java
import org.apache.ibatis.session.ExecutorType;

...

SqlSession session = factory.openSession(ExecutorType.BATCH);
```

- `openSession()` の引数に `ExecutorType` を渡すことで指定できる。
- ここで指定した条件は、設定ファイルよりも優先される。

# Mapper
ここまでは、 `SqlSession` に直接ステートメント ID とパラメータを渡す方法で SQL を実行していた。

MyBatis には、これとは別に Mapper という仕組みも用意されている。
Mapper を使うと、 `SqlSession` を直接使うより型安全になる。

基本的にこの Mapper を使用した方法の方が推奨っぽい。
（マッピングのファイルが自然と Mapper 単位になるので、管理もしやすくなるっぽい）

## 基本

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/305fd414-5fd7-e525-017a-000b9180b520.jpeg)

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    private int id;
    private String string;
    private int number;

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", string=" + string + ", number=" + number + "]";
    }
}
```

```xml:sample_mapper.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.TestTableMapper"> <!-- ★Mapper の FQCN を namespace にする -->
  
  <select id="selectAll" resultType="sample.mybatis.TestTable">
    select *
      from test_table
  </select>
</mapper>
```

```java:TestTableMapper.java
package sample.mybatis;

import java.util.List;

// ★ Mapper インターフェース
public interface TestTableMapper {
    
    // ★ステートメントID と同じ名前のメソッドを定義する
    List<TestTable> selectAll();
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                // ★Mapper のインスタンスを取得する
                TestTableMapper mapper = session.getMapper(TestTableMapper.class);
                
                mapper.selectAll().forEach(System.out::println);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.T.selectAll - ==>  Preparing: select * from test_table 
[DEBUG] s.m.T.selectAll - ==> Parameters: 
[DEBUG] s.m.T.selectAll - <==      Total: 3
TestTable [id=1, string=hoge, number=100]
TestTable [id=2, string=fuga, number=200]
TestTable [id=3, string=piyo, number=300]
```

- Mapper を利用する場合、専用のインターフェースを定義する（`TestTableMapper`）。
    - このインターフェースのメソッドと、設定ファイルの各ステートメントが対応するようにそれぞれを定義する。
    - ステートメントの方は、 `namespace` をインターフェースの FQCN にする（`sample.mybatis.TestTableMapper`）。
    - ステートメントID とインターフェースのメソッド名を一致させる（`selectAll`）。
    - パラメータが存在する場合は、メソッドに引数を渡すように定義する。
- Mapper のインスタンスは、 `SqlSession` の `getMapper()` メソッドで取得する。

## パラメータを複数の引数に分けて渡す
```xml:sample_mapper.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.TestTableMapper">
  
  <select id="selectByStringOrNumber" resultType="sample.mybatis.TestTable">
    select *
      from test_table
     where string = #{string}
        or number = #{number}
  </select>
</mapper>
```

```java:TestTableMapper.java
package sample.mybatis;

import java.util.List;

import org.apache.ibatis.annotations.Param;

public interface TestTableMapper {
    
    List<TestTable> selectByStringOrNumber(
        @Param("string") String string,
        @Param("number") int number
    );
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTableMapper mapper = session.getMapper(TestTableMapper.class);
                
                mapper.selectByStringOrNumber("hoge", 300).forEach(System.out::println);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.T.selectByStringOrNumber - ==>  Preparing: select * from test_table where string = ? or number = ? 
[DEBUG] s.m.T.selectByStringOrNumber - ==> Parameters: hoge(String), 300(Integer)
[DEBUG] s.m.T.selectByStringOrNumber - <==      Total: 2
TestTable [id=1, string=hoge, number=100]
TestTable [id=3, string=piyo, number=300]
```

- パラメータが複数存在するケースで、メソッドの引数をそれぞれ分けて定義したい場合は `@Param` アノテーションで引数をアノテートする。
- `@Param` の `value` には、パラメータの名前を指定する。


# 動的 SQL
条件に合わせて SQL を動的に組み立てることができる。

## if
**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ec329f27-aa9f-4ec7-4334-1419fb41e245.jpeg)

```xml:sample_mapper.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">
  
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select *
      from test_table
     where string_value = 'hoge'
     <if test="numberValue != null"> <!-- ★if タグで条件分岐 -->
       and number_value = #{numberValue}
     </if>
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                session
                    .selectList("sample.mybatis.selectTest") // ★パラメータ未設定
                    .forEach(System.out::println);
                
                Map<String, Object> param = new HashMap<>();
                param.put("numberValue", 100);
                
                session
                    .selectList("sample.mybatis.selectTest", param) // ★パラメータを設定
                    .forEach(System.out::println);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where string_value = 'hoge' 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 2
TestTable [id=1, stringValue=hoge, numberValue=100]
TestTable [id=2, stringValue=hoge, numberValue=200]

[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where string_value = 'hoge' and number_value = ? 
[DEBUG] s.m.selectTest  - ==> Parameters: 100(Integer)
[DEBUG] s.m.selectTest  - <==      Total: 1
TestTable [id=1, stringValue=hoge, numberValue=100]
```

- `<if>` タグを使用することで、条件が満たされたときだけ SQL を追加させたりできるようになる。
- `test` 属性で条件式を記述する。
    - この中では、検索条件で渡すパラメータの値を参照できる。
    - AND や OR 条件を記述するときは、 `and`, `or` を使用する（`&&`, `||` ではない！）。

## choose
**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/505c3500-ddcf-4bd4-86a3-43272dd2d537.jpeg)


```xml:sample_mapper.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">
  
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select *
      from test_table
    <choose>
      <when test="value == null">
        where value is null
      </when>
      <otherwise>
        where value = #{value}
      </otherwise>
    </choose>
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTable testTable = session.selectOne("sample.mybatis.selectTest");
                System.out.println(testTable);
                
                Map<String, Object> param = new HashMap<>();
                param.put("value", "hoge");

                testTable = session.selectOne("sample.mybatis.selectTest", param);
                System.out.println(testTable);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where value is null 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 1
TestTable [id=2, value=null]

[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where value = ? 
[DEBUG] s.m.selectTest  - ==> Parameters: hoge(String)
[DEBUG] s.m.selectTest  - <==      Total: 1
TestTable [id=1, value=hoge]
```

- `<choose>` タグを使うことで、複数の選択肢のうち１つだけを適用するという条件を定義できる。
- `<when>` タグで、指定した条件が満たされた場合の SQL を記述する。
- `<otherwise>` タグで、それ以外の条件が全て満たされなかった場合の SQL を記述する。

## where

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/71aa6f21-47f9-49b6-f4ab-21d6ddd70ce4.jpeg)

```xml:sample_mapper.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">
  
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select *
      from test_table
    <where>
      <if test="string != null">
        and string = #{string}
      </if>
      <if test="number != null">
        and number = #{number}
      </if>
    </where>
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                // ★パラメータを空で実行
                Map<String, Object> param = new HashMap<>();
                selectAndPrint(session, param);
                
                // ★パラメータに string だけ指定して実行
                param = new HashMap<>();
                param.put("string", "hoge");
                selectAndPrint(session, param);
                
                // ★パラメータに string と number を指定して実行
                param = new HashMap<>();
                param.put("string", "hoge");
                param.put("number", 200);
                selectAndPrint(session, param);
            }
        }
    }
    
    private static void selectAndPrint(SqlSession session, Map<String, Object> param) {
        session
            .selectList("sample.mybatis.selectTest", param)
            .forEach(System.out::println);
        System.out.println();
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 4
TestTable [id=1, string=hoge, number=100]
TestTable [id=2, string=hoge, number=200]
TestTable [id=3, string=fuga, number=200]
TestTable [id=4, string=piyo, number=300]

[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table WHERE string = ? 
[DEBUG] s.m.selectTest  - ==> Parameters: hoge(String)
[DEBUG] s.m.selectTest  - <==      Total: 2
TestTable [id=1, string=hoge, number=100]
TestTable [id=2, string=hoge, number=200]

[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table WHERE string = ? and number = ? 
[DEBUG] s.m.selectTest  - ==> Parameters: hoge(String), 200(Integer)
[DEBUG] s.m.selectTest  - <==      Total: 1
TestTable [id=2, string=hoge, number=200]
```

- `<where>` タグを使うと、子要素が何らかの文字列が存在した場合に限り `WHERE` 句を先頭に追加してくれるようになる。
- また、 `<where>` タグ内の文字列が `AND` か `OR` で始まる場合、自動でその記述が除去される。
- この動きを `<if>` などだけで実現使用とすると、結構複雑な記述になってしまう。

### trim タグで置き換える
上記の `<where>` を使った定義は、 `<trim>` タグを使って以下のように置き換えることができる。

```xml:sample_mapper.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">
  
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select *
      from test_table
    <trim prefix="where" prefixOverrides="AND|OR">
      <if test="string != null">
        and string = #{string}
      </if>
      <if test="number != null">
        and number = #{number}
      </if>
    </trim>
  </select>
</mapper>
```

- `prefix` 属性に、先頭に追加する文字列。
- `prefixOverrides` 属性に、先頭から除去する文字列をパイプ(`|`)区切りで指定する。

## set
**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/48202c45-9ce2-f983-f42f-53ce69125128.jpeg)

```java:TestTable.java
package sample.mybatis;

public class TestTable {
    private int id;
    private String string;
    private Integer number;
    
    public TestTable id(int id) {
        this.id = id;
        return this;
    }
    
    public TestTable string(String string) {
        this.string = string;
        return this;
    }
    
    public TestTable number(int number) {
        this.number = number;
        return this;
    }
}
```

```xml:sample_mapper.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">
  
  <update id="updateTest">
    update test_table
    <set>
      <if test="string != null">
        string = #{string},
      </if>
      <if test="number != null">
        number = #{number},
      </if>
    </set>
    where id = #{id}
  </update>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                session.update("sample.mybatis.updateTest",
                        new TestTable().id(1).number(555));
                
                session.update("sample.mybatis.updateTest",
                        new TestTable().id(3).string("update").number(999));
                
                session.commit();
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.updateTest  - ==>  Preparing: update test_table SET number = ? where id = ? 
[DEBUG] s.m.updateTest  - ==> Parameters: 555(Integer), 1(Integer)
[DEBUG] s.m.updateTest  - <==    Updates: 1
[DEBUG] s.m.updateTest  - ==>  Preparing: update test_table SET string = ?, number = ? where id = ? 
[DEBUG] s.m.updateTest  - ==> Parameters: update(String), 999(Integer), 3(Integer)
[DEBUG] s.m.updateTest  - <==    Updates: 1
```

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/077c9257-bbd6-7ff7-cacf-9c24ce571e8b.jpeg)

- `<set>` タグを使うと、内部の文字列が空でない場合に、先頭に `SET` 句が追加される。
- また、末尾のカンマ（`,`）が自動で除去される。

### trim タグで置き換える

`<where>` のときと同じように、 `<trim>` で置き換えることもできる。

```xml:sample_mapper.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">
  
  <update id="updateTest">
    update test_table
    <trim prefix="set" suffixOverrides=",">
      <if test="string != null">
        string = #{string},
      </if>
      <if test="number != null">
        number = #{number},
      </if>
    </trim>
    where id = #{id}
  </update>
</mapper>
```

- `suffixOverrides` 属性で、末尾の削除する文字を指定する。

## foreach

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/3179a412-c4b3-d992-78af-90ebab830699.jpeg)

```xml:sample_mapper.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">
  
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select *
      from test_table
     where id in
     <foreach collection="list" item="item"
              open="(" separator="," close=")">
       #{item}
     </foreach>
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;
import java.util.Arrays;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                session
                    .selectList("sample.mybatis.selectTest", Arrays.asList(1, 3, 5))
                    .forEach(System.out::println);
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id in ( ? , ? , ? ) 
[DEBUG] s.m.selectTest  - ==> Parameters: 1(Integer), 3(Integer), 5(Integer)
[DEBUG] s.m.selectTest  - <==      Total: 3
TestTable [id=1]
TestTable [id=3]
TestTable [id=5]
```

- `<foreach>` タグを使用すると、コレクションを反復処理しながら SQL を構築できる。
- `collection` 属性に、反復処理するコレクションを指定する。
    - `list` は、パラメータに反復処理可能なオブジェクトを渡したときにデフォルトで利用できる参照名（[特殊なパラメータ名](#特殊なパラメータ名) を参照）。
- `item` 属性で、反復中の各要素を参照するための一時変数名を宣言する。
- `open` 属性は、反復の先頭に追加する文字列を指定する。
- `separator` 属性は、各反復結果の間に挿入する文字列を指定する。
- `close` 属性は、反復の最後に追加する文字列を指定する。
- `index` 属性というのもあり、反復中のインデックス値を参照するための一時変数名を宣言できる。
- おもに `IN` 句の動的生成に利用する。

## bind

```xml:sample_mapper.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">
  
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    <bind name="parameter" value="'@@@' + _parameter + '@@@'"/>
    select *
      from test_table
     where value = #{parameter}
  </select>
</mapper>
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                session.selectList("sample.mybatis.selectTest", "abc");
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where value = ? 
[DEBUG] s.m.selectTest  - ==> Parameters: @@@abc@@@(String)
```

- `<bind>` タグで、一時変数を定義できる。
- `name` 属性で、一時変数の名前を定義する。
- `value` 属性で、 OGNL 式を使った値の定義ができる。
- `_parameter` は、パラメータとして渡した値を参照できる暗黙変数（[特殊なパラメータ名](#特殊なパラメータ名) を参照）。

# プログラムで SQL を動的生成する
MyBatis にはアノテーションを使って SQL を定義する方法が用意されている。

しかし、文字列連結で SQL を組み立てていると、カンマ区切りとか `AND`, `OR` 区切りなど、注意しないといけない細かい問題がいろいろある。
そのへんの面倒な部分を隠蔽した API が用意されている。

```xml:sample_mapper.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.TestTableMapper"> <!-- ★Mapper を namespace に指定 -->
</mapper>
```

```java:TestTableMapper.java
package sample.mybatis;

import java.util.List;

import org.apache.ibatis.annotations.SelectProvider;

// ★Mapper インターフェースをつくる
public interface TestTableMapper {
    // ★@SelectProvider で SQL を生成するクラスとメソッドを設定する
    @SelectProvider(type=TestTableSqlProvider.class, method="getSelectTestSql")
    List<TestTable> selectTest();
}
```

```java:TestTableSqlProvider.java
package sample.mybatis;

import org.apache.ibatis.jdbc.SQL;

// ★SQL を生成するクラス。
public class TestTableSqlProvider {
    
    // ★SQL を生成するメソッド
    public String getSelectTestSql() {
        // ★SQL クラスを使って、 SQL を生成する
        SQL sql = new SQL() {{ // ★匿名クラスで生成
            SELECT("id, hoge_value");
            SELECT("fuga_value");
            SELECT("piyo_value");
            FROM("test_table");
        }};
        
        return sql.toString();
    }
}
```

```java:Main.java
package sample.mybatis;

import java.io.InputStream;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {
    
    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            
            try (SqlSession session = factory.openSession()) {
                TestTableMapper mapper = session.getMapper(TestTableMapper.class);
                mapper.selectTest();
            }
        }
    }
}
```

```text:実行結果
[DEBUG] s.m.T.selectTest - ==>  Preparing: SELECT id, hoge_value, fuga_value, piyo_value FROM test_table WHERE (hoge_value is not null) 
[DEBUG] s.m.T.selectTest - ==> Parameters: 
```

- Mapper のメソッドを `@SelectProvider` でアノテートする。
    - `type` に SQL を生成するクラスを、 `method` に SQL を生成するメソッドの名前を指定する。
    - `INSERT` や `UPDATE` には `@InsertProvider` や `@UpdateProvider` がある。
- SQL の生成には、 `org.apache.ibatis.jdbc.SQL` クラスを使用する。
- `SQL` クラスを匿名クラスとして new して、インスタンス初期化ブロック内で `SELECT()` や `FROM()` メソッドを使って SQL を構築する。
    - 各引数には文字列で SQL の部品を渡す。
    - この際、カンマ区切りなどは必要に応じて追加されたりする。
- 生成した `SQL` インスタンスの `toString()` メソッドで構築した SQL を文字列として取得できる。

## 各メソッドの使い方について
### SELECT
```java:
new SQL() {{
    SELECT("foo");
    SELECT("bar");
    SELECT("fizz, buzz");
    FROM("foo_table");
}};
```

```sql:
SELECT foo, bar, fizz, buzz FROM foo_table 
```

- 連続して記述することで、各項目がカンマで勝手に区切られる。
- 明示的にカンマ区切りの文字列を渡してもOK。
- 末尾に不要なカンマがある場合は、除去**してくれない**。

### FROM
```java:
new SQL() {{
    SELECT("*");
    FROM("foo_table");
    FROM("bar_table");
}};
```

```sql:
SELECT * FROM foo_table, bar_table 
```

- `FROM` を連続させることで、カンマ区切りで FROM 句が列挙される。

### WHERE
```java:
new SQL() {{
    SELECT("*");
    FROM("foo_table");
    WHERE("foo = ?");
    WHERE("bar = ?");
    AND();
    WHERE("fizz = ?");
    WHERE("buzz = ?");
    OR();
    WHERE("hoge = ?");
    WHERE("fuga = ?");
}};
```

```sql:
SELECT *
  FROM foo_table
 WHERE (
       foo = ?
       AND bar = ?
 ) AND (
       fizz = ?
       AND buzz = ?
 )  OR (
       hoge = ?
       AND fuga = ?
 ) 
```

- `WHERE()` メソッドで `WHERE` 句を生成できる。
- 連続させると `AND` 条件で連結される。
- `AND()` および `OR()` メソッドで `AND`, `OR` で連結できる。

### INNER_JOIN
```java:
new SQL() {{
    SELECT("*");
    FROM("foo_table");
    INNER_JOIN("bar_table on bar_table.id = foo_table.bar_id");
    INNER_JOIN("fizz_table on fizz_table.id = bar_table.fizz_id");
}};
```

```sql:
    SELECT *
      FROM foo_table
INNER JOIN bar_table
        on bar_table.id = foo_table.bar_id
INNER JOIN fizz_table
        on fizz_table.id = bar_table.fizz_id 
```

- `INNER_JOIN()` メソッドで、 `INNER JOIN` 句を追加できる。
- 結合条件は頑張って文字列で組み立てないといけないっぽい。

### ORDER_BY
```java
new SQL() {{
    SELECT("*");
    FROM("foo_table");
    ORDER_BY("foo");
    ORDER_BY("bar desc");
}};
```

```sql
SELECT * FROM foo_table ORDER BY foo, bar desc 
```

- `ORDER_BY()` メソッドで `ORDER BY` 句を生成できる。
- 連続させることで、自動でカンマ区切りになる。

### GROUP_BY
```java
new SQL() {{
    SELECT("foo, bar");
    FROM("foo_table");
    GROUP_BY("foo");
    GROUP_BY("bar");
}};
```

```sql
SELECT foo, bar FROM foo_table GROUP BY foo, bar 
```

- `GROUP_BY()` メソッドで、 `GROUP BY` 句を生成できる。
- 連続させることで、自動でカンマ区切りになる。

### HAVING
```java
new SQL() {{
    SELECT("foo, count(*)");
    FROM("foo_table");
    GROUP_BY("foo");
    HAVING("0 < count(*)");
    HAVING("count(*) < 100");
}};
```

```sql
  SELECT foo
        ,count(*)
    FROM foo_table
GROUP BY foo
  HAVING (
         0 < count(foo)
         AND count(foo) < 100
  )
```

- `HAVING()` メソッドで `HAVING` 句を生成できる。
- `WHERE()` と同じような動作をする（連続させると `AND` で連結される）。

### INSERT_INTO, VALUES
```java
new SQL() {{
    INSERT_INTO("foo_table");
    VALUES("foo_value", "#{foo_value}");
    VALUES("fizz_value, buzz_value", "#{fizz_value}, #{buzz_value}");
}};
```

```sql
INSERT INTO foo_table (foo_value, fizz_value, buzz_value) VALUES (?, ?, ?) 
```

- `INSERT_INTO()` メソッドで `INSERT` 文を生成できる。
- `VALUES()` メソッドで、登録する項目を定義できる。
    - 第一引数に値を設定する項目の名前を、
    - 第二引数に `VALUES` 句で指定する値を渡す。
    - 複数記述すると、自動でカンマ区切りになる。

### UPDATE, SET
```java
new SQL() {{
    UPDATE("foo_table");
    SET("foo_value = #{foo_value}");
    SET("bar_value = #{bar_value}");
    WHERE("id = #{id}");
}};
```

```sql
UPDATE foo_table SET foo_value = ?, bar_value = ? WHERE (id = ?)
```

- `UPDATE()` メソッドで、 `UPDATE` 文を生成できる。
- `SET()` メソッドで、 `SET` 句を生成できる。
    - 連続させることで、自動でカンマ区切りになる。

### DELETE_FROM
```java
new SQL() {{
    DELETE_FROM("foo_table");
    WHERE("id = #{id}");
}};
```

```sql
DELETE FROM foo_table WHERE (id = ?) 
```

- `DELETE_FROM()` メソッドで `DELETE` 文を生成できる。

# Spring Boot と連携する
Spring Boot 用のプラグインが用意されているので、それを使えば簡単に連携できる。

**test_table**

![mybatis.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/800cf20e-a8bf-3e57-20c8-8551801556a9.jpeg)

```text:フォルダ構成
|-build.gradle
`-src/main/
  |-java/
  |  `-sample/mybatis/springboot/
  |    |-Main.java
  |    `-TestTableMapper.java
  `-resources/
     |-application.properties
     |-mybatis-config.xml
     `-TestTableMapper.xml
```

```groovy:build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.1.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
    // ★MyBatis の SpringBoot 用プラグインを読み込む
    compile 'org.mybatis.spring.boot:mybatis-spring-boot-starter:1.0.0'
    compile 'mysql:mysql-connector-java:5.1.38'
}
```

```properties:application.properties
# ★MyBatis の設定ファイルを読み込む
mybatis.config=mybatis-config.xml

# ★データソースの設定
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- ★データソースの管理は Spring Boot に任せるので、こちらには記述しない -->
  <mappers>
    <mapper resource="TestTableMapper.xml"/>
  </mappers>
</configuration>
```

```xml:TestTableMapper.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.springboot.TestTableMapper">

  <select id="selectTest" resultType="map">
    select * from test_table
  </select>
</mapper>
```

```java:TestTableMapper.java
package sample.mybatis.springboot;

import java.util.List;
import java.util.Map;

public interface TestTableMapper {
    
    List<Map<String, Object>> selectTest();
}
```

```java:Main.java
package sample.mybatis.springboot;

import java.io.IOException;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) throws IOException {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private SqlSessionTemplate sqlSession;
    @Autowired
    private TestTableMapper mapper;
    
    public void method() throws IOException {
        System.out.println("[SqlSessionTemplate]");
        this.sqlSession
            .selectList("sample.mybatis.springboot.TestTableMapper.selectTest")
            .forEach(System.out::println);
        
        System.out.println("[TestTableMapper]");
        this.mapper
            .selectTest()
            .forEach(System.out::println);
    }
}
```

```text:実行結果
[SqlSessionTemplate]
{id=1, value=hoge}
{id=2, value=fuga}
{id=3, value=piyo}

[TestTableMapper]
{id=1, value=hoge}
{id=2, value=fuga}
{id=3, value=piyo}
```

- Spring Boot で MyBatis を使う場合は、 `mybatis-spring-boot-starter` を依存関係に追加する。
- 次に Spring Boot の設定ファイル（`application.properties`）にて、 `mybatis.config` という名前で MyBatis の設定ファイルを指定する。
- データソースは Spring Boot に管理してもらうので、 Spring Boot の方に設定を記述して、 MyBatis の方には記述しない。
- `SqlSession` にアクセスするには、 `SqlSessionTemplate` を `@Autowired` でインジェクションすればいい。
    - `SqlSessionTemplate` の API は `SqlSession` と同じ。
    - `SqlSession` と違いスレッドセーフで、異なるスレッドや DAO から使っても問題ない。
- Mapper インターフェースを定義しておけば、 `@Autowired` でインジェクションできる。
    - Spring にスキャンされる場所にインターフェースを入れておく必要がある。
    - `Main` クラスを `@MapperScan` でアノテートすれば、任意のパッケージの Mapper インターフェースをスキャンすることもできる（例：`@MapperScan("foo.bar")`）。
    - こちらもスレッドセーフなインスタンスがインジェクションされるため、異なるスレッドや DAO から利用しても問題ない。






# 参考
- [MyBatis – MyBatis 3 | イントロダクション](https://mybatis.github.io/mybatis-3/ja/index.html)
- [mybatis-spring – MyBatis-Spring | イントロダクション](http://mybatis.org/spring/ja/index.html)
- [MyBatisでダイナミックSQL（動的SQL）をアノテーションでやる｜Pimp my Code. @wataru420](http://ameblo.jp/wataru420/entry-11135592975.html)
- [iBATIS - Wikipedia](https://ja.wikipedia.org/wiki/IBATIS)
- [MyBatis - Wikipedia](https://ja.wikipedia.org/wiki/MyBatis)