gorm软删除必须使用名为deletedat且类型为gorm.deletedat(*time.time)的字段,否则delete操作将变为硬删或报错;默认查询自动过滤已软删数据,需unscoped()绕过。

软删除字段必须用 gorm.DeletedAt 类型
GORM 的软删除机制是“约定大于配置”,它只识别名为 DeletedAt 且类型为 gorm.DeletedAt(即 *time.Time)的字段。如果你自定义字段名(如 IsDeleted 或 deleted_at),GORM 默认不会将其视为软删除字段,Delete 操作会变成硬删,或者直接报错。
正确写法:
type User struct {
ID uint `gorm:"primaryKey"`
Name string
DeletedAt gorm.DeletedAt `gorm:"index"` // 必须叫 DeletedAt,且类型为 gorm.DeletedAt
}
常见错误:
- 用
bool IsDeleted字段 —— GORM 完全无视,Delete()仍执行物理删除 - 用
time.Time DeletedAt(非指针)—— GORM 无法区分“未删除”和“删除时间为零值”,导致查询异常 - 漏加
gorm:"index"—— 软删除后查询变慢,尤其数据量大时
Delete() 不带条件会触发软删除,但要注意事务和钩子
调用 db.Delete(&user) 时,GORM 自动将 DeletedAt 设为当前时间,而非执行 DELETE FROM。但这仅在模型含合法 DeletedAt 字段且未禁用软删除时生效。
实操要点:
- 确保没全局禁用:检查是否调用了
db.Unscoped()或设置了gorm.Config.SkipDefaultTransaction = true干扰行为 - 软删除会触发
BeforeDelete和AfterDelete钩子,但钩子内收到的是“即将软删”的对象,DeletedAt还是零值;真正赋值发生在钩子之后 - 若需在软删前校验(如禁止删除管理员),应在钩子中
return errors.New("forbidden"),GORM 会中止操作 - 事务中软删除失败(如唯一约束冲突)会导致整个事务回滚,但
DeletedAt已被设值的问题不会自动还原 —— 需手动处理或避免在事务中混用软删与强约束
查询时默认过滤已软删除数据,绕过要用 Unscoped()
GORM 在所有 Find、First、Where 等查询方法中,默认自动添加 WHERE deleted_at IS NULL 条件。这是透明的,也是最易被忽略的“隐形过滤”。
典型场景与对策:
- 查全部(含已软删):必须显式调用
db.Unscoped().Find(&users),否则永远看不到软删记录 - 只查已软删的:用
db.Unscoped().Where("deleted_at IS NOT NULL").Find(&users),不能省略Unscoped() - 关联查询(如
Preload)也受软删除影响:预加载的关联表若含DeletedAt,同样被过滤;需对关联表单独Unscoped() -
Count()方法默认也不统计软删数据,要总数得写db.Unscoped().Model(&User{}).Count(&n)
硬删、恢复、批量操作需手动控制,没有“一键还原”
GORM 不提供 Restore() 方法,恢复软删除记录就是把 DeletedAt 设为 nil;硬删则必须用 Unscoped().Delete()。这两者都容易误操作。
安全建议:
- 恢复操作示例:
db.Model(&user).Update("deleted_at", nil)—— 注意不是UpdateColumn,否则跳过钩子 - 批量恢复需慎用:
db.Unscoped().Where("deleted_at IS NOT NULL").Update("deleted_at", nil),务必加WHERE条件,否则清空全部记录的DeletedAt - 硬删前强烈建议先日志记录:
db.Unscoped().Delete(&user); log.Printf("hard delete user %d", user.ID) - 别依赖
SELECT * FROM ... WHERE deleted_at IS NULL手动写 SQL 绕过 GORM —— 会丢失钩子、关联预加载、字段映射等能力,维护成本陡增
软删除不是银弹。时间戳精度、时区、归档策略、索引膨胀、审计合规性,这些都得在 DeletedAt 字段之外另行设计。GORM 只负责那行 UPDATE ... SET deleted_at = NOW(),剩下的,得你来填坑。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











