恢复软删除数据需用Unscoped().Model().Update("deleted_at", nil),因Save()不更新DeletedAt字段;DeletedAt必须为*time.Time;关联数据需手动恢复。

恢复被软删除的数据,本质是把 DeletedAt 字段设为 nil,而不是调用 Save() 或 Delete() —— 这两个方法在默认行为下都无效。
为什么 db.Save(&user) 无法恢复软删除记录
因为 GORM 把 DeletedAt 当作受控字段,Save() 不会更新它,哪怕你手动设置了 user.DeletedAt = nil。GORM 会直接忽略这个赋值,SQL 中根本不会出现 SET deleted_at = NULL。
- 错误写法:
user.DeletedAt = nil; db.Save(&user)→ 无效果,deleted_at保持原值 - 正确逻辑:必须显式指定字段名和值,且绕过软删除的查询过滤层
- 关键前提:模型中
DeletedAt必须是*time.Time(指针类型),否则nil无法被正确识别或写入
恢复单条记录的标准写法:用 Unscoped().Model().Update()
这是最稳妥、最明确的方式,适用于绝大多数场景。
- 先确保查到的是软删除状态的记录(可选但推荐):
db.Unscoped().First(&user, 123),检查user.DeletedAt != nil - 执行恢复:
db.Unscoped().Model(&User{}).Where("id = ?", 123).Update("deleted_at", nil) - 注意顺序:
Unscoped()必须放在Where()之前,否则条件可能匹配不到已软删除的行(因为默认查询会加WHERE deleted_at IS NULL) - 第二个参数必须是
nil,不是time.Time{}或空字符串,否则会写入零值时间,仍被视为已删除
批量恢复或使用 Restore() 方法的注意事项
GORM v2 提供了 Restore() 方法,但它有隐含依赖,容易踩坑。
-
db.Unscoped().Model(&User{}).Where("deleted_at IS NOT NULL").Restore()看似简洁,但要求结构体中必须定义为DeletedAt gorm.DeletedAt类型(而非仅*time.Time),否则会静默失败 - 批量恢复时,建议加索引:
gorm:"index"在DeletedAt字段上,否则WHERE deleted_at IS NOT NULL可能全表扫描,大表下超时或锁表 - 如果表已有数据但后来才加
DeletedAt字段,必须跑一次AutoMigrate(),否则 GORM 不识别该字段为软删除标记,Restore()和Update("deleted_at", nil)都可能不生效
最容易被忽略的点:关联数据不会自动恢复
GORM 软删除不级联,恢复主记录时,它的 has many 关联(比如订单下的订单项)仍处于软删除状态,业务上可能表现为“数据不一致”。
- 没有自动机制,必须手动处理:
db.Unscoped().Model(&Item{}).Where("order_id = ? AND deleted_at IS NOT NULL", orderID).Update("deleted_at", nil) - 若用
Preload()加载关联,即使主记录已恢复,预加载结果仍为空——因为Preload默认也走软删除过滤,需对关联模型单独加Unscoped() - 恢复操作应尽量包裹在事务中,尤其是涉及主从关系时,避免部分成功导致状态错乱


















