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

  • 8
    いいね
  • 2
    コメント

以前「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)ベースに更新しました。

動作検証バージョン

  • Kotlin 1.1.1
  • MyBatis Spring Boot 1.3.0
  • MyBatis 3.4.4
  • MyBatis Spring 1.3.1
  • Spring Boot 1.5.2.RELEASE
  • Spring Framework 4.3.7.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/domain/mapper/TodoMapper.kt
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
interface TodoMapper {

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

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

}

開発プロジェクトの作成

今回も「SPRING INITIALIZR」を使います。Kotlinを使う場合は、「Switch to the full version.」というリンクをクリックしましょう。ウィザードに以下の値を入力して「Generate Project」ボタンを押下すると、Mavenプロジェクトがダウンロードされます。

入力項目 備考
Project Type Maven Project ※デフォルト
Version 1.5.2 ※デフォルト
Group com.example ※デフォルト
Artifact mybatis-kotlin-demo ※デフォルト(demo)から変更
Name mybatis-kotlin-demo ※デフォルト
Description Demo project for Spring Boot ※デフォルト
Package Name com.example ※デフォルト
Packaging jar ※デフォルト
Java Version 1.8 ※デフォルト
Language Kotlin ※デフォルト(Java)から変更
Dependencies MyBatis, H2

spring-initializr-kotlin.png

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

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

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

src/main/kotlin/com/example/domain/Todo.kt
package com.example.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/domain/mapper/TodoMapper.kt
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
interface TodoMapper {

    @Insert("""
        INSERT INTO todo
            (title, details, finished)
        VALUES
            (#{title}, #{details}, #{finished})
    """)
    @Options(useGeneratedKeys = true)
    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/MybatisKotlinDemoApplication.kt
package com.example

import com.example.domain.Todo
import com.example.mapper.TodoMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.transaction.annotation.Transactional

@SpringBootApplication
class MybatisKotlinDemoApplication : CommandLineRunner {

    @Autowired
    lateinit var todoMapper: TodoMapper

    @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>) {
    SpringApplication.run(MybatisKotlinDemoApplication::class.java, *args)
}

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

コンソール
$ ./mvnw spring-boot:run
...
2017-04-11 00:34:06.333  INFO 17388 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
ID       : 1
TITLE    : 飲み会
DETAILS  : 銀座 19:00
FINISHED : false
2017-04-11 00:34:06.402  INFO 17388 --- [           main] c.e.MybatisKotlinDemoApplicationKt       : Started MybatisKotlinDemoApplicationKt in 1.545 seconds (JVM running for 7.118)

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

JUnit上でMybatisKotlinDemoApplicationを起動

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

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

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
...
ID       : 1
TITLE    : 飲み会
DETAILS  : 銀座 19:00
FINISHED : false
2017-04-11 00:34:36.795  INFO 17408 --- [           main] c.e.MybatisKotlinDemoApplicationTests    : Started MybatisKotlinDemoApplicationTests in 1.257 seconds (JVM running for 1.844)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.362 sec - in com.example.MybatisKotlinDemoApplicationTests
2017-04-11 00:34:36.844  INFO 17408 --- [       Thread-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@44a7bfbc: startup date [Tue Apr 11 00:34:35 JST 2017]; root of context hierarchy

Results :

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

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.633 s
[INFO] Finished at: 2017-04-11T00:34:36+09:00
[INFO] Final Memory: 44M/614M
[INFO] ------------------------------------------------------------------------

まとめ

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

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

補足

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

参考サイト