Java
spring
MyBatis
spring-boot

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定義は自動コンフィギュレーションによって解決されます :smile:

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


Note:

2017/1/4: 追記

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

2017/4/10: 追記

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


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


動作検証バージョン


  • MyBatis Spring Boot Starter 1.3.0

  • MyBatis 3.4.4

  • MyBatis Spring 1.3.1

  • Spring Boot 1.5.2.RELEASE

  • Spring Framework 4.3.7.RELEASE


検証コード


Java版


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を使う」)を投稿しました。


Raw String Literals版(JDK 12)

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


開発プロジェクトの作成

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

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

入力項目

備考

Project Type
Maven Project
※デフォルト

Version
1.5.2
※デフォルト

Group
com.example
※デフォルト

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

Dependencies
MyBatis, H2

spring-initializr.png

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


ディレクトリ構成

.

├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── example
│   │   └── MybatisDemoApplication.java
│   └── resources
│   └── application.properties
└── test
└── java
└── com
└── example
└── 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] ------------------------------------------------------------------------
[INFO] Building mybatis-demo 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:1.5.2.RELEASE:run (default-cli) > test-compile @ mybatis-demo >>>
[INFO]
[INFO] --- maven-resources-plugin:2.6: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.1:compile (default-compile) @ mybatis-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ mybatis-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /usr/local/apps/mybatis-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ mybatis-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< spring-boot-maven-plugin:1.5.2.RELEASE:run (default-cli) < test-compile @ mybatis-demo <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:1.5.2.RELEASE:run (default-cli) @ mybatis-demo ---

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

2017-04-10 08:13:24.496 INFO 16389 --- [ main] com.example.MybatisDemoApplication : Starting MybatisDemoApplication on Kazuki-no-MacBook-Pro.local with PID 16389 (/usr/local/apps/mybatis-demo/target/classes started by shimizukazuki in /usr/local/apps/mybatis-demo)
2017-04-10 08:13:24.498 INFO 16389 --- [ main] com.example.MybatisDemoApplication : No active profile set, falling back to default profiles: default
2017-04-10 08:13:24.529 INFO 16389 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@312975db: startup date [Mon Apr 10 08:13:24 JST 2017]; root of context hierarchy
2017-04-10 08:13:24.711 WARN 16389 --- [ main] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in '[com.example]' package. Please check your configuration.
2017-04-10 08:13:25.190 INFO 16389 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-04-10 08:13:25.199 INFO 16389 --- [ main] com.example.MybatisDemoApplication : Started MybatisDemoApplication in 0.867 seconds (JVM running for 3.135)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.169 s
[INFO] Finished at: 2017-04-10T08:13:25+09:00
[INFO] Final Memory: 23M/315M
[INFO] ------------------------------------------------------------------------
2017-04-10 08:13:25.318 INFO 16389 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@312975db: startup date [Mon Apr 10 08:13:24 JST 2017]; root of context hierarchy
2017-04-10 08:13:25.319 INFO 16389 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown


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


mybatis-spring-boot-starterの確認

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


pom.xml

<dependency>

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

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


コンソール

$ ./mvnw dependency:tree

...
[INFO] +- org.mybatis.spring.boot:mybatis-spring-boot-starter:jar:1.3.0:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:1.5.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:1.5.2.RELEASE:compile
[INFO] | | | \- org.springframework:spring-context:jar:4.3.7.RELEASE:compile
[INFO] | | | +- org.springframework:spring-aop:jar:4.3.7.RELEASE:compile
[INFO] | | | \- org.springframework:spring-expression:jar:4.3.7.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.2.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.2.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.1.11:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.1.11:compile
[INFO] | | | +- org.slf4j:jcl-over-slf4j:jar:1.7.24:compile
[INFO] | | | +- org.slf4j:jul-to-slf4j:jar:1.7.24:compile
[INFO] | | | \- org.slf4j:log4j-over-slf4j:jar:1.7.24:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:1.5.2.RELEASE:compile
[INFO] | | +- org.apache.tomcat:tomcat-jdbc:jar:8.5.11:compile
[INFO] | | | \- org.apache.tomcat:tomcat-juli:jar:8.5.11:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:4.3.7.RELEASE:compile
[INFO] | | +- org.springframework:spring-beans:jar:4.3.7.RELEASE:compile
[INFO] | | \- org.springframework:spring-tx:jar:4.3.7.RELEASE:compile
[INFO] | +- org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:jar:1.3.0:compile
[INFO] | +- org.mybatis:mybatis:jar:3.4.4:compile
[INFO] | \- org.mybatis:mybatis-spring:jar:1.3.1:compile
...


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

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


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

package com.example.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/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);

}



Note:

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


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

@Mapper

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


src/main/resources/com/example/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.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.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/MybatisDemoApplication.java

package com.example;

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/MybatisDemoApplication.java

package com.example;

import com.example.domain.Todo;
import com.example.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

...
2017-04-10 08:22:10.820 INFO 16509 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
2017-04-10 08:22:10.890 INFO 16509 --- [ main] com.example.MybatisDemoApplication : Started MybatisDemoApplication in 1.811 seconds (JVM running for 5.112)
...

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

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


ディレクトリ構成


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


JUnit上でMybatisDemoApplicationを起動

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


コンソール

$ ./mvnw test

...
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
2017-04-10 08:22:56.784 INFO 16532 --- [ main] com.example.MybatisDemoApplicationTests : Started MybatisDemoApplicationTests in 1.192 seconds (JVM running for 1.749)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.304 sec - in com.example.MybatisDemoApplicationTests
2017-04-10 08:22:56.818 INFO 16532 --- [ Thread-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@68567e20: startup date [Mon Apr 10 08:22:55 JST 2017]; root of context hierarchy

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.394 s
[INFO] Finished at: 2017-04-10T08:22:56+09:00
[INFO] Final Memory: 25M/248M
[INFO] ------------------------------------------------------------------------


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

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


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

package com.example;

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"));
}

}


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


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/MybatisDemoApplication.java

@MapperScan("com.example.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.domain


カンマ区切りで複数のベースパッケージを指定することができます。


タイプハンドラーの適用

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

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


src/main/resources/applicaion.properties

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


カンマ区切りで複数のベースパッケージを指定することができます。


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/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/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;
}
// ...
}


@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>1.3.0</version>
<scope>test</scope>
</dependency>

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


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です。


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を付与すると、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/mapper/MapperTestApplication.java

package com.example.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定義を(いろんな意味で)効率的に行うことができます。


参考サイト