title | emoji | type | topics | published | ||||
---|---|---|---|---|---|---|---|---|
Komapperで遊んでみる |
🍣 |
tech |
|
true |
※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
※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
https://github.com/komapper/komapper/blob/20a2bdc967d054af8c78638d1544e2f59712c211/komapper-core/src/main/kotlin/org/komapper/core/dsl/builder/RelationDeleteStatementBuilder.kt#L26-L30
ここで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)
}
https://github.com/komapper/komapper/blob/00d719b18bf8891317cb78c4af385b7ab584c773/DESIGN_DOC.md#loosely-coupled-architecture にあるとおり、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: The dialect is not found for the JDBC url. Try to add the 'komapper-dialect-mysql-jdbc' dependency. driver='mysql'
at org.komapper.jdbc.JdbcDialects.get(JdbcDialects.kt:24)
https://github.com/komapper/komapper/blob/bc289886313a85146b1c371d1869710e1115b634/komapper-jdbc/src/main/kotlin/org/komapper/jdbc/JdbcDialects.kt#L19-L25 ここで、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()
}
}
例えば、こういうのとか…
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なのは…
SQLAlchemy のjoined loadingと似た感じで使える。 joinしてincludeAll()メソッドを呼べば紐付いた状態で返ってくる
開発者がSeasar2やDomaといったJavaのフレームワークを作っていた日本人でもある
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の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) しかし、使用者が少ないので、問題が起きたら、自分でエラーを解決するか、開発者本人に聞く必要がある。なので、もっとみんな使って欲しい…。