0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ktormに実装されていないSQL関数を自分で作成する

Posted at

Ktormについて

最近の仕事ではSpring Batchを使ったバッチ処理をKotlinで書いています。
mysqlのDBにSQLを投げる際にORマッパーをどうするか調査した結果、Kotlin製のOR
マッパーであるKtormを使用しています。

KtormはSQL部分をDSLで見通しよく書けるので、結構満足して使っています。

例)

SQL

select
  user_id,
  sum(amount)
from
  reports
where
  created_at >= '2020/05/01'
group by
  user_id

上記のようなSQLだと全てDSLで書くことができ、テーブルのカラム名もクラスに定義され(今回の例ではReportDef)
カラムの内容の変更があればエンティティ部分とその使用箇所を修正すればいいし、修正漏れがあればコンパイルエラーになってくれます。

sql部分
database.from(ReportDef)
.select(
    ReportDef.userId,
    sum(ReportDef.amount)
).where {
    ReportDef.createdAt greaterEq date
}.groupBy(ReportDef.userId)
Report.kt
interface Report : Entity<Report> {
    companion object : Entity.Factory<Report>()

   var id: Long
   var userId: Long
   var amount: Int
   var purchaseType: String

}
object ReportDef : Table<Report>("reports") {
   val id by long("id").primaryKey().bindTo { it.id }
   val userId by long("user_id").bindTo { it.userId }
   val amount by long("amount").bindTo { it.amount }
   val purchaseType by varchar("purchase_type").bindTo { it.purchaseType }
}

ハマったケース

今回 購入方法がCASHとかCARDのように複数存在し、'CASH'のみの使用回数を知る必要がありました。
SQLだとselect部分にcount(purchase_type='CASH')を追加する必要がありました。

count(purchase_type)だとKtormではcount(ReportDef.purchaseMethod)で書けるのですが、
count(purchase_type='CASH')はさすがに素のKtormでは書けません。

基本的にORマッパーが提供している機能で必要なSQLを書ければ良いのですが、
未提供の場合は最悪のケースで文字列の中に素のSQLを書くことになり、
なんとか避けたいなぁと思っていました。

調査結果

色々調べたところ、Ktorm自体もmysqlで使用するSQL関数を拡張しているロジック
があり、これを参考に拡張関数のcountifメソッドを実装することでKtormのDSLで書くことができました。

sql部分
database.from(ReportDef)
.select(
    ReportDef.userId,
    sum(ReportDef.amount),
    ReportDef.purchaseMethod.countif('CASH') // 追加部分
).where {
    ReportDef.createdAt greaterEq date
}.groupBy(ReportDef.userId)
DatabaseMethodExtension.kt

fun <T : Any> ColumnDeclaring<T>.countIf(right: String): FunctionExpression<T> {
    val rightExpression = ArgumentExpression(right, VarcharSqlType)
    val equal = BinaryExpression(BinaryExpressionType.EQUAL, asExpression(), rightExpression.asExpression(), BooleanSqlType)
    return FunctionExpression(
            functionName = "sum",
            arguments = listOf(equal),
            sqlType = sqlType)
}

やっていることをざっくり説明すると以下になります。

  • SQLでのsum関数を使用するFunctionExpressionを返す拡張メソッドを用意する
  • 引数から渡された文字列を表すArgumentExpressionを用意する
  • =を表すBinaryExpressionを用意し、右辺側に先ほどのArgumentExpressionを渡す
  • =を表すBinaryExpressionをFunctionExpressionのarguments引数に渡す

まとめ

  • KtormのDSLはSQL関数をどうしても使う必要があるケースでもメソッドを実装することで対応可能。
  • Kotlinの拡張関数は便利

参考URL

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?