Version v3.1 of the documentation is no longer actively maintained. The site that you are currently viewing is an archived snapshot. For up-to-date documentation, see the latest version.
エンティティクラス
概要
Komapperでは、データベースのテーブルに対応するKotlinクラスをエンティティクラスと呼びます。
エンティティクラスをテーブルにマッピングさせるには別途アノテーションを用いたマッピング定義が必要です。
マッピング定義はコンパイル時に解析されその結果がメタモデルとなります。 メタモデルはクエリの構築や実行で利用されます。
エンティティクラスの定義
エンティティクラスは次の要件を満たさなければいけません。
- Data Classである
- 可視性がprivateでない
- 型パラメータを持っていない
例えば、次のようなテーブル定義があるとします。
create table if not exists ADDRESS (
ADDRESS_ID integer not null auto_increment,
STREET varchar(500) not null,
VERSION integer not null,
CREATED_AT timestamp,
UPDATED_AT timestamp,
constraint pk_ADDRESS primary key(ADDRESS_ID)
);
上記のテーブル定義に対応するエンティティクラス定義は次のようになります。
data class Address(
val id: Int = 0,
val street: String,
val version: Int = 0,
val createdAt: LocalDateTime? = null,
val updatedAt: LocalDateTime? = null,
)
プロパティとカラムの間における型の対応関係については データ型 を参照ください。
マッピング定義
マッピング定義の作成方法は2種類あります。
- エンティティクラス自身がマッピング定義を持つ方法(セルフマッピング)
- エンティティクラスとは別にエンティティ定義クラスを作成する方法(分離マッピング)
同一のエンティティクラスに対して1つの方法のみ適用できます。
セルフマッピング
このときエンティティクラスは前のセクションで説明した要件に加えて次の条件を満たさなければいけません。
@KomapperEntity
で注釈される
例えば、前のセクションで示したAddress
クラスにこの方法を適用すると次のように変更できます。
@KomapperEntity
data class Address(
@KomapperId
@KomapperAutoIncrement
@KomapperColumn(name = "ADDRESS_ID")
val id: Int = 0,
val street: String,
@KomapperVersion
val version: Int = 0,
@KomapperCreatedAt
val createdAt: LocalDateTime? = null,
@KomapperUpdatedAt
val updatedAt: LocalDateTime? = null,
)
分離マッピング
エンティティ定義クラスは次の要件を満たさなければいけません。
- Data Classである
- 可視性がprivateでない
- 型パラメータを持っていない
@KomapperEntityDef
で注釈され引数でエンティティクラスを受け取る- エンティティクラスに定義されたプロパティと異なる名前のプロパティを持たない
例えば、前のセクションで示したAddress
クラスに対するエンティティ定義クラスは次のように記述できます。
@KomapperEntityDef(Address::class)
data class AddressDef(
@KomapperId
@KomapperAutoIncrement
@KomapperColumn(name = "ADDRESS_ID")
val id: Nothing,
@KomapperVersion
val version: Nothing,
@KomapperCreatedAt
val createdAt: Nothing,
@KomapperUpdatedAt
val updatedAt: Nothing,
)
エンティティ定義クラスは、参照するエンティティクラスに定義された同名のプロパティに対し様々な設定ができます。
定義されないプロパティに対してはデフォルトのマッピング定義が適用されます。
上記の例ではエンティティクラスに登場するstreet
プロパティがエンティティ定義クラスには登場しませんが、
street
プロパティにはテーブル上のSTREET
カラムにマッピングされます。
エンティティ定義クラスのプロパティの型に制約はありません。上記の例ではNothing
を使っています。
メタモデル
マッピング定義からはorg.komapper.core.dsl.metamodel.EntityMetamodel
のインターフェースを実装する形でメタモデルが生成されます。
生成されたメタモデルはorg.komapper.core.dsl.Meta
オブジェクトの拡張プロパティとなります。
アプリケーションではこの拡張プロパティを使ってクエリを組み立てられます。
// get a generated metamodel
val a = Meta.address
// define a query
val query = QueryDsl.from(a).where { a.street eq "STREET 101" }.orderBy(a.id)
aliases
上述の例では拡張プロパティの名前はaddress
ですが、これは@KomapperEntity
や@KomapperEntityDef
のaliases
プロパティで変更できます。
@KomapperEntity(aliases = ["addr"])
data class Address(
...
)
aliases
プロパティには複数の名前を指定できます。
その際、名前ごとに異なるインスタンスとして公開されます。
複数の異なるインスタンスが必要となる主なユースケースは自己結合やサブクエリです。
@KomapperEntity(aliases = ["employee", "manager"])
data class Employee(
...
)
例えば、マネージャーの一覧を取得するには上記のように複数の名前をつけた上で以下のようなクエリを作ります。
val e = Meta.employee
val m = Meta.manager
val query: Query<List<Employee>> = QueryDsl.from(m)
.distinct()
.innerJoin(e) {
m.employeeId eq e.managerId
}
なお、事前に名前を持ったメタモデルを定義しない場合でも、clone
関数を使えば同じことが実現可能です。
val e = Meta.employee
val m = e.clone()
val query: Query<List<Employee>> = QueryDsl.from(m)
.distinct()
.innerJoin(e) {
m.employeeId eq e.managerId
}
unit
上述の例ではaddress
の拡張プロパティを持つのはorg.komapper.core.dsl.Meta
ですが、
このオブジェクトは@KomapperEntity
や@KomapperEntityDef
のunit
プロパティで変更できます。
object MyMeta
@KomapperEntity(unit = MyMeta::class)
data class Address(
...
)
上のように定義するとunit
プロパティに指定したオブジェクトがaddress
の拡張プロパティを持ちます。
// get a generated metamodel
val a = MyMeta.address
// define a query
val query = QueryDsl.from(a).where { a.street eq "STREET 101" }.orderBy(a.id)
clone
clone
関数を使って既存のメタモデルを基に別のメタモデルを生成できます。
主なユースケースは、データ構造が同じで名前だけが異なるテーブルにデータをコピーする場合です。
val a = Meta.address
val archive = a.clone(table = "ADDRESS_ARCHIVE")
val query = QueryDsl.insert(archive).select {
QueryDsl.from(a).where { a.id between 1..5 }
}
cloneしたメタモデルを他のメタモデルと同様に公開したい場合は、
オブジェクトでインスタンスを保持した上でMeta
オブジェクトの拡張プロパティを定義してください。
object MetamodelHolder {
private val _addressArchive = Meta.address.clone(table = "ADDRESS_ARCHIVE")
val Meta.addressArchive get() = _addressArchive
}
define
define
関数を使ってメタモデルに対しデフォルトのWHERE句を定義できます。
あるメタモデルを使う際に必ず同じ検索条件を用いたいケースで便利です。
object MetamodelHolder {
private val _bostonOnly = Meta.department.define { d ->
where {
d.location eq "BOSTON"
}
}
val Meta.bostonOnly get() = _bostonOnly
}
上記のbostonOnly
メタモデルを利用すると、
クエリで検索条件を指定しないにも関わらずWHERE句をもったSQLが生成されます。
val d = Meta.bostonOnly
val query = QueryDsl.from(d)
/*
select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT as t0_ where t0_.LOCATION = ?
*/
WHERE句を持つクエリを組み立てた場合は検索条件がAND演算子で連結されます。
val d = Meta.bostonOnly
val query = QueryDsl.from(d).where { d.departmentNo greaterEq 0 }
/*
select t0_.DEPARTMENT_ID, t0_.DEPARTMENT_NO, t0_.DEPARTMENT_NAME, t0_.LOCATION, t0_.VERSION from DEPARTMENT as t0_ where t0_.LOCATION = ? and t0_.DEPARTMENT_NO >= ?
*/
defineしたメタモデルを結合先としてクエリに含めた場合もこの機能は有効です。
val e = Meta.employee
val d = Meta.bostonOnly
val query = QueryDsl.from(e).innerJoin(d) {
e.departmentId eq d.departmentId
}
/*
select t0_.EMPLOYEE_ID, t0_.EMPLOYEE_NO, t0_.EMPLOYEE_NAME, t0_.MANAGER_ID, t0_.HIREDATE, t0_.SALARY, t0_.DEPARTMENT_ID, t0_.ADDRESS_ID, t0_.VERSION from EMPLOYEE as t0_ inner join DEPARTMENT as t1_ on (t0_.DEPARTMENT_ID = t1_.DEPARTMENT_ID) where t1_.LOCATION = ?
*/
SELECT文だけでなくUPDATE文やDELETE文でも有効です。
val d = Meta.bostonOnly
val query = QueryDsl.delete(d).all()
/*
delete from DEPARTMENT as t0_ where t0_.LOCATION = ?
*/
デフォルトのWHERE句にパラメータを渡したい場合は、拡張関数として定義することもできます。 ただし、メタモデルが毎回異なるインスタンスとなることは注意してください。
object MetamodelHolder {
fun Meta.locationSpecificDepartment(value: String) = Meta.department.define { d ->
where {
d.location eq value
}
}
}
上記の拡張関数を呼び出す例です。
val d = Meta.locationSpecificDepartment("NEW YORK")
val query = QueryDsl.from(d)
val list = db.runQuery { query }
クラスに付与するアノテーション一覧
ここで説明するアノテーションは全てorg.komapper.annotation
パッケージに属します。
@KomapperEntity
エンティティクラスがマッピング定義を持つことを表します。 aliasesプロパティとunitプロパティを持ちます。
@KomapperEntity(aliases = ["addr"])
data class Address(
...
)
@KomapperEntityDef
エンティティ定義クラスであることを表します。
entity
プロパティやaliasesプロパティやunitプロパティを指定できます。
@KomapperEntityDef(entity = Address::class, aliases = ["addr"])
data class AddressDef(
...
)
@KomapperTable
エンティティクラスとマッピングするテーブルの名前を明示的に指定します。
@KomapperEntityDef(Address::class)
@KomapperTable("ADDRESS", schema = "ACCOUNT", alwaysQuote = true)
data class AddressDef(
...
)
catalog
プロパティやschema
プロパティにはテーブルが属するカタログやスキーマの名前を指定できます。
alwaysQuote
プロパティにtrue
を設定すると生成されるSQLの識別子が引用符で囲まれます。
このアノテーションでテーブルの名前を指定しない場合、アノテーション処理のkomapper.namingStrategy
オプションに従って名前が解決されます。
アノテーションプロセッシングのオプションも参照ください。
@KomapperProjection
クエリの結果をエンティティへ射影することを有効にします。
@KomapperEntity
@KomapperProjection
data class Address(
...
)
function
プロパティには生成される拡張関数の名前を指定できます。
指定しない場合、selectAsAddress
のようにselectAs + エンティティクラスの単純名
となります。
拡張関数はSelectQuery
とTemplateSelectQueryBuilder
に対して生成されます。
SelectQuery
の拡張関数は次のように利用できます。
val e = Meta.employee
val query: Query<List<Address>> = QueryDsl.from(e)
.where { e.employeeName startsWith "S" }
.selectAsAddress(
version = e.version,
addressId = e.employeeId,
street = concat(e.employeeName, " STREET"),
)
TemplateSelectQueryBuilder
の拡張関数は次のように利用できます。
val sql = "select address_id, street, version from address order by address_id"
val query: Query<List<Address>> = QueryDsl.fromTemplate(sql).selectAsAddress()
このアノテーションを利用せずに全エンティティクラスについて射影を有効にしたい場合、
アノテーション処理のkomapper.enableEntityProjection
オプションを利用できます。
アノテーションプロセッシングのオプションも参照ください。
プロパティに付与するアノテーション一覧
ここで説明するアノテーションは全てorg.komapper.annotation
パッケージに属します。
@KomapperId
プライマリーキーであることを表します。 複合プライマリキーを表すために、 1つのエンティティクラス内に複数指定することもできます。
virtual
プロパティにtrue
が設定された場合、
SCHEMAクエリは注釈されたプロパティを主キーだと見做しません。
@KomapperId(virtual = true)
val id: Int
@KomapperSequence
プライマリキーがデータベースのシーケンスで生成されることを表します。
必ず@KomapperId
と一緒に付与する必要があります。
このアノテーションを付与するプロパティの型は次のいずれかでなければいけません。
- Int
- Long
- UInt
- 上述の型をプロパティとして持つValue Class
@KomapperId
@KomapperSequence(name = "ADDRESS_SEQ", startWith = 1, incrementBy = 100)
val id: Int
name
プロパティにはシーケンスの名前を指定しなければいけません。カタログやスキーマの指定もできます。
startWith
プロパティとincrementBy
プロパティの値はシーケンス定義に合わせなければいけません。
alwaysQuote
プロパティにtrue
を設定すると生成されるSQLの識別子が引用符で囲まれます。
@KomapperAutoIncrement
プライマリーキーがデータベースの自動インクリメント機能で生成されることを表します。
必ず@KomapperId
と一緒に付与する必要があります。
このアノテーションを付与するプロパティの型は次のいずれかでなければいけません。
- Int
- Long
- UInt
- 上述の型をプロパティとして持つValue Class
@KomapperVersion
楽観的排他制御に使われるバージョン番号であることを表します。
このアノテーションを付与すると、 Updateクエリや Deleteクエリで楽観的排他制御が行われます。
このアノテーションを付与するプロパティの型は次のいずれかでなければいけません。
- Int
- Long
- UInt
- 上述の型をプロパティとして持つValue Class
@KomapperCreatedAt
生成時のタイムスタンプであることを表します。
このアノテーションを付与すると、 Insertクエリにてタイムスタンプがプロパティに設定されます。
このアノテーションを付与するプロパティの型は次のいずれかでなければいけません。
- java.time.Instant
- java.time.LocalDateTime
- java.time.OffsetDateTime
- kotlinx.datetime.Instant
- kotlinx.datetime.LocalDateTime
- 上述の型をプロパティとして持つValue Class
Note
kotlinx-datetimeのデータ型を使うには、 kotlinx-datetimeのサポート を参照ください。@KomapperUpdatedAt
更新時のタイムスタンプであることを表します。
このアノテーションを付与すると、 Insertクエリと Updateクエリにてタイムスタンプがプロパティに設定されます。
このアノテーションを付与するプロパティの型は次のいずれかでなければいけません。
- java.time.Instant
- java.time.LocalDateTime
- java.time.OffsetDateTime
- kotlinx.datetime.Instant
- kotlinx.datetime.LocalDateTime
- 上述の型をプロパティとして持つValue Class
Note
kotlinx-datetimeのデータ型を使うには、 kotlinx-datetimeのサポート を参照ください。@KomapperEnum
Enum型のプロパティに対し、プロパティとカラムのマッピング方法を明示的に指定します。
@KomapperEnum(EnumType.ORDINAL)
val color: Color // このcolorプロパティがColorというEnum型に対応すると想定してください
@KomapperEnum
のtype
プロパティには次のいずれかを指定できます。
- EnumType.NAME
- Enumクラスの
name
プロパティを文字列型のカラムにマッピングする。 - EnumType.ORDINAL
- Enumクラスの
ordinal
プロパティを整数型のカラムにマッピングする。 - EnumType.PROPERTY
- Enumクラスの任意のプロパティをカラムにマッピングする。
マッピング対象のプロパティ名は
@KomapperEnum
のhint
プロパティに指定する必要がある。 - EnumType.TYPE
- Enumクラスをenum型のカラムにマッピングする。 Enumクラスに対応する ユーザー定義のデータ型 が必要であることに注意してほしい。
enum class Color(val code: String) { RED("r"), GREEN("g"), BLUE("b") }
@KomapperEntity
data class Box(
@KomapperId
val id: Int,
@KomapperEnum(EnumType.NAME)
val top: Color,
@KomapperEnum(EnumType.ORDINAL)
val bottom: Color,
@KomapperEnum(EnumType.PROPERTY, hint = "code")
val side: Color
)
Enum型のプロパティに対して@KomapperEnum
を指定しない場合、
アノテーション処理のkomapper.enumStrategy
オプションに従ってマッピング方法が解決されます。
アノテーションプロセッシングのオプションも参照ください。
@KomapperColumn
プロパティとマッピングするカラムの名前を明示的に指定します。
@KomapperColumn(name = "ADDRESS_ID", alwaysQuote = true, masking = true)
val id: Nothing
alwaysQuote
プロパティにtrue
を設定すると生成されるSQLの識別子が引用符で囲まれます。
masking
プロパティにtrue
を設定すると、 ログの中で対応するデータがマスキングされます。
alternateType
プロパティを利用すると、マッピングするSQLの型を変更できます。
詳細は、Alternate typeを参照ください。
このアノテーションでカラムの名前を指定しない場合、アノテーション処理のkomapper.namingStrategy
オプションに従って名前が解決されます。
アノテーションプロセッシングのオプションも参照ください。
@KomapperIgnore
マッピングの対象外であることを表します。
@KomapperEmbeddedId
複合プライマリキーの組込バリューを表します。
data class EmoloyeeId(val id1: Int, val id2: String)
@KomapperEntity
data class Employee(@KomapperEmbeddedId val id: EmoloyeeId, val name: String)
virtual
プロパティにtrue
が設定された場合、
SCHEMAクエリは注釈されたプロパティを複合主キーだと見做しません。
data class EmoloyeeId(val id1: Int, val id2: String)
@KomapperEntity
data class Employee(@KomapperEmbeddedId(virtual = true) val id: EmoloyeeId, val name: String)
@KomapperEmbedded
組込バリューを表します。
data class Money(val amount: BigDecimal, val currency: String)
@KomapperEntity
data class Employee(@KomapperId val id: Int, @KomapperEmbedded val salary: Money)
@KomapperEnumOverride
組込バリュー内のEnum型のプロパティに対し、
@KomapperEnum
を適用します。
enum class Currency { JPY, USD }
data class Money(val amount: BigDecimal, val currency: Currency)
@KomapperEntity
data class Employee(
@KomapperId
val id: Int,
@KomapperEmbedded
@KomapperEnumOverrde("currency", KomapperEnum(EnumType.ORDINAL))
val salary: Money
)
@KomapperColumnOverride
組込バリュー内のプロパティに対し、
@KomapperColumn
を適用します。
data class Money(val amount: BigDecimal, val currency: String)
@KomapperEntity
data class Employee(
@KomapperId
val id: Int,
@KomapperEmbedded
@KomapperColumnOverrde("amount", KomapperColumn("SALARY_AMOUNT"))
@KomapperColumnOverrde("currency", KomapperColumn("SALARY_CURRENCY"))
val salary: Money
)