KotlinでMyBatisを使う時のTips

kotlinでMyBatisを使うときのTipsメモです。


kotlinでMyBatisを使う時に少しはまった点をまとめておきます。
今後他に何か遭遇したら追記していきます。

data classを使う

mappingでcollectionやassociationを使う時にdata classが使えなくて困りました。

例えば下記のような場合。
GroupとUserのマッピングで、Listを持っている場合、
Userがdata classだとマッピングされません。

User.kt

data class User (
    val userId: Int,
    val name: String,
    val age: Int
)

Group.kt

data class GroupUser (
    val groupId: Int,
    val users: List<User>
)

UserDao.kt

interface UserDao {
    @Select("""
        SELECT gu.group_id, u.* FROM group_user gu
        JOIN user u ON gu.user_id = u.user_id
        WHERE gu.group_id = #{groupId}
    """)
    @ResultMap("groupUser")
    fun selectGroupUser(groupId: Int): GroupUser
}

UserDao.xml

<mapper namespace="com.example.kotlin.mybatis.dao.UserDao">
  <resultMap id="groupUser" type="com.example.kotlin.mybatis.model.GroupUser">
    <id column="group_id" property="groupId" />
    <collection property="users" ofType="com.example.kotlin.mybatis.model.User" autoMapping="true">
      <id column="user_id" property="userId"/>
    </collection>
  </resultMap>
</mapper>


下記のようにvarで普通のclassにすれば解決しますが、出来ればdata classを使いたいです。

User.kt

class User {
    var userId: Int = 0
    lateinit var name: String
    var age: Int = 0
}

GroupUser.kt

class GroupUser {
    var groupId: Int = 0
    var users = mutableListOf<User>()
}

no arg compiler pluginを使うと解決しました。
https://kotlinlang.org/docs/reference/compiler-plugins.html#no-arg-compiler-plugin

noargのpluginを追加します

build.gradle

plugins {
    id "org.jetbrains.kotlin.plugin.noarg" version "1.3.41"
}

no argを示すためのアノテーションを用意。

NoArg.kt

annotation class NoArg

build.gradleにno argのアノテーションのパスを指定します

build.gradle

noArg {
    annotation("com.example.kotlin.mybatis.annotations.NoArg")
}

後は、@NoArgアノテーションをdata classに付けるだけです。

User.kt

@NoArg
data class User (
    val userId: Int,
    val name: String,
    val age: Int
)

Group.kt

@NoArg
data class GroupUser (
    val groupId: Int,
    val users: List<User>
)

実行するとうまく取れました。

実行結果

-- select using collection --
user: User(userId=1, name=Alice, age=20)
user: User(userId=2, name=bob, age=19)
user: User(userId=3, name=Cindy, age=33)


Annotationでの${}のバインド

MyBatisでは
${value} は指定した文字列が直接代入され、
#{value} は安全にエスケープされて代入されます。
SQLインジェクションのリスクがあるので、普通は#{value}を使います。
http://www.mybatis.org/mybatis-3/ja/sqlmap-xml.html

しかし、何らかのケースでAnnotationで${}を使いたい場合、
${}はkotlinのStringTemplateとかぶってて使えません。

下記のようには書けません。

UserDao.kt

interface UserDao {
    // error!
    @Select("""
        SELECT * FROM user
        ORDER BY ${column}
    """)
    fun selectOrderBy(column: String): List<User>
}

その場合、${"$"}で書いてやると大丈夫です。

interface UserDao {
    @Select("""
        SELECT * FROM user
        ORDER BY ${"$"}{column}
    """)
    fun selectOrderBy(column: String): List<User>
}


【参考】
https://github.com/jeffgbutler/mybatis-kotlin-examples/blob/master/src/test/kotlin/example01/Example01Test.kt
https://qiita.com/akira108/items/005830d44075c7968dac


サンプルコードは下記にあげました。
github.com


おわり。