Edited at

Kotlinでmybatis-spring-boot-starterを使う

以前「GroovyでMyBatisのMapperをつくる(とSQLの可読性がGood!!)」という投稿をしましたが、今回は最近話題?のKotlin上でMaBatis(mybatis-spring-boot-starter)を使ってみました。

なお、題材は以前投稿した「mybatis-spring-boot-starterの使い方」と一緒です。


Note:

2017/4/10:

mybatis-spring-boot-starter 1.3.0 (Spring Boot 1.5.2.RELEASE)ベースに更新しました。

2019/5/3:

mybatis-spring-boot-starter 2.0.1 (Spring Boot 2.1.4.RELEASE)ベースに更新しました。



動作検証バージョン


  • Kotlin 1.2.71

  • MyBatis Spring Boot 2.0.1

  • MyBatis 3.5.1

  • MyBatis Spring 2.0.1

  • Spring Boot 2.1.4.RELEASE

  • Spring Framework 5.1.6.RELEASE


Note:IDEは・・・

もちろんIntelliJ IDEA :smile: (本投稿はIDEなし前提ですが・・・)



Kotlin版でのアプローチ

Groovy版は主言語はあくまでJavaで、Javaアノテーションの表現の貧弱なところをGroovyの複数行文字列(いわゆる「ヒアドキュメント」に分類される仕組み)を利用することで解消するというアプローチでした。Kotlin版は、主言語自体をKotlinとして、Kotlinの中からMyBatis(mybatis-spring-boot-starter)を利用するというアプローチになっています。


Kotlinの複数行文字列(ヒアドキュメント!?)を使う

Groovy版と同様に、SQLはKotlinのアノテーションを使って指定します。Kotlinのアノテーションを利用すると以下のような感じでSQLが記載できます :v:


src/main/kotlin/com/example/mybatisdemo/domain/mapper/TodoMapper.kt

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
interface TodoMapper {

@Insert("""
INSERT INTO todo
(title, details, finished)
VALUES
(#{title}, #{details}, #{finished})
"""
)
@Options(useGeneratedKeys = true, keyProperty = "id")
fun insert(todo: Todo)

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

}



開発プロジェクトの作成

今回も「SPRING INITIALIZR」を使います。ウィザードに以下の値を入力して「Generate Project」ボタンを押下すると、Mavenプロジェクトがダウンロードされます。なお、パッケージ名を変更するためには「More Options」をクリックする必要があります。

入力項目

備考

Project
Maven Project
※デフォルト

Language
Kotlin
※デフォルト(Java)から変更

Version
2.1.4
※デフォルト

Group
com.example
※デフォルト

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

Name
mybatis-kotlin-demo
※デフォルト

Description
Demo project for Spring Boot
※デフォルト

Package Name
com.example.mybatisdemo
※デフォルト(com.example.mybatiskotlindemo)から変更

Packaging
jar
※デフォルト

Java Version
8
※デフォルト

Dependencies
MyBatis, H2

image.png

任意のディレクトリにダウンロードしたZipファイルを解凍すれば、Mavenプロジェクトの出来上がりです。


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

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


src/main/kotlin/com/example/mybatisdemo/domain/Todo.kt

package com.example.mybatisdemo.domain

class Todo {
var id: Int = 0
var title: String = ""
var details: String? = null
var finished: Boolean = false
}



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

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


src/main/kotlin/com/example/mybatisdemo/domain/mapper/TodoMapper.kt

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
interface TodoMapper {

@Insert("""
INSERT INTO todo
(title, details, finished)
VALUES
(#{title}, #{details}, #{finished})
"""
)
@Options(useGeneratedKeys = true, keyProperty = "id")
fun insert(todo: Todo)

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

}



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


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

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


src/main/kotlin/com/example/mybatisdemo/MybatisKotlinDemoApplication.kt

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.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.transaction.annotation.Transactional

@SpringBootApplication
class MybatisKotlinDemoApplication (private val todoMapper: TodoMapper) : CommandLineRunner {

@Transactional
override fun run(vararg args: String?) {
val newTodo: Todo = Todo()
newTodo.title = "飲み会"
newTodo.details = "銀座 19:00"

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

val loadedTodo: Todo = todoMapper.select(newTodo.id) // インサートしたTodoを取得して標準出力する
println("ID : " + loadedTodo.id)
println("TITLE : " + loadedTodo.title)
println("DETAILS : " + loadedTodo.details)
println("FINISHED : " + loadedTodo.finished)
}

}

fun main(args: Array<String>) {
runApplication<MybatisKotlinDemoApplication>(*args)
}


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


コンソール

$ ./mvnw spring-boot:run

...
2019-05-03 14:25:17.711 INFO 32402 --- [ main] c.e.m.MybatisKotlinDemoApplicationKt : Started MybatisKotlinDemoApplicationKt in 1.919 seconds (JVM running for 13.144)
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.610 s
[INFO] Finished at: 2019-05-03T14:25:17+09:00
[INFO] Final Memory: 55M/606M
[INFO] ------------------------------------------------------------------------
2019-05-03 14:25:18.133 INFO 32402 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-05-03 14:25:18.137 INFO 32402 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.

お〜MyBatisと連携できましたね :clap: :clap: :clap:


JUnit上でMybatisKotlinDemoApplicationを起動

ダウンロードした開発プロジェクトには、JUnit用のテストケースクラス(MybatisKotlinDemoApplicationTests)が格納されています。せっかくなので、MybatisKotlinDemoApplicationTestsを以下のように修正してテスト結果をassertしてみます。


src/test/kotlin/com/example/mybatisdemo/MybatisKotlinDemoApplicationTests.kt

package com.example.mybatisdemo

import org.hamcrest.Matchers.containsString
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

@RunWith(SpringRunner::class)
@SpringBootTest
class MybatisKotlinDemoApplicationTests {

companion object {
@ClassRule @JvmField val out = OutputCapture()
}

@Test
fun contextLoads() {
out.expect(containsString("ID : 1"))
out.expect(containsString("TITLE : 飲み会"))
out.expect(containsString("DETAILS : 銀座 19:00"))
out.expect(containsString("FINISHED : false"))
}

}


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


コンソール

$ ./mvnw clean test

...
2019-05-03 14:27:36.468 INFO 32460 --- [ main] c.e.m.MybatisKotlinDemoApplicationTests : Started MybatisKotlinDemoApplicationTests in 1.477 seconds (JVM running for 2.456)
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.516 s - in com.example.mybatisdemo.MybatisKotlinDemoApplicationTests
2019-05-03 14:27:36.832 INFO 32460 --- [ Thread-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-05-03 14:27:36.841 INFO 32460 --- [ 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: 11.338 s
[INFO] Finished at: 2019-05-03T14:27:37+09:00
[INFO] Final Memory: 46M/566M
[INFO] ------------------------------------------------------------------------


まとめ

Kotlin上でも問題なくMyBatisが使えることと、アノテーションにSQLを書いても可読性が下がらないことが確認できました。KotlinはJavaに置き換わる言語として期待・注目されているみたいなので、わたしも勉強しようと思っています。(本は買ったけどまだ読んでない :sweat_smile:

なお、本投稿で紹介したプロジェクトの完成品は、GitHubで公開しています。


補足

Kotlinは超初心者なので、へんなところあるかもしれません(あしからず・・・)


参考サイト