gorm软删除需结构体含time.time类型的deletedat字段,否则delete()执行硬删除;默认查询自动排除软删除记录,恢复需unscoped().update("deleted_at", nil)。

软删除在 GORM 中默认通过 DeletedAt 字段实现,但必须满足两个前提:结构体中定义了该字段,且使用了 GORM 提供的 gorm.Model 或手动嵌入 gorm.DeletedAt —— 否则调用 Delete 会变成硬删除。
为什么 Delete() 没触发软删除?
最常见原因是结构体没包含 DeletedAt 字段,或类型不对(比如用了 int64 而非 time.Time)。GORM 只有在检测到字段名是 DeletedAt 且类型为 time.Time(或实现了 scanner/valuer 的时间兼容类型)时,才启用软删除逻辑。
-
DeletedAt必须是time.Time类型,不能是*time.Time(nil 值会被忽略,导致无法恢复) - 若自定义结构体,需显式嵌入:
gorm.Model(含ID,CreatedAt,UpdatedAt,DeletedAt)或单独声明DeletedAt time.Time - 使用
db.Unscoped().Delete()会跳过软删除,直接物理删除 —— 这是设计行为,不是 bug
如何正确查询未被软删除的记录?
GORM 默认所有查询(Find, First, Where 等)自动添加 WHERE deleted_at IS NULL 条件。这意味着你不需要手动过滤,但也要注意:一旦用了 Unscoped(),这个过滤就消失了。
- 正常查询:自动排除
DeletedAt != nil的记录 - 查包括已软删除的:必须加
Unscoped(),例如db.Unscoped().Where("name = ?", "foo").First(&u) - 只查已软删除的:用
Unscoped().Where("deleted_at IS NOT NULL"),注意字段名大小写要和数据库一致(GORM 默认 snake_case,即deleted_at)
如何恢复软删除的记录?
恢复本质是把 DeletedAt 设为 nil。GORM 不提供 Undelete 方法,需手动更新:
db.Unscoped().Where("id = ?", 123).Update("deleted_at", nil)
注意点:
- 必须用
Unscoped(),否则WHERE deleted_at IS NULL会让条件不匹配 -
Update第二个参数必须是nil(不是time.Time{}),否则会写入零值时间,仍被视为已删除 - 如果想批量恢复,同样适用
Unscoped().Model(&User{}).Where(...).Update("deleted_at", nil)
软删除不是银弹:它让数据“看似消失”,但实际仍占空间、影响索引体积、可能拖慢大表查询;更隐蔽的问题是,Count 默认也不统计软删除记录,而业务有时需要“总注册数”这类含已删数据的指标 —— 这时候就得主动 Unscoped().Count(),别等线上对不上数才想起这茬。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











