Exposed で Entity インスタンスを apply したときに 変更のあるプロパティだけ更新される件を確認する。
環境はこれ:
$ kotlin -version
Kotlin version 2.1.0-release-394 (JRE 17.0.10+7-LTS)
まず、 name, hp をプロパティを持つ Pokemon オブジェクトを考えます。 これを DAO で扱うために、テーブル、DAO(Entity) を定義します。
テーブル:
object PokemonsTable: IntIdTable("pokemons") {
val name = varchar("name", length = 128)
val hp = integer("hp") // hit point
}
DAO(Entity):
class PokemonDAO(id: EntityID<Int>): IntEntity(id) {
companion object: IntEntityClass<PokemonDAO>(PokemonsTable)
var name by PokemonsTable.name
var hp by PokemonsTable.hp
}
先ほど定義した Tables / DAO を使ってアプリケーションを書いていきます。
データベースとの接続を最初につくります。
Database.connect(
url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
driver = "org.h2.Driver",
user = "root",
password = "",
)
それからテーブルを用意:
SchemaUtils.drop(PokemonsTable)
SchemaUtils.create(PokemonsTable)
それでは最初の操作は「テーブルにポケモンをひとつ追加」です。
PokemonDAO.new {
name = "Pikachu"
hp = 120
}
ただし、これらのデータベース処理は transaction ブロックで実行する必要があるので、 全体では結局次のようになります。
transaction {
// 0)
addLogger(StdOutSqlLogger)
// 1)
SchemaUtils.drop(PokemonsTable)
SchemaUtils.create(PokemonsTable)
// 2)
PokemonDAO.new {
name = "Pikachu"
hp = 120
}
}
処理内容がわかるように addLogger(StdOutSqlLogger) を入れています。 これで内部で発行される SQL を標準出力として見ることができます。
実行してみます。
$ kotlin pokemons.main.kts
SQL: SELECT SETTING_VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE SETTING_NAME = 'MODE'
SQL: DROP TABLE IF EXISTS POKEMONS
SQL: CREATE TABLE IF NOT EXISTS POKEMONS (ID INT AUTO_INCREMENT PRIMARY KEY, "name" VARCHAR(128) NOT NULL, HP INT NOT NULL)
SQL: INSERT INTO POKEMONS (HP, "name") VALUES (120, 'Pikachu')
ここでは一つのポケモンしか存在しないことが自明なので、次のようにして取得します。
val pokemon: PokemonDAO? = PokemonDAO.all().firstOrNull()
println(pokemon?.name) // => Pikachu
ここで、この pokemon を apply を使って更新します。
pokemon?.apply {
name = "Charamander"
hp = 110
}
これで実行してみると 該当部分の SQL は次のように発行されます。
SQL: UPDATE POKEMONS SET HP=110, "name"='Charamander' WHERE ID = 1
まあこれは当然の振る舞いです。
次に、更新するときに name は "Pikachu" のままにしてみます。
pokemon?.apply {
name = "Pikachu"
hp = 110
}
これを実行すると次のようになります。
SQL: UPDATE POKEMONS SET HP=110 WHERE ID = 1
へー。 どうやら既存の値と同じ場合は該当フィールド(ここでは name)の更新は起きないようです。
今回書いたコードを掲載します。
// pokemons.main.kts
@file:Repository("https://repo1.maven.org/maven2/")
@file:DependsOn("org.jetbrains.exposed:exposed-dao:0.56.0")
@file:DependsOn("org.jetbrains.exposed:exposed-core:0.56.0")
@file:DependsOn("org.jetbrains.exposed:exposed-jdbc:0.56.0")
@file:DependsOn("com.h2database:h2:2.3.232")
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.StdOutSqlLogger
import org.jetbrains.exposed.sql.SchemaUtils
object PokemonsTable: IntIdTable("pokemons") {
val name = varchar("name", length = 128)
val hp = integer("hp") // hit point
}
class PokemonDAO(id: EntityID<Int>): IntEntity(id) {
companion object: IntEntityClass<PokemonDAO>(PokemonsTable)
var name by PokemonsTable.name
var hp by PokemonsTable.hp
}
Database.connect(
url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
driver = "org.h2.Driver",
user = "root",
password = "",
)
transaction {
// 0)
addLogger(StdOutSqlLogger)
// 1)
SchemaUtils.drop(PokemonsTable)
SchemaUtils.create(PokemonsTable)
// 2)
PokemonDAO.new {
name = "Pikachu"
hp = 120
}
// 3)
val pokemon: PokemonDAO? = PokemonDAO.all().firstOrNull()
println(pokemon?.name)
// 4)
pokemon?.apply {
//name = "Charamander"
name = "Pikachu"
hp = 110
}
}
以上です。