软删除必须使用*time.time类型的deletedat字段,gorm依赖其nil状态自动过滤;恢复需unscoped()配合where和update("deletedat", nil);自定义字段名需手动处理;preload默认不加载已删关联记录。

软删除必须加 DeletedAt 字段,且类型只能是 *time.Time
Go 的 GORM 默认用 DeletedAt 实现软删除,不是随便起个 is_deleted 就行。它依赖这个字段的 nil 状态自动过滤已删记录——nil 表示“没删”,非-nil 才算“已删”。如果用 bool 或 int,GORM 根本不认,Find、First 全部绕过软删除逻辑,等于白设。
实操建议:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
DeletedAt字段必须是*time.Time类型,不能是time.Time(值类型会导致零值被当成已删除) - 建表时别手动加该字段,让 GORM 自动迁移:GORM 会为它加索引并识别为软删除标记
- 如果已有表没这个字段,加完后要跑
AutoMigrate,否则查询仍走物理删除路径
GORM 的 Unscoped() 不是万能恢复开关,得配 Where 用
很多人以为调 Unscoped().Where("id = ?", id).Update("DeletedAt", nil) 就能恢复,结果发现没生效——因为 Unscoped() 只是“查的时候不加 DeletedAt IS NULL 条件”,但 Update 默认仍跳过软删除字段。真正恢复得显式写清目标字段。
实操建议:
- 恢复操作必须用
Unscoped().Where(...).Update("DeletedAt", nil),漏掉Unscoped()会查不到记录;漏掉Where会全表恢复 - 别用
Save()恢复:它会把整个 struct 写回去,可能误覆盖其他字段,且不触发软删除逻辑 - 如果用了复合主键或非标准主键,
Where条件必须完整匹配,否则Unscoped()也救不了
自定义软删除字段名?可以,但得关默认逻辑再手动处理
想用 IsDeleted 或 deleted 这种字段名?GORM 不支持直接配置别名替代 DeletedAt。强行改名只会让软删除失效,所有查询照常返回已删数据。
实操建议:
- 如果必须用布尔字段,就放弃 GORM 内置软删除,改用普通字段 + 手动
Where("is_deleted = ?", false) - 恢复时用
Model(&u).Where("id = ?", id).Update("is_deleted", false),不加Unscoped() - 注意:这种方案下,
Count、关联预加载、事务内查询都得自己补Where,漏一处就数据错乱
软删除影响关联查询,Preload 默认不加载已删关联记录
比如用户有多个订单,订单表也启用了软删除。用 Preload("Orders") 查用户时,即使订单只是逻辑删除,也不会出现在 Orders 切片里——GORM 的 Preload 默认带软删除过滤。
实操建议:
- 要查出包括已删订单的完整列表,得写
Preload("Orders", func(db *gorm.DB) *gorm.DB { return db.Unscoped() }) - 但别全局开
Unscoped:会影响其他业务逻辑,比如管理员后台和普通用户接口应隔离软删除行为 - 如果关联表没启用软删除,而主表启用了,
Preload仍受主表Unscoped影响——这点容易被忽略
Unscoped、还有预加载场景下默认过滤逻辑的隐式生效。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










