INSERTクエリ

追加のためのクエリ

概要

INSERTクエリはQueryDslinsertとそれに続く関数を呼び出して構築します。

クエリ実行時にキーが重複した場合、org.komapper.core.UniqueConstraintExceptionがスローされます。

single

エンティティ1件を追加するにはsingleを呼び出します。

val address: Address = Address(16, "STREET 16", 0)
val query: Query<Address> = QueryDsl.insert(a).single(address)
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?)
*/

このクエリを実行した場合の戻り値は追加されたデータを表す新しいエンティティです。

下記のマッピング定義に応じて、発行されるSQLにも新しいエンティティにも適切な値が反映されます。

  • @KomapperAutoIncrement
  • @KomapperSequence
  • @KomapperVersion
  • @KomapperCreatedAt
  • @KomapperUpdatedAt

multiple

1文でエンティティ複数件を追加するにはmultipleを呼び出します。

val query: Query<List<Address>> = QueryDsl.insert(a).multiple(
    Address(16, "STREET 16", 0),
    Address(17, "STREET 17", 0),
    Address(18, "STREET 18", 0)
)
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?), (?, ?, ?), (?, ?, ?)
*/

このクエリを実行した場合の戻り値は追加されたデータを表す新しいエンティティのリストです。

下記のマッピング定義に応じて、発行されるSQLにも新しいエンティティにも適切な値が反映されます。

  • @KomapperAutoIncrement
  • @KomapperSequence
  • @KomapperVersion
  • @KomapperCreatedAt
  • @KomapperUpdatedAt

batch

バッチでエンティティ複数件を追加するにはbatchを呼び出します。

val query: Query<List<Address>> = QueryDsl.insert(a).batch(
    Address(16, "STREET 16", 0),
    Address(17, "STREET 17", 0),
    Address(18, "STREET 18", 0)
)
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?)
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?)
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?)
*/

このクエリを実行した場合の戻り値は追加されたデータを表す新しいエンティティのリストです。

下記のマッピング定義に応じて、発行されるSQLにも新しいエンティティにも適切な値が反映されます。

  • @KomapperAutoIncrement
  • @KomapperSequence
  • @KomapperVersion
  • @KomapperCreatedAt
  • @KomapperUpdatedAt

onDuplicateKeyIgnore

onDuplicateKeyIgnoreを呼び出すことでキーが重複した場合のエラーを無視できます。 onDuplicateKeyIgnoreには重複チェック対象のキーを指定できます。指定されない場合は主キーが使われます。

val address: Address = ..
val query: Query<Address?> = QueryDsl.insert(a).onDuplicateKeyIgnore().executeAndGet(address)

上記クエリに対応するSQLはどのDialectを使うかで異なります。 例えば、MariaDBのDialectを使う場合は次のようなSQLになります。

insert ignore into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?)

PostgreSQLのDialectを使う場合は次のようなSQLになります。

insert into ADDRESS as t0_ (ADDRESS_ID, STREET, VERSION) values (?, ?, ?) on conflict (ADDRESS_ID) do nothing

executeAndGet

onDuplicateKeyIgnoreに続けてexecuteAndGetを呼び出した場合、戻り値は追加されたデータを表すエンティティです。 キーが重複していた場合はnullが戻ります。

single

onDuplicateKeyIgnoreに続けてsingleを呼び出した場合の戻り値はドライバの返す値です。

multiple

onDuplicateKeyIgnoreに続けてmultipleを呼び出した場合の戻り値はドライバの返す値です。

batch

onDuplicateKeyIgnoreに続けてbatchを呼び出した場合の戻り値はドライバの返す値です。

onDuplicateKeyUpdate

onDuplicateKeyUpdateを呼び出すことでキーが重複した場合に対象行を更新できます。 onDuplicateKeyUpdateには重複チェック対象のキーを指定できます。指定されない場合は主キーが使われます。

val department: Department = ..
val query: Query<Address> = QueryDsl.insert(d).onDuplicateKeyUpdate().executeAndGet(department)

上記クエリに対応するSQLはどのDialectを使うかで異なります。 例えば、MariaDBのDialectを使う場合は次のようなSQLになります。

insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) values (?, ?, ?, ?, ?) on duplicate key update DEPARTMENT_NO = values(DEPARTMENT_NO), DEPARTMENT_NAME = values(DEPARTMENT_NAME), LOCATION = values(LOCATION), VERSION = values(VERSION)

PostgreSQLのDialectを使う場合は次のようなSQLになります。

insert into DEPARTMENT as t0_ (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) values (?, ?, ?, ?, ?) on conflict (DEPARTMENT_ID) do update set DEPARTMENT_NO = excluded.DEPARTMENT_NO, DEPARTMENT_NAME = excluded.DEPARTMENT_NAME, LOCATION = excluded.LOCATION, VERSION = excluded.VERSION

executeAndGet

onDuplicateKeyUpdateに続けてexecuteAndGetを呼び出した場合、戻り値は追加もしくは更新されたデータを表すエンティティです。

single

onDuplicateKeyUpdateに続けてsingleを呼び出した場合の戻り値はドライバの返す値です。

multiple

onDuplicateKeyUpdateに続けてmultipleを呼び出した場合の戻り値はドライバの返す値です。

batch

onDuplicateKeyUpdateに続けてbatchを呼び出した場合の戻り値はドライバの返す値です。

set

onDuplicateKeyUpdateの呼び出し後、setを使って更新対象のカラムに特定の値を設定できます。

val department: Department = ..
val query = QueryDsl.insert(d).onDuplicateKeyUpdate().set { excluded ->
    d.departmentName eq "PLANNING2"
    d.location eq concat(d.location, concat("_", excluded.location))
}.single(department)

set関数に渡すラムダ式にはexcludedパラメータがあります。 excludedは、追加しようとしているエンティティのメタモデルを表します。 したがって、excludedの利用により追加しようとしているデータに基づいた更新を実現できます。

PostgreSQLのDialectを使う場合、上記のクエリは次のようなSQLとして発行されます。

insert into DEPARTMENT as t0_ (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) values (?, ?, ?, ?, ?) on conflict (DEPARTMENT_ID) do update set DEPARTMENT_NAME = ?, LOCATION = (concat(t0_.LOCATION, (concat(?, excluded.LOCATION))))

MySQLのDialectを使う場合、上記のクエリは次のようなSQLとして発行されます。

insert into DEPARTMENT (DEPARTMENT_ID, DEPARTMENT_NO, DEPARTMENT_NAME, LOCATION, VERSION) values (?, ?, ?, ?, ?) as excluded on duplicate key update DEPARTMENT_NAME = ?, LOCATION = (concat(DEPARTMENT.LOCATION, (concat(?, excluded.LOCATION))))

where

onDuplicateKeyUpdateの呼び出し後、whereを使って更新条件を設定できます。

val department: Department = ..
val query = QueryDsl.insert(d).onDuplicateKeyUpdate().where {
    d.departmentName eq "PLANNING"
}.single(department)

dangerouslyOnDuplicateKeyIgnore

この関数は、任意のconflict_targetを指定できることを除いて、onDuplicateKeyIgnoreと同等です。

dangerouslyOnDuplicateKeyUpdate

この関数は、任意のconflict_targetを指定できることを除いて、onDuplicateKeyUpdateと同等です。

values

プロパティごとの値を設定して1件を追加するにはvalues関数にラムダ式を渡します。

ラムダ式の中ではeq関数を使って値を設定できます。

val query: Query<Pair<Long, Int?>> = QueryDsl.insert(a).values {
  a.addressId eq 19
  a.street eq "STREET 16"
  a.version eq 0
}
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?)
*/

eqIfNotNullを使って値がnullでない場合にのみ値を設定することもできます。

val query: Query<Pair<Long, Int?>> = QueryDsl.insert(a).values {
    a.addressId eq 19
    a.street eqIfNotNull street
    a.version eq 0
}

クエリを実行した場合の戻り値は追加された件数と生成されたIDのPairです。 IDはマッピング定義に@KomapperAutoIncrement@KomapperSequenceが注釈されている場合にのみ返されます。

以下のマッピング定義を持つプロパティについて明示的にeqを呼び出さない場合、発行されるSQLに自動で値が設定されます。 明示的にeqを呼び出した場合は明示した値が優先されます。

  • @KomapperSequence
  • @KomapperVersion
  • @KomapperCreatedAt
  • @KomapperUpdatedAt

@KomapperAutoIncrementの定義は常に有効です。明示的に値をeqした場合、無視されます。

select

検索結果を追加するにはselectを呼び出します。

val aa = Meta.address.clone(table = "ADDRESS_ARCHIVE")
val query: Query<Long, List<Int>> = QueryDsl.insert(aa).select {
  QueryDsl.from(a).where { a.addressId between 1..5 }
}
/*
insert into ADDRESS_ARCHIVE (ADDRESS_ID, STREET, VERSION) select t1_.ADDRESS_ID, t1_.STREET, t1_.VERSION from ADDRESS as t1_ where t1_.ADDRESS_ID between ? and ?
*/

クエリを実行した場合の戻り値は追加された件数と生成されるIDのリストのPairです。 IDはエンティティクラスのマッピング定義に@KomapperAutoIncrementが注釈されている場合にのみ生成されます。

以下のマッピング定義は考慮されません。

  • @KomapperSequence
  • @KomapperVersion
  • @KomapperCreatedAt
  • @KomapperUpdatedAt

returning

以下の関数の後続でreturning関数を呼び出すことで、 追加された値や更新された値を取得できます。

  • single
  • multiple
  • values

single関数の後続でreturning関数を呼び出す例です。

val address: Address = Address(16, "STREET 16", 0)
val query: Query<Address> = QueryDsl.insert(a).single(address).returning()
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?) returning ADDRESS_ID, STREET, VERSION
*/

returning関数にプロパティを指定することで取得対象のカラムを限定できます。

val query: Query<Int?> = QueryDsl.insert(a).single(address).returning(a.addressId)
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?) returning ADDRESS_ID
*/
val query: Query<Pair<Int?, String?>> = QueryDsl.insert(a).single(address).returning(a.addressId, a.street)
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?) returning ADDRESS_ID, STREET
*/
val query: Query<Triple<Int?, String?, Int?>> = QueryDsl.insert(a).single(address).returning(a.addressId, a.street, a.version)
/*
insert into ADDRESS (ADDRESS_ID, STREET, VERSION) values (?, ?, ?) returning ADDRESS_ID, STREET, VERSION
*/

returning関数は、onDuplicateKeyIgnore関数やonDuplicateKeyUpdate関数と組み合わせて使用することもできます。

val departments = listOf(
    Department(5, 50, "PLANNING", 1),
    Department(1, 60, "DEVELOPMENT", 1),
)
val query: Query<List<Department>> = QueryDsl.insert(d).onDuplicateKeyUpdate().multiple(departments).returning()
/*
insert into department as t0_ (department_id, department_no, department_name, version) 
values (?, ?, ?, ?), (?, ?, ?, ?) on conflict (department_id)
do update set department_no = excluded.department_no, department_name = excluded.department_name, version = excluded.version 
returning department_id, department_no, department_name, version
*/

options

クエリの挙動をカスタマイズするにはoptionsを呼び出します。 ラムダ式のパラメータはデフォルトのオプションを表します。 変更したいプロパティを指定してcopyメソッドを呼び出してください。

val address: Address = Address(16, "STREET 16", 0)
val query: Query<Address> = QueryDsl.insert(a).single(address).options {
    it.copy(
      queryTimeoutSeconds = 5
    )
}

指定可能なオプションには以下のものがあります。

batchSize
バッチサイズです。デフォルトはnullです。
disableSequenceAssignment
IDにシーケンスで生成した値をアサインすることを無効化するかどうかです。デフォルトはfalseです。
queryTimeoutSeconds
クエリタイムアウトの秒数です。デフォルトはnullでドライバの値を使うことを示します。
returnGeneratedKeys
AUTO INCREMENTされたIDの値を返すかどうかです。デフォルトはtrueです。
suppressLogging
SQLのログ出力を抑制するかどうかです。デフォルトはfalseです。

executionOptions の同名プロパティよりもこちらに明示的に設定した値が優先的に利用されます。

最終更新 November 3, 2023: Support excluded tables for MariaDB (b78bd35)