mybatis-spring-boot-starter 1.3の変更点

2017年4月10日のリリースされた1.3の変更点をまとめました。

なお、

* mybatis-spring-boot-starterの使い方については、[こちら](http://qiita.com/kazuki43zoo/items/ea79e206d7c2e990e478)（バージョン1.3対応済）
* mybatis-spring-boot-starter 1.1から1.2の変更点については、[こちら](http://qiita.com/kazuki43zoo/items/84b4a89846a7d0d65922)
* mybatis-spring-boot-starter 1.0から1.1の変更点については、[こちら](http://qiita.com/kazuki43zoo/items/5c46ac2f539d365aa53b)

をご覧下さい。

> **Note:**
> **2017/8/21: 追記**
> バージョン1.3.1の変更点を追加しました。
> **2019/5/7: 追記**
> バージョン1.3.2, 1.3.3, 1.3.4の変更点を反映しました。


# 依存ライブラリの必須バージョン

バージョン1.3を使う場合は、以下のバージョンが必須になります。

* MyBatis 3.4+
* Mybatis-Spring 1.3+
* Spring Boot 1.5+

# Spring Boot 1.5+が必須

バージョン1.3より、MyBatisの機能をテストする時に必要となるBean定義をサポートする`@MybatisTest`アノテーションが追加され、そのアノテーションがSpring Boot 1.5でサポートされたAPIに依存しているため、Spring Boot 1.5+が必須になります。なお、Spring Boot 1.4系でmybatis-spring-boot-starterを使う場合は、バージョン1.2系を使用してください。


# 依存ライブラリのバージョン更新

バージョン1.3では、以下のライブラリのバージョンが更新されています。

| ライブラリ名 | 1.2.0のバージョン | 1.3.0のバージョン | 1.3.1のバージョン | 1.3.2のバージョン | 1.3.3のバージョン | 1.3.4のバージョン |
| --------- | --------------- | -------------- | -------------- | -------------- | -------------- | -------------- |
| MyBatis | 3.4.2 | **3.4.4** | **3.4.5** | **3.4.6** | 3.4.6 | 3.4.6 |
| MyBatis Spring | 1.3.1 | 1.3.1 | 1.3.1 | **1.3.2** | 1.3.2 | 1.3.2 |
| Spring Boot | 1.4.3.RELEASE | **1.5.2.RELEASE** | **1.5.6.RELEASE** | **1.5.10.RELEASE** | **1.5.19.RELEASE** | **1.5.20.RELEASE** |

> **Note:**
> バージョン1.3.0と同時にリリースされたバージョン1.2.1と比較すると、Spring Bootのバージョンだけ更新(1.4.5.RELEASE -> 1.5.2.RELEASE)されています。

# `ConfigurationCustomizer`インタフェースの追加

MyBatisのコンフィギュレーション情報を保持するBean(`org.apache.ibatis.session.Configuration`のBean)を、Javaコードでカスタマイズするためのコールバックインタフェース(`org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer`)が追加されました。以下のように、このインタフェースを実装したクラスをBean定義しておくと、`ConfigurationCustomizer`の`configure`メソッドがコールバックされる仕組みになっており、Javaコードを使用してMyBayisのコンフィギュレーションを完全にカスタマイズすることができます。

```java:ラムダ式を使用したConfigurationCustomizerのBean定義例
@Configuration
public class MyBatisConfiguration {
	@Bean
	ConfigurationCustomizer mybatisConfigurationCustomizer() {
		return (configuration) -> {
			// aplication.propertiesで指定・表現できないカスタマイズコードを実装
			configuration.getTypeHandlerRegistry().register(RoundingMode.class, EnumOrdinalTypeHandler.class);
		};
	}
}
```

> **Note:**
>
> `ConfigurationCustomizer`インタフェースは、バージョン1.3.0と同時にリリースされたバージョン1.2.1にもバックポートされています。


# `@MybatisTest`(mybatis-spring-boot-starter-test)の追加

バージョン1.3より、MyBatisの機能をテストする時に必要となるBean定義をサポートする`@MybatisTest`アノテーションが追加されました。これは、Spring Bootが提供する[`@DataJpaTest`](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test)や[`@JdbcTest`](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jdbc-test)のMyBatis版になります（当然ながら・・使い方もSpring Boot提供のアノテーションと同じです！！）。
この対応に伴い、MyBatisのテストをする際に必要となるライブラリ群を解決するための`mybatis-spring-boot-starter-test`が新設されているので、MyBatis提供の機能に対するテストを行う場合は、`pom.xml`に以下の定義を追加しましょう！！

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

たとえば、以下のようなMapperインタフェースに対するテストを行う場合は、

```java:src/main/java/com/example/mapper/TodoMapper.java
package com.example.mapper;

import com.example.domain.Todo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface TodoMapper {

    @Insert("INSERT INTO todo (title, details, finished) VALUES (#{title}, #{details}, #{finished})")
    @Options(useGeneratedKeys = true)
    void insert(Todo todo);

    @Select("SELECT id, title, details, finished FROM todo WHERE id = #{id}")
    Todo select(int id);

}
```

`@MybatisTest`を使用して、以下のようなテストケースクラスを作ればOKです。

```java:src/main/java/com/example/mapper/TodoMapperTests.java
package com.example.mapper;

import com.example.domain.Todo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.boot.test.autoconfigure.MybatisTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@MybatisTest // (1)
public class TodoMapperTests {

    @Autowired
    private TodoMapper todoMapper; // (2)

    @Autowired
    private NamedParameterJdbcOperations jdbcOperations; // (3)

    @Test
    public void insert() {

        // setup
        // none

        // perform test and assertions
        {
            // (4)
            Todo newTodo = new Todo();
            newTodo.setTitle("飲み会");
            newTodo.setDetails("銀座 19:00");
            todoMapper.insert(newTodo);

            // (5)
            Todo actualTodo =
                jdbcOperations.queryForObject("SELECT * FROM todo WHERE id = :id",
                    new MapSqlParameterSource("id", newTodo.getId()),
                    new BeanPropertyRowMapper<>(Todo.class));
            assertThat(actualTodo.getId()).isEqualTo(newTodo.getId());
            assertThat(actualTodo.getTitle()).isEqualTo("飲み会");
            assertThat(actualTodo.getDetails()).isEqualTo("銀座 19:00");
            assertThat(actualTodo.isFinished()).isEqualTo(false);
        }
    }


    @Test
    public void select() {

        // (6)
        // setup
        Todo newTodo = new Todo();
        newTodo.setTitle("飲み会");
        newTodo.setDetails("銀座 19:00");

        GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcOperations.update(
            "INSERT INTO todo (title, details, finished) VALUES(:title, :details, :finished)",
            new BeanPropertySqlParameterSource(newTodo), keyHolder);

        // perform test and assertions
        {
            // (7)
            Todo actualTodo = todoMapper.select(keyHolder.getKey().intValue());

            assertThat(actualTodo.getId()).isEqualTo(keyHolder.getKey().intValue());
            assertThat(actualTodo.getTitle()).isEqualTo("飲み会");
            assertThat(actualTodo.getDetails()).isEqualTo("銀座 19:00");
            assertThat(actualTodo.isFinished()).isEqualTo(false);
        }
    }

}
```

| 項番 | 説明 |
| --- | ---- |
| (1) | クラスに`@MybatisTest`を付与する。このアノテーションを付与することで、MyBatisを動かすために必要になるAutoConfigureクラスだけが有効になります。デフォルトの動作ではコンポーネントスキャンが無効化されているので、テスト時に必要ないBeanが無題にDIコンテナに登録されることを防ぐことができます。 | 
| (2) | テスト対象のMapperをインジェクションする。`@MybatisTest`を付与すると、MyBatis提供のAutoConfigureによってMapperのBeanが生成されます。 | 
| (3) | 登録データの検証およびテストデータを登録するために、`NamedParameterJdbcTemplate` のBeanをインジェクションする。`@MybatisTest`を付与すると、`JdbcTemplate`と`NamedParameterJdbcTemplate`のBeanがDIコンテナに登録されます。 | 
| (4) | テスト対象の`insert`メソッドを呼び出す。 | 
| (5) | `insert`メソッドの呼び出し結果を検証する。ここでは、`NamedParameterJdbcOperations`のメソッドを介して登録したデータを取得し、登録したデータの妥当性を検証しています。 | 
| (6) | `select`メソッドのテストを行うためにテストデータを登録する。ここでは、`NamedParameterJdbcOperations`のメソッドを介してテストデータを登録しています。なお、Spring Framework提供の`@Sql`アノテーションなどを使用してテストデータを登録する方法もあります。 | 
| (7) | テスト対象の`select`メソッドを呼び出す。 |
| (8) | `select`メソッドの呼び出し結果を検証する。 | 

この状態でテストを実行すると、上位パッケージに存在するSpring Bootアプリケーションクラス(`@SpringBootApplication`を付与したクラス)内で定義しているBean定義も読み込まれる仕組みになっているため、MyBatisのテストに必要ないBeanがDIコンテナに登録される可能性があります。
もし、MyBatisのテストに必要ないBeanをDIコンテナに登録したくない場合は、テストケースクラスと同じパッケージに以下のような空のSpring Bootアプリケーションクラスを作成してください。こうすることで、上位パッケージにあるSpring Bootアプリケーションクラスを無効化することができます。

```java:src/test/java/com/example/mapper/MapperTestApplication.java
package com.example.mapper;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MapperTestApplication {

}
```

さらに詳しい情報は、[公式リファレンス](http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/)およびSpring Bootが提供する[`@DataJpaTest`](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test)や[`@JdbcTest`](http://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-jdbc-test)のリファレンスをご覧ください。

> **Note:**
> `@MybatisTest`アノテーション（および`mybatis-spring-boot-starter-test`）は、バージョン1.3と同時にリリースされたバージョン1.2.1にもバックポートされています。

# IDE上でのプロパティ値の入力補完改善

1.3.0までは、一部のプロパティ(`mybatis.configuration.default-scripting-language`)に対する入力補完が効かない状態になっていましたが、Spring Bootが提供する「[Adding additional meta-data](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#configuration-metadata-additional-metadata)」の仕組みを利用することで、1.3.1より入力補完が効くように改善されています。

# コンフギュレーションプロパティで`typeAliasesSuperType`の指定をサポート

[バージョン2.0の変更点](https://qiita.com/kazuki43zoo/items/38a24e051359e39c012c#%E3%82%B3%E3%83%B3%E3%83%95%E3%82%AE%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%A7typealiasessupertype%E3%81%AE%E6%8C%87%E5%AE%9A%E3%82%92%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88)を参照してください。(バージョン1.3.3よりサポート)