软删除(也称为”paranoid”模式)允许你标记记录为已删除,而不是真正从数据库中移除。系统不会执行 DELETE 语句,而是将记录的 deletedAt 列设置为当前时间戳。
适用场景:
要在模型上启用软删除,只需添加 deletedAt 属性:
import { Bone, DataTypes } from 'leoric';
class Post extends Bone {
static attributes = {
id: { type: DataTypes.BIGINT, primaryKey: true },
title: DataTypes.STRING,
deletedAt: DataTypes.DATE, // 启用软删除
}
}
import { Bone, Column } from 'leoric';
class Post extends Bone {
@Column({ primaryKey: true })
id: bigint;
@Column()
title: string;
@Column()
deletedAt: Date; // 启用软删除
}
如果你没有显式定义属性,而是让 Leoric 从数据库表结构推断,当表中存在 deleted_at 列时会自动启用软删除。
启用软删除后,调用 .remove() 会更新 deletedAt 列而非删除行:
const post = await Post.findOne({ id: 1 });
await post.remove();
// SQL: UPDATE posts SET deleted_at = '2026-03-26 00:00:00' WHERE id = 1
静态方法:
await Post.remove({ id: 1 });
// SQL: UPDATE posts SET deleted_at = '2026-03-26 00:00:00' WHERE id = 1
默认情况下,所有查询会自动排除已软删除的记录:
const posts = await Post.find();
// SQL: SELECT * FROM posts WHERE deleted_at IS NULL
const post = await Post.findOne({ id: 1 });
// SQL: SELECT * FROM posts WHERE id = 1 AND deleted_at IS NULL LIMIT 1
要从数据库中永久删除记录,传入 true 给 .remove():
// 实例方法
const post = await Post.findOne({ id: 1 });
await post.remove(true);
// SQL: DELETE FROM posts WHERE id = 1
// 静态方法
await Post.remove({ id: 1 }, true);
// SQL: DELETE FROM posts WHERE id = 1
unscoped使用 .unscoped 在查询中包含已软删除的记录:
const allPosts = await Post.unscoped.find();
// SQL: SELECT * FROM posts(不添加 WHERE deleted_at IS NULL 过滤)
paranoid: false也可以在特定查询中传入 paranoid: false:
await Post.update({ title: '已更新' }, { where: { id: 1 }, paranoid: false });
// 先通过 unscoped 找到已软删除的记录
const post = await Post.findOne({ id: 1 }).unparanoid;
await post.restore();
// SQL: UPDATE posts SET deleted_at = NULL WHERE id = 1 AND deleted_at IS NOT NULL
await Post.restore({ id: 1 });
// SQL: UPDATE posts SET deleted_at = NULL WHERE id = 1 AND deleted_at IS NOT NULL
注意:如果模型未启用软删除(即没有
deletedAt属性),restore()会抛出错误。
启用软删除的模型,其关联也会遵守 deletedAt 作用域。通过 include() 或 with() 加载关联记录时,已软删除的关联记录会被自动过滤。
class Post extends Bone {
static initialize() {
this.hasMany('comments');
}
}
class Comment extends Bone {
static attributes = {
deletedAt: DataTypes.DATE,
}
}
const post = await Post.findOne({ id: 1 }).with('comments');
// deletedAt 非空的评论会被排除
软删除与 Leoric 的自动时间戳管理协同工作。当记录被软删除时:
deletedAt 设置为当前日期/时间updatedAt 在软删除操作期间不会自动更新当记录被恢复时:
deletedAt 设置为 null