商城首页欢迎来到中国正版软件门户

您的位置:首页 >Golang实现数据库软删除方法

Golang实现数据库软删除方法

  发布于2026-04-16 阅读(0)

扫一扫,手机访问

应使用 deleted_at(time.Time 类型),因 GORM 官方软删除机制依赖 DeletedAt 字段名和 time.Time 类型,is_deleted 会绕过自动过滤、更新及 Unscoped() 等核心行为。

golang如何实现数据库软删除_golang数据库软删除实现方法

软删除字段该用 deleted_at 还是 is_deleted

Go 生态中,gorm 官方软删除约定是 deleted_at*time.Time 类型),不是布尔字段。用 is_deleted 会绕过 GORM 的自动软删除机制,导致 FindUpdateDelete 行为不一致,且无法利用 Unscoped() 恢复逻辑。

必须满足两个条件才能启用 GORM 软删除:

  • struct 中定义字段名恰好为 DeletedAt(首字母大写)且类型为 *time.Time
  • 结构体标签里显式声明 gorm:"index"(非必需但强烈建议,否则 WHERE deleted_at IS NULL 查询无索引)

示例:

type User struct {
    ID        uint      `gorm:"primaryKey"`
    Name      string    `gorm:"not null"`
    DeletedAt *time.Time `gorm:"index"`
}

GORM 的 Delete 为什么没删数据?

调用 db.Delete(&user) 实际执行的是 UPDATE SET deleted_at = NOW(),不是 DELETE FROM。这是软删除的默认行为,无需额外配置。

但要注意几个典型陷阱:

  • 如果 user 是零值(比如刚 new 出来没赋 ID),GORM 会报 invalid argument 错误 —— 必须传入带有效主键的实例
  • 若想物理删除(跳过软删除),必须显式调用 Unscoped().Delete()
  • db.Where("name = ?", "foo").Delete(&User{}) 会批量软删除所有匹配记录,但不会触发单条记录的 BeforeDelete 钩子

查询时如何自动过滤已软删除的记录?

GORM 默认所有查询(FindFirstWhere 等)都会自动加上 WHERE deleted_at IS NULL 条件 —— 前提是模型含 DeletedAt 字段且未调用 Unscoped()

常见误操作:

  • 手动写 Where("deleted_at IS NULL"):多余,且可能和 GORM 自动添加的条件冲突(尤其在复杂嵌套查询中)
  • Raw() 执行原生 SQL:完全绕过软删除逻辑,查到已软删除的数据 —— 若需安全查询,务必自己补 AND deleted_at IS NULL
  • 关联查询(Preload)默认也受软删除影响,但若主表用了 Unscoped(),关联表仍走默认过滤,不会“传染”

需要恢复软删除数据时,别直接改 deleted_at

最安全的方式是调用 Unscoped().Model(&user).Update("deleted_at", nil),而不是 db.Model(&user).Update("deleted_at", nil) —— 后者会被 GORM 当作普通字段更新,但因软删除机制存在,Update 默认仍加 WHERE deleted_at IS NULL,结果是找不到记录,更新失败。

另外注意:

  • nil 是唯一能“恢复”的值,设成任意时间(包括过去时间)都算已删除
  • 恢复操作本身不会触发 AfterDeleteBeforeDelete 钩子,因为这不是一次删除行为
  • 如果业务要求“恢复时校验状态”,得在 Update 前手动查一遍 deleted_at != nil

软删除真正难的不是实现,而是团队对“何时该 Unscoped、何时不该、哪些接口暴露了 raw 查询”缺乏统一约束。字段命名、钩子遗漏、索引缺失、测试覆盖不足——这些地方出问题,比写错一行 Delete 更难排查。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注