COMMANDクエリ

コマンドを利用するクエリ

概要

COMMANDクエリはTEMPLATEクエリをベースにしています。

コマンドは、SQLテンプレートとパラメータを1つの単位として扱う機能です。クラスにorg.komapper.annotation.KomapperCommandアノテーションを付けてSQLテンプレートを定義し、クラスのプロパティにパラメータを定義します。SQLの結果をどのように処理するかは、特定のクラスを継承することで表現されます。

COMMANDクエリを使用するには、Gradleビルドスクリプトに次の依存関係宣言を含める必要があります。

val komapperVersion: String by project
dependencies {
    implementation("org.komapper:komapper-template:$komapperVersion")
}

コマンド

下記は、複数件を取得するコマンドの例です。

@KomapperCommand("""
    select * from ADDRESS where street = /*street*/'test'
""")
class ListAddresses(val street: String): Many<Address>({ selectAsAddress() })

コマンドを定義しビルドを実行すると、QueryDslexecuteという名前の拡張関数が生成されます。したがって、上記のコマンドは次のようにして実行できます。

val query: Query<List<Address>> = QueryDsl.execute(ListAddresses("STREET 10"))

コマンドは、fromTemplateexecuteTemplateを使う方法に比べて、コンパイル時にSQLテンプレートを検証できるという利点があります。具体的には次のことが可能です。

  • SQLテンプレートの構文上の誤りを検出できます。例えば、/*%end*/が不足している場合コンパイルエラーとなります。
  • 利用されていないパラメータを検出できます。利用されていないパラメータが見つかった場合は警告メッセージを出力します。なお、パラメータにorg.komapper.annotation.KomapperUnusedアノテーションを付与することで警告を抑制できます。
  • パラメータの型やメンバを検証できます。例えば、String型のnameがパラメータの場合、SQLテンプレート上で/* name.unknown */のように存在しないメンバにアクセスしようとするとコンパイルエラーとなります。

コマンドには5つの種類があります。

  • One
  • Many
  • Exec
  • ExecReturnOne
  • ExecReturnMany

One

1件を取得するコマンドはorg.komapper.core.Oneを継承します。

@KomapperCommand("""
    select * from ADDRESS where address_id = /*id*/0
""")
class GetAddressById(val id: Int): One<Address?>({ selectAsAddress().singleOrNull() })

Oneの型パラメータには取得したい値の型を指定します。Oneのコンストラクタには、検索結果を処理するラムダ式を渡します。ラムダ式の中でできることは、fromTemplateで言及したselect関数やselectAsEntity関数と同じです。

上述の例では、Addressクラスに@KomapperProjetionが付与されていることを前提にしています。そのため、selectAsAddress関数を使って結果をAddressクラスに変換しています。

Many

複数件を取得するコマンドはorg.komapper.core.Manyを継承します。

@KomapperCommand("""
    select * from ADDRESS where street = /*street*/'test'
""")
class ListAddresses(val street: String): Many<Address>({ selectAsAddress() })

Manyの型パラメータには取得したい1件を表現する型を指定します。Manyのコンストラクタには、検索結果を処理するラムダ式を渡します。ラムダ式の中でできることは、fromTemplateで言及したselect関数やselectAsEntity関数と同じです。

上述の例では、Addressクラスに@KomapperProjetionが付与されていることを前提にしています。そのため、selectAsAddress関数を使って結果をAddressクラスに変換しています。

Exec

更新系のDMLを実行するコマンドはorg.komapper.core.Execを継承します。

@KomapperCommand("""
    update ADDRESS set street = /*street*/'' where address_id = /*id*/0
""")
class UpdateAddress(val id: Int, val street: String): Exec()

ExecReturnOne

更新系のDMLを実行し1件を返すコマンドはorg.komapper.core.ExecReturnOneを継承します。

@KomapperCommand("""
    insert into ADDRESS
        (address_id, street, version)
    values
        (/* id */0, /* street */'', /* version */1)
    returning address_id, street, version
""")
class InsertAddressThenReturn(val id: Int, val street: String): ExecReturnOne<Address>({ selectAsAddress().single() })

ExecReturnOneの型パラメータやコンストラクタについては、Oneと同様です。

ExecReturnMany

更新系のDMLを実行し複数件を返すコマンドはorg.komapper.core.ExecReturnManyを継承します。

@KomapperCommand("""
    update ADDRESS set street = /*street*/'' returning address_id, street, version
""")
class UpdateAddressThenReturn(val id: Int, val street: String): ExecReturnMany<Address>({ selectAsAddress() })

ExecReturnManyの型パラメータやコンストラクタについては、Manyと同様です。

パーシャル

パーシャルはSQLテンプレートの部品を再利用するための機能です。 パーシャルはorg.komapper.annotation.KomapperPartialアノテーションを注釈したクラスとして定義します。

@KomapperPartial(
    """
    limit /* limit */0 offset /* offset */0
    """,
)
data class Pagination(val limit: Int, val offset: Int)

パーシャルを使用するには、パーシャルをパラメータとして受け取るコマンドを定義し、そのパラメータをSQLテンプレート内で/*> partialName */のように参照します。

@KomapperCommand(
    """
    select * from address order by address_id
    /*> pagination */
    """,
)
class UsePartial(val pagination: Pagination?) : Many<Address>({ selectAsAddress() })

パーシャルのパラメータがnullの場合、パーシャルのSQLはコマンドのSQLには含まれません。

シールドされたサブクラスとしてのパーシャル

パーシャルクラスをシールドされたサブクラスとして表現することができます。 その場合、パーシャルなSQLはポリモーフィックに決定されます。

sealed interface FilterBy {
    @KomapperPartial(
        """
        where street = /* street */''
        """,
    )
    class Street(val street: String) : FilterBy

    @KomapperPartial(
        """
        where address_id = /* id */0
        """,
    )
    class Id(val id: Int) : FilterBy
}

@KomapperCommand(
    """
    select * from address
    /*> filterBy */
    """,
)
class UseSealedPartial(val filterBy: FilterBy?) : Many<Address>({ selectAsAddress() })

上記の例では、FilterBy.StreetFilterBy.Idのどちらのインスタンスが UseSealedPartialクラスのfilterByパラメータに渡されるかによって、 コマンドのSQLに取り込まれるパーシャルなSQLが変わります。

最終更新 September 16, 2024: Fix a typo (2f7f363)