Edited at

mybatis-spring-boot-starterの使い方

今回は2015年11月にバージョン1.0.0がリリースされ、2016年4月19日に1.1がリリースされたmybatis-spring-boot-starterの使い方を紹介します。

MyBatisをSpring Boot上で使う際は、mybatis-springから提供されているSqlSessionFactoryBeanSqlSessionTemplateのBean定義を開発者が行う必要がありましたが、mybatis-spring-boot-starterの登場によりこれらのBean定義は自動コンフィギュレーションによって解決されます



まずは、実際にmybatis-spring-boot-starterを利用して簡単なCLI(Command Line Interface)アプリケーションを作ってみます 。


Note:

2019/7/16: 追記

JDK 13で導入予定(現時点ではPreview版)のText Blocksを使ったデモアプリケーションへのリンクを追加しました。

2019/7/15: 追記

2019年7月15日にバージョン2.1.0がリリースされたので、内容を2.1ベースにしました。

2019/5/7: 追記

mybatis-spring-boot-starter 2.0の変更点へのリンクを追加しました。加えて、2.0.1上でmybatis.type-aliases-packageを利用する時に発生する可能性がある不具合に関する説明(説明へのリンク)を追加しました。

2019/5/3: 追記

2019年1月22日にバージョン2.0.0(2019年4月にバージョン2.0.1)がリリースされたので、内容を2.0ベースにしました。

2017/4/10: 追記

2017年4月10日にバージョン1.3.0がリリースされたので、内容を1.3ベースにしました。

2017/1/4: 追記

2017年1月2日にバージョン1.2.0がリリースされたので、内容を1.2ベースにしました。 なお、バージョン1.2を使う場合は、Spring Boot 1.4以降(正確にはSpring Framework 4.3以降)上で動かす必要があります。(2017/1/4)


なお、メジャー/マイナーバージョンアップ時の変更点については、以下の投稿にまとめてあります。


動作検証バージョン


  • MyBatis Spring Boot Starter 2.1.0

  • MyBatis 3.5.2

  • MyBatis Spring 2.0.2

  • Spring Boot 2.1.6.RELEASE

  • Spring Framework 5.1.8.RELEASE


検証コード


Java版


Text Blocks版(JDK 13)

JDK 13 Early-Access Buildsを使用して、MapperメソッドにText Blocksを使ったデモアプリケーションをGitHubに公開しました。


Groovy版

2016/7/13に、MapperインタフェースをGroovyで作る方法(「GroovyでMyBatisのMapperをつくる(とSQLの可読性がGood!!)」)を投稿しました。


Kotlin版

2016/8/3に、Kotlin上でMyBatis(mybatis-spring-boot-starter)を使う方法(「Kotlinでmybatis-spring-boot-starterを使う」)を投稿しました。


純正の簡易サンプル

MyBatis Spring Bootから簡易的なサンプルがいくつか提供されているので、そちらも合わせて参照すると良いかと思います。


開発プロジェクトの作成

Spring Bootは、Spring-Boot向けの開発プロジェクトを生成するためのWeb Service「SPRING INITIALIZR」を提供しています。今回は、このWeb Serviceを利用して開発プロジェクトを作成します。

SPRING INITIALIZRはMavenとGradleプロジェクトをサポートしていますが、今回の記事ではMavneプロジェクトを作成します。ウィザードに以下の値を入力して「Generate Project」ボタンを押下すると、Mavenプロジェクトがダウンロードされます。

入力項目

備考

Project
Maven Project
※デフォルト

Language
Java
※デフォルト

Spring Boot
2.1.6
※デフォルト

Group
com.example
※デフォルト

Artifact
mybatis-demo
※デフォルト(demo)から変更

Dependencies
MyBatis, H2

ダウンロードしたZipファイルを解凍すると、以下のようなディレクトリ構成のプロジェクトが生成できます。


ディレクトリ構成

.

├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── example
│   │   └── mybatisdemo
│   │   └── MybatisDemoApplication.java
│   └── resources
│   └── application.properties
└── test
└── java
└── com
└── example
└── mybatisdemo
└── MybatisDemoApplicationTests.java



Note:

curlとtarコマンドが使える環境であれば、以下のコマンドを実行するだけで開発プロジェクトを作ることもできます。

$ curl -s https://start.spring.io/starter.tgz\

-d name=mybatis-demo\
-d artifactId=mybatis-demo\
-d dependencies=mybatis,h2\
-d baseDir=mybatis-demo\
| tar -xzvf -

作成した開発プロジェクトをSpring Bootアプリケーションとして起動してみます。


コンソール

$ ./mvnw spring-boot:run

[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.example:mybatis-demo >----------------------
[INFO] Building mybatis-demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) > test-compile @ mybatis-demo >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ mybatis-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ mybatis-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ mybatis-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /private/tmp/mybatis-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ mybatis-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) < test-compile @ mybatis-demo <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) @ mybatis-demo ---

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)

2019-07-15 17:25:33.712 INFO 55692 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Starting MybatisDemoApplication on shimizukazutakanoMacBook-puro.local with PID 55692 (/private/tmp/mybatis-demo/target/classes started by shimizukazuki in /private/tmp/mybatis-demo)
2019-07-15 17:25:33.714 INFO 55692 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : No active profile set, falling back to default profiles: default
2019-07-15 17:25:34.049 WARN 55692 --- [ main] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in '[com.example.mybatisdemo]' package. Please check your configuration.
2019-07-15 17:25:34.579 INFO 55692 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Started MybatisDemoApplication in 1.121 seconds (JVM running for 4.19)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.064 s
[INFO] Finished at: 2019-07-15T17:25:34+09:00
[INFO] ------------------------------------------------------------------------


とりあえずエラーはでないことは確認できたので、先に進みます。


mybatis-spring-boot-starterの確認

ウィザードで「MyBatis」を選択すると、Spring Bootのバージョンに対応するmybatis-spring-boot-starterが依存アーティファクトに追加されます。今回はSpring Boot 2.1.6を選択しているため、mybatis-spring-boot-starter 2.1.0が追加されます。(ちなみに・・・Spring Boot 1.5系を選ぶとmybatis-spring-boot-starter 1.3.4が追加されます)


pom.xml

<dependency>

<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>

mybatis-spring-boot-starter 2.1.0は、推移的に以下のアーティファクトも追加してくれます


コンソール

$ ./mvnw dependency:tree

...
[INFO] +- org.mybatis.spring.boot:mybatis-spring-boot-starter:jar:2.1.0:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.1.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.1.6.RELEASE:compile
[INFO] | | | \- org.springframework:spring-context:jar:5.1.8.RELEASE:compile
[INFO] | | | +- org.springframework:spring-aop:jar:5.1.8.RELEASE:compile
[INFO] | | | \- org.springframework:spring-expression:jar:5.1.8.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.1.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.1.6.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.11.2:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.11.2:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.26:compile
[INFO] | | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.23:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:2.1.6.RELEASE:compile
[INFO] | | +- com.zaxxer:HikariCP:jar:3.2.0:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:5.1.8.RELEASE:compile
[INFO] | | +- org.springframework:spring-beans:jar:5.1.8.RELEASE:compile
[INFO] | | \- org.springframework:spring-tx:jar:5.1.8.RELEASE:compile
[INFO] | +- org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:jar:2.1.0:compile
[INFO] | +- org.mybatis:mybatis:jar:3.5.2:compile
[INFO] | \- org.mybatis:mybatis-spring:jar:2.0.2:compile
...


ドメインオブジェクトの作成

今回は、ドメインオブジェクトとしてTodoクラスを作ります。


src/main/java/com/example/mybatisdemo/domain/Todo.java

package com.example.mybatisdemo.domain;

public class Todo {
private int id;
private String title;
private String details;
private boolean finished;
// ... setter and getter
}



Note:

setterとgetterの生成はIDEの機能を使ってもよいですが、Lombokを使う方が開発効率があがります!!



Mapperインターフェースの作成

TodoへのCRUD操作を提供するMapperインターフェースを作ります。


src/main/java/com/example/mybatisdemo/mapper/TodoMapper.java

package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.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, keyProperty = "id")
void insert(Todo todo);

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

}



Note:

SQLをXMLファイルに書きたい場合は、以下のようなMapperインターフェースとXMLファイルを作成します。


src/main/java/com/example/mybatisdemo/mapper/TodoMapper.java

@Mapper

public interface TodoMapper {
void insert(Todo todo);
Todo select(int id);
}


src/main/resources/com/example/mybatisdemo/mapper/TodoMapper.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="com.example.mybatisdemo.mapper.TodoMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO todo (title, details, finished) VALUES (#{title}, #{details}, #{finished})
</insert>
<select id="select" resultType="com.example.mybatisdemo.domain.Todo">
SELECT id, title, details, finished FROM todo WHERE id = #{id}
</select>
</mapper>



todoテーブルの作成

H2の組み込みデータベースにtodoテーブルを作成します。

Spring Bootの自動コンフィギュレーションで作成されるDataSourceを使う場合は、クラスパス直下にschema.sqldata.sqlというファイルを配置しておくと、Spring Boot起動時にこれらのファイルを読み込んでSQLを実行してくれます。


src/main/resources/schema.sql

CREATE TABLE todo (

id IDENTITY
,title TEXT NOT NULL
,details TEXT
,finished BOOLEAN NOT NULL
);


MybatisDemoApplicationの修正とSpring Bootアプリケーションの起動

MybatisDemoApplicationを修正し、Mapperインタフェースを経由してデータベースにアクセスします。


  • 修正前


src/main/java/com/example/mybatisdemo/MybatisDemoApplication.java

package com.example.mybatisdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MybatisDemoApplication {

public static void main(String[] args) {
SpringApplication.run(MybatisDemoApplication.class, args);
}
}



  • 修正後


src/main/java/com/example/mybatisdemo/MybatisDemoApplication.java

package com.example.mybatisdemo;

import com.example.mybatisdemo.domain.Todo;
import com.example.mybatisdemo.mapper.TodoMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.Transactional;

@SpringBootApplication
public class MybatisDemoApplication implements CommandLineRunner { // CommandLineRunnerを実装する

public static void main(String[] args) {
SpringApplication.run(MybatisDemoApplication.class, args);
}

private final TodoMapper todoMapper;

public MybatisDemoApplication(TodoMapper todoMapper) {
this.todoMapper = todoMapper; // Mapperをインジェクションする
}

// Spring Boot起動時にCommandLineRunner#runメソッドが呼び出される
@Transactional
@Override
public void run(String... args) throws Exception {
Todo newTodo = new Todo();
newTodo.setTitle("飲み会");
newTodo.setDetails("銀座 19:00");

todoMapper.insert(newTodo); // 新しいTodoをインサートする

Todo loadedTodo = todoMapper.select(newTodo.getId()); // インサートしたTodoを取得して標準出力する
System.out.println("ID : " + loadedTodo.getId());
System.out.println("TITLE : " + loadedTodo.getTitle());
System.out.println("DETAILS : " + loadedTodo.getDetails());
System.out.println("FINISHED : " + loadedTodo.isFinished());
}

}


MybatisDemoApplicationを修正したら、Spring Bootアプリケーションとして起動します。


コンソール

$ ./mvnw spring-boot:run

...
[INFO] --- spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) @ mybatis-demo ---

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.6.RELEASE)

2019-07-15 17:35:04.734 INFO 55816 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Starting MybatisDemoApplication on shimizukazutakanoMacBook-puro.local with PID 55816 (/private/tmp/mybatis-demo/target/classes started by shimizukazuki in /private/tmp/mybatis-demo)
2019-07-15 17:35:04.736 INFO 55816 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : No active profile set, falling back to default profiles: default
2019-07-15 17:35:05.561 INFO 55816 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-15 17:35:05.820 INFO 55816 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-15 17:35:06.176 INFO 55816 --- [ main] c.e.mybatisdemo.MybatisDemoApplication : Started MybatisDemoApplication in 1.746 seconds (JVM running for 5.696)
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.341 s
[INFO] Finished at: 2019-07-15T17:35:06+09:00
[INFO] ------------------------------------------------------------------------
2019-07-15 17:35:06.266 INFO 55816 --- [ Thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-07-15 17:35:06.270 INFO 55816 --- [ Thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
...


標準出力にインサートしたTodoの状態が出力されました

ちなみに、開発プロジェクトは以下のような状態になります。


ディレクトリ構成


.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── example
│   │   └── mybatisdemo
│   │   ├── MybatisDemoApplication.java // 修正したファイル
│   │   ├── domain
│   │   │   └── Todo.java // 追加したファイル
│   │   └── mapper
│   │   └── TodoMapper.java // 追加したファイル
│   └── resources
│   ├── application.properties
│   ├── com
│   │   └── example
│      │ └── mybatisdemo
│   │   └── mapper
│   │   └── TodoMapper.xml // 追加したファイル(SQLをXMLに記述する場合のみ)
│   └── schema.sql // 追加したファイル
└── test
└── java
└── com
└── example
└── mybatisdemo
└── MybatisDemoApplicationTests.java


JUnit上でMybatisDemoApplicationを起動

ダウンロードした開発プロジェクトには、JUnit用のテストケースクラス(MybatisDemoApplicationTests)が格納されています。


コンソール

$ ./mvnw test

...
2019-07-15 17:36:55.348 INFO 55839 --- [ main] c.e.m.MybatisDemoApplicationTests : Started MybatisDemoApplicationTests in 1.4 seconds (JVM running for 2.08)
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.125 s - in com.example.mybatisdemo.MybatisDemoApplicationTests
2019-07-15 17:36:55.688 INFO 55839 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-07-15 17:36:55.690 INFO 55839 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.304 s
[INFO] Finished at: 2019-07-15T17:36:56+09:00
[INFO] ------------------------------------------------------------------------

テストは成功したみたいですが、ダウンロードした状態だとテスト結果をassertしていません。

せっかくなので、MybatisDemoApplicationTestsを以下のように修正してテスト結果をassertしてみます。


src/test/java/com/example/mybatisdemo/MybatisDemoApplicationTests.java

package com.example.mybatisdemo;

import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.Matchers.containsString;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisDemoApplicationTests {

@ClassRule
public static OutputCapture out = new OutputCapture(); // System.outの内容をキャプチャする

@Test
public void contextLoads() {
// System.outした内容を検証する
out.expect(containsString("ID : 1"));
out.expect(containsString("TITLE : 飲み会"));
out.expect(containsString("DETAILS : 銀座 19:00"));
out.expect(containsString("FINISHED : false"));
}

}


再度テストを実行すると、標準出力した内容が正しいことが検証できました


Mapperインタフェースのスキャンの仕組み

なにやらMyBatisを使ってデータベースにアクセスすることはできましたが、どうやってMapperインタフェースがスキャンされたのでしょうか??

mybatis-springの機能だけだと、@MapperScanを使ってスキャン対象のベースパッケージを指定する必要がありました。しかし、mybatis-spring-boot-starterを使うとSpring Bootアプリケーション(MybatisDemoApplication)が格納されているパッケージ(com.example)配下に格納されているインターフェースのうち、@org.apache.ibatis.annotations.Mapper(mybatis 3.4で追加されたアノテーション)が付与されたインタフェースがMapperインターフェースとしてスキャンされます。


Note:

mybatis-spring-boot-starter 1.0系では、Spring Bootアプリケーション(MybatisDemoApplication)が格納されているパッケージ(com.example)配下に格納されているインタフェースが全てMapperインタフェースとしてスキャンされていました。

この仕組みは便利な反面で、意図していないインターフェースがMapperインタフェースとしてスキャンされてDIコンテナに登録されてしまうという問題がありました。 @Mapperはこの問題を解決する手段として追加されたマーカーアノテーションです。


Mapperインタフェースに@Mapperを付与しない場合は、@MapperScanを使ってスキャン対象のベースパッケージを明示的に指定しましょう。


src/main/java/com/example/mybatisdemo/MybatisDemoApplication.java

@MapperScan("com.example.mybatisdemo.mapper") // スキャンするベースパッケージを明示的に指定する

@SpringBootApplication
public class MybatisDemoApplication {
// ...
}


タイプエイリアスの利用

SQLをXMLに記述する際に、parameterTyperesultType属性にクラスのFQCN(Fully Qualified Class Name)ではなく、シンプルなクラス名(エイリアス名)を指定したい場合があると思います。

そのような場合は、applicaion.propertiesに以下の定義を追加するだけです。


src/main/resources/applicaion.properties

mybatis.type-aliases-package=com.example.mybatisdemo.domain


カンマ区切りで複数のベースパッケージを指定することができます。2.0.1よりワイルドカード(例:com.example.**.domain)が指定できるようになっています(mybatis-spring 2.0.1+で利用可能だが2.0.2+の利用を推奨)。


Important:

2.0.1でmybatis.type-aliases-packageを使用すると、特定の条件をみたす時にエラーが発生する不具合が報告されています。不具合の詳細については、mybatis-spring-boot-starter 2.0の変更点を参照してください。

2019/7/15 追記

2.1.0で解決済みです。


2.0.0よりタイプエイリアスを登録するクラスであることを示す親クラスを指定するためのプロパティが追加されています。これはmybatis-springでもともとサポートされていた仕組みを、Spring Bootのコンフィギュレーションプロパティで指定できるように改善したものです。


src/main/resources/applicaion.properties

mybatis.type-aliases-super-type=com.example.mybatisdemo.TypeAliasTarget



タイプハンドラーの適用

MyBatisがデフォルトで用意していない型のタイプハンドラーを追加したい場合や、デフォルトで適用されるタイプハンドラーを上書きしたい場合があると思います。

そのような場合は、applicaion.propertiesに以下の定義を追加するだけです。


src/main/resources/applicaion.properties

mybatis.type-handlers-package=com.example.mybatisdemo.typehandler


カンマ区切りで複数のベースパッケージを指定することができます。2.0.1よりワイルドカード(例:com.example.**.typehandler)が指定できるようになっています(mybatis-spring 2.0.1+で利用可能だが2.0.2+の利用を推奨)。

バージョン2.1.0より、DIコンテナに登録したTypeHandlerを検出してMyBatisに適用することができます。


src/main/java/com/example/mybatisdemo/MybatisDemoApplication.java

@SpringBootApplication

public class MybatisDemoApplication {
@Bean
MyTypeHandler myTypeHandler(){
return new MyTypeHandler();
}
}

複数のTypeHandlerをMyBatisに適用したい場合は、それぞれBean定義するだけです。


SqlSessionの実行モードの変更

デフォルトの実行モードはMyBatis設定の設定値(デフォルトはSIMPLEモード)ですが、アプリケーションの種類や要件によっては実行モードを変更したい場合があると思います。

そのような場合は、applicaion.propertiesに以下の定義を追加するだけです。もちろんMyBatis設定の設定値を変更してもOKです。


src/main/resources/applicaion.properties

mybatis.executor-type=BATCH



Mapper XMLファイルを明示的に読み込む

Mapperインタフェースを経由しないで直接SqlSession(SqlSessionTemplate)を使ってSQLを実行する場合は、SQLが定義されているMapper XMLファイルを明示的に読み込む必要があります。

そのような場合は、applicaion.propertiesに以下の定義を追加するだけです。


src/main/resources/applicaion.properties

mybatis.mapper-locations=classpath*:/mybatis/sqls/**/*.xml


カンマ区切りで複数のロケーションを指定することもできますし、


src/main/resources/applicaion.properties

mybatis.mapper-locations[0]=classpath*:/mybatis/sqls/aaa/**/*.xml

mybatis.mapper-locations[1]=classpath*:/mybatis/sqls/bbb/**/*.xml

という感じで指定することもできます。

また、propertiesファイルではなくyamlファイルを使用すると、


src/main/resources/applicaion.yml

mybatis:

mapper-locations:
- classpath*:/mybatis/sqls/aaa/**/*.xml
- classpath*:/mybatis/sqls/bbb/**/*.xml

という記述にすることもできます。


MyBatisの設定

バージョン1.0系では、MyBatis自体の動作をカスタマイズしたい場合は、MyBatis設定ファイルの<settings>タグ内に設定を追加する必要がありましたが、バージョン1.1系からapplicaion.propertiesに直接設定できるようになりました。また、バージョン1.3(1.2.1にもバックポート済み)からConfigurationCustomizerインタフェースが追加され、MyBatisの設定を完全にカスタマイズすることが可能になっています。


MyBatis設定ファイル使用時の設定例


src/main/resources/applicaion.properties

mybatis.config-location=classpath:/mybatis/mybatis-config.xml



src/main/resources/mybatis/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" />
<setting name="defaultFetchSize" value="100" />
<setting name="defaultStatementTimeout" value="30" />
</settings>
</configuration>

もちろん<settings>以外のタグも利用できます。

ちなみに・・・MyBatis設定ファイルが存在するかチェックを行う仕組みも提供されており、デフォルトでは無効になっています。この仕組みを有効にしたい場合は、以下の定義を追加してください。


src/main/resources/applicaion.properties

mybatis.check-config-location=true



applicaion.properties使用時の設定例

MyBatis設定ファイルを使わずにapplicaion.propertiesだけで設定することもできます。


src/main/resources/applicaion.properties

mybatis.configuration.map-underscore-to-camel-case=true

mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=30

なお、mybatis.configuration.xxxxmybatis.config-locationを同時に指定することはできません。

指定するとエラーになります。


Note:

バージョン1.1.1までは、IDE上でmybatis.configuration配下のプロパティが補完されませんでしたが、バージョン1.2で解決されています。(ただ・・・同じプロパティキーが重複してるな・・・・)

2017/1/8 追記

入力補完時にプロパティキーが重複しているのは・・・maven-bundle-plugin 2.5.4のバグ?が原因っぽく、maven-bundle-plugin 3.0.0+を使ってビルドしたら解決できました。これは、mybatis-spring-boot-starter#gh-127で対応予定でバージョン1.2.1で直ると思います。

2017/4/10 追記

入力補完時にプロパティキーが重複する問題は、バージョン1.3(1.2.1にもバックポート済み)で解決済みです。



ConfigurationCustomizerを使用したカスタマイズ例

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


ラムダ式を使用したConfigurationCustomizerのBean定義例

@Configuration

public class MyBatisConfiguration {
@Bean
ConfigurationCustomizer mybatisConfigurationCustomizer() {
return (configuration) -> {
// aplication.propertiesで指定・表現できないカスタマイズコードを実装
configuration.getTypeHandlerRegistry().register(RoundingMode.class, EnumOrdinalTypeHandler.class);
};
}
}


外部定義したプロパティ値の適用

MyBatisでは、外部定義したプロパティ値をMyBatis設定ファイルやMapperファイルの中に埋め込むことができ、外部定義はMyBatis設定ファイルの<properties>要素を使って指定します。(プログラムで直接指定することもできますが、本投稿では扱いません)

さらに、MyBatis-Springを使う場合はMyBatis設定ファイルの<properties>要素に加えて、SqlSessionFactoryBeanconfigurationPropertiesプロパティ(java.util.Properties型)に外部定義を指定することができ、バージョン1.2より、application.properties(or application.yml)を使って指定できるようになりました。

例えば、MyBatis設定ファイルで以下のように定義していた部分は・・・


MyBatis設定ファイル(抜粋)

<properties>

<property name="key" value="value"/>
</properties>

以下のように、application.properties(or application.yml)の中で指定するように書き換えることができます。


src/main/resources/application.properties

mybatis.configuration-properties.key=value



Note:

実は・・・バージョン1.1.1でも、以下のように定義することで同じことが実現できました。この方法はバージョン1.2以降でも使うことができ、同じキー名がある場合はバージョン1.2で追加されたmybatis.configuration-propertiesで指定した値が優先されます。


src/main/resources/application.properties

mybatis.configuration.variables.key=value


たとえば・・・mybatis.configuration.variablesには環境依存しない値(or デフォルト値)を定義しておき、プロファイル毎(環境毎)のプロパティファイルでmybatis.configuration-propertiesを使用して環境依存値に上書きするといった使い方もできます。



Plugin(Interceptor)の適用

MyBatisは、MyBatisのコアコンポーネントが行う処理に割り込む仕組み(Interceptor)を提供しています。

この仕組みを利用することはあまりないと思いますが、MyBatisにInterceptorを適用したい場合は、以下のようなBean定義を追加するだけです。


src/main/java/com/example/mybatisdemo/MybatisDemoApplication.java

@SpringBootApplication

public class MybatisDemoApplication {
@Bean
Interceptor myInterceptor(){
return new MyInterceptor();
}
// ...
}

複数のIntereptorをMyBatisに適用したい場合は、それぞれBean定義するだけです。


DatabaseIdProviderの適用

MyBatisは、接続中のデータベースを識別するための仕組み(DatabaseIdProvider)を提供しています。

この仕組みを利用することはあまりないと思いますが、MyBatisにDatabaseIdProviderを適用したい場合は、以下のようなBean定義を追加するだけです。


src/main/java/com/example/mybatisdemo/MybatisDemoApplication.java

@SpringBootApplication

public class MybatisDemoApplication {
@Bean
VendorDatabaseIdProvider vendorDatabaseIdProvider() {
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties vendorProperties = new Properties();
vendorProperties.put("PostgreSQL", "postgresql");
vendorProperties.put("H2", "h2");
databaseIdProvider.setProperties(vendorProperties);
return databaseIdProvider;
}
// ...
}


LanguageDriverの適用

MyBatisは、動的SQLを組み立てるため言語を変更(カスタマイズ)するための仕組み(LanguageDriver)を提供しています。

デフォルトではMyBatisに組み込まれているXMLベースの言語(XMLLanguageDriver)が適用されますが、デフォルトの動作を変更したい場合は、開発者が独自に作成した LanguageDriver クラスは、以下のようなBean定義を追加するだけです(バージョン2.1より利用可能)。


独自のLanguageDriverのBean定義例

@Bean

MyLanguageDriver myLanguageDriver() {
return MyLanguageDriver();
}

さらに、DIコンテナから検出した LanguageDriver のBeanが1つの場合は、検出した LanguageDriver がデフォルトの LanguageDriver クラスとして扱われます(複数のBeanを検出した場合は、MyBatis本体のデフォルト設定である XMLLanguageDriver がそのまま適用されます)。自動検出機能によってデフォルトの LanguageDriver クラスを変更したくない場合や、複数の LanguageDriver を共存して利用する際にデフォルトの LanguageDriver クラスを変更したい場合は、 次に紹介する mybatis.default-scripting-language-driver プロパティ(バージョン2.1で追加したプロパティ)を使用して、デフォルトの LanguageDriver クラスを明示的に指定するようにしてください。


デフォルトで使用するLanguageDriverの指定

デフォルトで使用するLanguageDriverを明示的に指定する場合は、以下のように設定するだけです。

mybatis.default-scripting-language-driver={デフォルトのLanguageDriverクラスのFQCN}

このプロパティは、バージョン2.1より利用可能です。なお、バージョン2.0までは、デフォルトで使う LanguageDriver クラスを指定する方法として、 mybatis.configuration.default-scripting-language というプロパティ(MyBatis本体が提供するコンフィギュレーションクラスへ直接値を設定するためのプロパティ)をサポートしていましたが、後述の「LanguageDriverの自動コンフィグレーション」と組み合わせた時に期待通りの動作にならない事があるため、このプロパティはバージョン2.1で廃止(使用禁止)になっています。


LanguageDriverの自動コンフィグレーション

バージョン2.1より、MyBatis純正の以下の3つのサブモジュール(LanguageDriver)については、自動コンフィギュレーションの仕組みがサポートされています。

上記3つのサブモジュールのjarファイルをクラスパス上に追加すると、自動でサブモジュール提供の LanguageDriver クラスのBeanがDIコンテナに登録されてMyBatisへ適用されます。なお、開発者が明示的に上記サブモジュールから提供されているLanguageDriverクラスをBean定義した場合は、自動コンフィギュレーションは行われません。


WARNING:

以下の条件に全て一致する場合は、下位互換性が失われるため、追加で対策が必要になります。


  • 上記3つのサブモジュール(mybatis-velocity, mybatis-freemarker, mybatis-thymeleaf)のjarが1つのみクラスパス上にある

  • デフォルトの LanguageDriver がサブモジュール提供のLanguageDriverではない

条件に一致する場合は、サブモジュール提供のLanguageDriverクラスがデフォルトの LanguageDriver に設定される仕組みになっているため、以下のように明示的にデフォルトの LanguageDriver を指定する必要があります。

mybatis.default-scripting-language-driver={デフォルトのLanguageDriverクラスのFQCN}



MyBatis Velocityの利用

以下のアーティファクトを追加すると、org.mybatis.scripting.velocity.VelocityLanguageDriver(mybatis-velocity 2.0以前を指定するとorg.mybatis.scripting.velocity.Driver)をMyBatisへ適用します。


pom.xml

<!-- Mybatis Velocityを利用する場合 -->

<dependency>
<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-velocity</artifactId>
<version>2.1.0</version>
</dependency>

mybatis-velocity 2.1.0以降を利用する場合は、コンフィギュレーションプロパティの仕組みを利用してMyBatis VelocityおよびMyBatis Velocityの中で利用するVelocityのテンプレートエンジンの動作をカスタマイズすることができます。


src/main/resources/application.properties

#

# mybatis.scripting-language-driver.velocity.{key} = {value} 形式
#

# Velocity本体の動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.velocity.velocity-settings.{name} = {value}形式
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.velocity.velocity-settings.runtime.custom_directives = com.example.directives.MyDirective

# テンプレートエンジンに渡す追加属性(オブジェクト)を指定するプロパティ
# mybatis.scripting-language-driver.velocity.additional-context-attributes.{name} = {value}
mybatis.scripting-language-driver.velocity.additional-context-attributes.likeEscape = com.example.helpers.LikeEscape



MyBatis FreeMarkerの利用

以下のアーティファクトを追加すると、org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverをMyBatisへ適用します。


pom.xml

<dependency>

<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-freemarker</artifactId>
<version>1.2.0</version>
</dependency>

mybatis-velocity 1.2.0以降を利用する場合は、コンフィギュレーションプロパティの仕組みを利用してMyBatis FreeMarkerおよびMyBatis FreeMarkerの中で利用するFreeMarkerのテンプレートエンジンの動作をカスタマイズすることができます。


src/main/resources/application.properties

#

# mybatis.scripting-language-driver.freemarker.{key} = {value} 形式
#

# FreeMarker本体の動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.freemarker.freemarker-settings.{name} = {value}形式
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.freemarker.freemarker-settings.interpolation_syntax = dollar

# MyBatis FreeMarkerの動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.freemarker.{name} = {value}
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.freemarker.template-file.base-dir = sql



MyBatis Thymeleafの利用

以下のアーティファクトを追加すると、org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverをMyBatisへ適用します。


pom.xml

<dependency>

<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-thymeleaf</artifactId>
<version>1.0.1</version>
</dependency>

コンフィギュレーションプロパティの仕組みを利用してMyBatis ThymeleafおよびMyBatis Thymeleafの中で利用するThymeleafのテンプレートエンジンの動作をカスタマイズすることができます。


src/main/resources/application.properties

#

# mybatis.scripting-language-driver.thymeleaf.{key} = {value} 形式
#

# MyBatis Thymeleafの動作をカスタマイズするプロパティ
# mybatis.scripting-language-driver.thymeleaf.{name} = {value}
# {name}に設定可能なプロパティはリファレンスドキュメントを参照してください。
mybatis.scripting-language-driver.thymeleaf.use2way = false
mybatis.scripting-language-driver.thymeleaf.template-file.cache-enabled = false
mybatis.scripting-language-driver.thymeleaf.dialect.like-additional-escape-target-chars = %, _


Mapperの遅延初期化制御

バージョン2.1より、DIコンテナに登録したMapperの初期化タイミング(DIコンテナ初期化時、インジェクションまたは利用時)を制御することができます。デフォルトの動作は、DIコンテナ初期化時に全てのMapperが初期化されます。

これは、Spring Boot 2.2(投稿時点では正式版は未リリース)でサポートされるBeanの遅延初期化の仕組みを有効化した際に、特定の条件下においてMapprのメソッド呼び出し時にエラーが発生してしまうため、Mapperに対する遅延初期化の適用有無を利用者側で制御できるようにするために追加した仕組みです。エラーを回避するだけなら、Mapperに対する遅延初期化を無条件で無効化すればよいのですが、開発時(テスト時)には遅延初期化の仕組みは有効だと思うので、MyBatis(mybatis-spring & mybatis-spring-boot-starter)独自にMapperを遅延初期化を制御できるようにしました。詳しくは、「mybatis-spring-boot 2.1.0で追加されるmybatis.lazy-initializationについて」をご覧ください。


NOTE:

バージョン2.1より提供する遅延初期化の仕組みはMyBatis(mybatis-spring & mybatis-spring-boot-starter)独自の仕組みなので、Spring Boot 2.1系でも利用することができます。



@MybatisTest(mybatis-spring-boot-starter-test)の利用

バージョン1.3より、MyBatisの機能をテストする時に必要となるBean定義をサポートする@MybatisTestアノテーションが追加されました。これは、Spring Bootが提供する@DataJpaTest@JdbcTestのMyBatis版になります(当然ながら・・使い方もSpring Boot提供のアノテーションと同じです!!)。

この対応に伴い、MyBatisのテストをする際に必要となるライブラリ群を解決するためのmybatis-spring-boot-starter-testが新設されているので、MyBatis提供の機能に対するテストを行う場合は、pom.xmlに以下の定義を追加してください。


pom.xml

<dependency>

<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>

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


src/main/java/com/example/mybatisdemo/mapper/TodoMapper.java

package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.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, keyProperty = "id")
void insert(Todo todo);

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

}


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


src/main/java/com/example/mybatisdemo/mapper/TodoMapperTests.java

package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.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を付与すると、JdbcTemplateNamedParameterJdbcTemplateの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アプリケーションクラスを無効化することができます。


src/test/java/com/example/mybatisdemo/mapper/MapperTestApplication.java

package com.example.mybatisdemo.mapper;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MapperTestApplication {

}


さらに詳しい情報は、公式リファレンスおよびSpring Bootが提供する@DataJpaTest@JdbcTestのリファレンスをご覧ください。


まとめ

今回は、仕事でよく使うMyBatisをSpring Bootで動かす時に使うと便利なmybatis-spring-boot-starterを紹介しました。MyBatisのデフォルトの動作で問題なければ、特別な設定はいっさい不要です。また、バージョン1.3よりテストをサポートする新機能(@MybatisTest)が追加されており、MapperやDAOのテストを行う際に必要となるBean定義を(いろんな意味で)効率的に行うことができます。


参考サイト