Version v1.0-RC1 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(e).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
}
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
プロパティを持ちます。
@KomapperEntity(aliases = ["addr"])
data class Address(
...
)
aliases
については aliases を参照ください。
@KomapperEntityDef
エンティティ定義クラスであることを表します。
entity
プロパティやaliases
プロパティを指定できます。
@KomapperEntityDef(entity = Address::class, aliases = ["addr"])
data class AddressDef(
...
)
aliases
については aliases を参照ください。
@KomapperTable
エンティティクラスとマッピングするテーブルの名前を明示的に指定します。
@KomapperEntityDef(Address::class)
@KomapperTable("ADDRESS", schema = "ACCOUNT", alwaysQuote = true)
data class AddressDef(
...
)
catalog
プロパティやschema
プロパティにはテーブルが属するカタログやスキーマの名前を指定できます。
alwaysQuote
プロパティにtrue
を設定すると生成されるSQLの識別子が引用符で囲まれます。
このアノテーションでテーブルの名前を指定しない場合、アノテーション処理のkomapper.namingStrategy
オプションに従って名前が解決されます。
アノテーションプロセッシングのオプションも参照ください。
プロパティに付与するアノテーション一覧
ここで説明するアノテーションは全てorg.komapper.annotation
パッケージに属します。
@KomapperId
プライマリーキーであることを表します。 エンティティクラスのマッピングを行う上でこのアノテーションの存在は必須です。
@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.LocalDateTime
- java.time.OffsetDateTime
- 上述の型をプロパティとして持つValue Class
@KomapperUpdatedAt
更新時のタイムスタンプであることを表します。
このアノテーションを付与すると、 Insertクエリと Updateクエリにてタイムスタンプがプロパティに設定されます。
このアノテーションを付与するプロパティの型は次のいずれかでなければいけません。
- java.time.LocalDateTime
- java.time.OffsetDateTime
- 上述の型をプロパティとして持つValue Class
@KomapperColumn
プロパティとマッピングするカラムの名前を明示的に指定します。
@KomapperColumn(name = "ADDRESS_ID", alwaysQuote = true, masking = true)
val id: Nothing
alwaysQuote
プロパティにtrue
を設定すると生成されるSQLの識別子が引用符で囲まれます。
masking
プロパティにtrue
を設定すると、 ログの中で対応するデータがマスキングされます。
このアノテーションでカラムの名前を指定しない場合、アノテーション処理のkomapper.namingStrategy
オプションに従って名前が解決されます。
アノテーションプロセッシングのオプションも参照ください。
@KomapperIgnore
マッピングの対象外であることを表します。