Komapperで遊んでみる
※2023-09-02現在での情報ですので、最新情報は、 Komapper公式ドキュメント などを見てください
環境
komapper = 1.12.1
java = 20.0.2-tem
gradle = 8.3
kotlin = 1.9.10
db = h2, mysql:5.7
MySQL5.7での動作しないところ
※MySQL8ではちゃんと動作します!
こちらのコード、SQLが以下のように実行されます。
delete from employee as t0_
が、MySQL5.7では、以下エラーとなります。
[42000][1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'as t0_' at line 1
原因
dialect.supportsAliasForDeleteStatement()
というフラグがtrueになっていると、エイリアス付きのDELETE文を生成するようです。
解決法
以下のようにフラグをfalseへoverrideする。これで、delete from employee
といったクエリになります
import org.komapper.jdbc.JdbcDatabase
import org.komapper.jdbc.JdbcDialect
import org.komapper.jdbc.JdbcDialects
import org.komapper.jdbc.DefaultJdbcDatabaseConfig
object MySQL5Dialect : JdbcDialect by JdbcDialects.get("mysql") {
override fun supportsAliasForDeleteStatement() = false
}
val db by lazy {
val config = DefaultJdbcDatabaseConfig(
dataSource = hikariDatasource,
dialect = MySQL5Dialect,
)
JdbcDatabase(config)
}
複数のdialectを使う場合の注意点
にあるとおり、ServiceLoaderを使っています。
で、以下のように複数のdialectを使う場合、FatJarにした際に問題点が出てきます。
val komapperVersion = "1.12.1"
implementation("org.komapper:komapper-dialect-h2-jdbc:$komapperVersion")
implementation("org.komapper:komapper-dialect-mysql-jdbc:$komapperVersion")
エラーの内容
Caused by: java.lang.IllegalStateException:
at org.komapper.jdbc.JdbcDialects.get(JdbcDialects.kt:24)
原因
ここで、ServiceLoader#loadを使っているため、FatJarにした際に、同じインターフェースを実装するクラスがServiceLoader.load経由で複数あると、うまくロードされないようです。
解決策
参考 Gradle Shadow Pluginで作成したfat/uber JARで、複数のJDBCドライバがロードできない
build.gradle.ktsに以下追加
plugins {
// ~
id("com.github.johnrengelman.shadow") version "8.1.1"
}
tasks {
withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>() {
mergeServiceFiles()
}
}
Komapperで遊んでみた感想
SQLと乖離があまり無いDSL(SQLオジサンにとっては学習コストが低い)
例えば、こういうのとか…
Exposed
Member.select(Member.id eq id).single()
Komapper
val m = Meta.Member
QueryDsl.from(m).where { m.id eq id }.single()
Exposedのselectメソッドがwhereなのは…
eager loading的な機能も使いやすい
SQLAlchemy のjoined loadingと似た感じで使える。
joinしてincludeAll()メソッドを呼べば紐付いた状態で返ってくる
ドキュメントが充実
開発者がSeasar2やDomaといったJavaのフレームワークを作っていた日本人でもある
SQLを書きたい場合も対応している
Domaの上位互換という位置づけ?なので、SQLを書きたい場合も対応している
TEMPLATEクエリ | Komapper
Ktorm | Dialects and Native SQL
一方、Ktormは、SQLを書きたい場合は、jdbcのjava.sql.Connectionを直接さわれって…もはや、SQLを書きたかったら、他のDBアクセスライブラリ(例えば、Apache Commons DbUtilsとか)と組み合わせたほうが良さそう。
エンティティの自動生成機能がついている
開発初期にすでにデータベースがあって、それを使いたい場合とか、自動生成機能があると便利
Gradleプラグイン | Komapper
開発中に、自動生成結果とdiffを取って、逐一反映していくというのも良さそう。
※@KomapperOneToManyをつけたりして、結局、自動生成結果と一致しなくなるので、自動生成結果をそのまま使うのは難しいかも
Kotlinに最適化されているので、DSLが簡潔
Kotlinのinfix記法が使えるので、Java系ライブラリのようにカッコが多くならなくて良い
中間処理と終端処理などが分かりにくい問題が発生しない
// Komapperは、こうはなっていない
QueryDsl.from(m).where { m.id eq id }.toList()
.map{
MemberDto(
id = it.id,
name = it.name,
address = it.address,
)
}
Komapperは、こうはなっていない。これだと、終端処理のメソッドを呼んだかどうかの意識をしないといけない。
// Komapperは、こうはなってる
db.runQuery(QueryDsl.from(m).where { m.id eq id })
.map{
MemberDto(
id = it.id,
name = it.name,
address = it.address,
)
}
Komapperは、こうなっていて、メソッドチェーンでtoList()するよりも終端処理がされていることが分かりやすい
たしか、ScalaのORMのScalikeJDBCもそうなってたな…。
全体的に
いろいろ丁度いい(雑w)
しかし、使用者が少ないので、問題が起きたら、自分でエラーを解決するか、開発者本人に聞く必要がある。なので、もっとみんな使って欲しい…。
Discussion