逻辑删除

本章描述 mutation API 中的逻辑删除行为。

声明逻辑删除列

在标记删除状态的 KPojo 字段上使用 @LogicDelete。该注解默认启用。

User.kt

import com.kotlinorm.annotations.Default
import com.kotlinorm.annotations.LogicDelete
import com.kotlinorm.annotations.PrimaryKey
import com.kotlinorm.interfaces.KPojo

data class User(
    @PrimaryKey(identity = true)
    var id: Int? = null,
    var name: String? = null,
    @LogicDelete
    @Default("0") // @Default("false") for Postgres
    var deleted: Boolean? = null
) : KPojo

应用中多个表使用同一个逻辑删除字段时,可以配置全局策略,并在需要启用的 KPojo 中保留该字段。

global config

import com.kotlinorm.Kronos
import com.kotlinorm.beans.config.KronosCommonStrategy
import com.kotlinorm.beans.dsl.Field

Kronos.logicDeleteStrategy = KronosCommonStrategy(enabled = true, field = Field("deleted"))

删除会写入删除标记

启用逻辑删除后,delete().execute() 生成写入删除标记的 UPDATE。生成条件也会排除已经删除的行。

kotlin

User(id = 1)
    .delete()
    .by { it.id }
    .execute()

Mysql

UPDATE `user`
SET `deleted` = :deletedNew
WHERE `id` = :id
  AND `deleted` = 0

params

id = 1
deletedNew = 1

当前删除需要生成物理 DELETE 时,调用 .logic(false)。

kotlin

User(id = 1)
    .delete()
    .logic(false)
    .by { it.id }
    .execute()

Mysql

DELETE
FROM `user`
WHERE `id` = :id

查询和更新会排除已删除行

对启用逻辑删除的 KPojo,select 和 update 会追加活动行条件。

kotlin

User()
    .select { [it.id, it.name] }
    .toMapList()

User(id = 1)
    .update()
    .set { it.name = "Kronos" }
    .by { it.id }
    .execute()

select

SELECT `id`, `name`
FROM `user`
WHERE `deleted` = 0

update

UPDATE `user`
SET `name` = :nameNew
WHERE `id` = :id
  AND `deleted` = 0

插入和 upsert 会维护标记

insert 会把逻辑删除字段初始化为活动值。按匹配字段执行 upsert 时,如果匹配到已逻辑删除的记录,Kronos 会更新这条记录并把逻辑删除字段恢复为活动值。策略启用时,onConflict() upsert 也会把该字段加入插入列,并在冲突更新时写回活动值。

kotlin

User(name = "Kronos")
    .insert()
    .execute()

Mysql

INSERT INTO `user` (`name`, `deleted`)
VALUES (:name, :deleted)

params

name = "Kronos"
deleted = 0

kotlin

User(id = 1, name = "Kronos")
    .upsert { it.name }
    .on { it.id }
    .execute()

update

UPDATE `user`
SET `name` = :nameNew,
    `deleted` = :deletedNew
WHERE `id` = :id

DataGuard 边界

逻辑删除会把 delete 转成 update。没有用户条件时,生成语句仍可能命中全部活动行。

kotlin

User()
    .delete()
    .execute()

Mysql

UPDATE `user`
SET `deleted` = :deletedNew
WHERE `deleted` = 0

需要拒绝全表写入时,启用 📚DataGuard。条件 API 见 📚删除 和 📚更新。