gorm 有哪些常用特性?软删除、钩子等有哪些坑?
一句话结论:gorm 是 Go 最常用的 ORM:结构体即表模型,支持链式查询、Preload 预加载、Transaction 事务、AutoMigrate、钩子与软删除。常见坑集中在:共用 DB 实例导致查询条件累积、软删除与唯一索引冲突、Updates 用结构体时不更新零值、软删数据默认查不到、钩子里误改字段等。
常用特性速览
| 特性 | 说明 |
|---|---|
| 模型 | 结构体标签 gorm:"column:name;index;unique" 映射表 |
| 预加载 | Preload("Orders") / Joins 减少 N+1 查询 |
| 事务 | db.Transaction(func(tx) error {...}),返回 error 自动回滚 |
| 软删除 | 字段 gorm.DeletedAt:删除变 UPDATE,查询自动带 deleted_at IS NULL |
| 钩子 | BeforeSave/BeforeCreate/AfterCreate/BeforeUpdate/AfterFind 等 |
| 迁移 | AutoMigrate 建表与加列(不做删列等复杂变更) |
| 链式查询 | Where/Order/Limit 通常只在当次生效(复用 DB 除外) |
高频坑与对策
- 软删除加唯一索引:删除后同 key 再插入会撞唯一索引——把 deleted_at 一起放进唯一索引(组合唯一)。
- 查不到“已删”数据:默认查询排除软删行,要查用 Unscoped(),彻底删除也是 Unscoped().Delete。
- Updates 结构体跳过零值:想置 0/空要用 map 或 Select 指定列。
- 复用 db 实例条件累积:一次 Where 后再次使用同一 db 会带着旧条件,用 db.Session(&gorm.Session{}) 取全新会话。
- First 查不到返回 ErrRecordNotFound,要显式处理而不是一路忽略。
- 大批量插入逐条执行很慢:用 CreateInBatches 分批插入。
- 生产环境记得开慢 SQL 日志/Logger,方便定位 N+1 与慢查询。
type User struct {
ID uint `gorm:"primarykey"`
Name string `gorm:"uniqueIndex:uk_name_del"`
DeletedAt gorm.DeletedAt `gorm:"uniqueIndex:uk_name_del"` // 软删后允许重名
}
func biz(db *gorm.DB) error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&User{Name: "alice"}).Error; err != nil {
return err // 任一步失败整体回滚
}
return tx.Model(&User{}).Where("id = ?", 1).
Update("name", "bob").Error
})
}
常见追问 / 记忆点
- 追问:软删除是怎么实现的?——Delete 被钩子改写成 UPDATE deleted_at=now,查询层自动追加过滤条件。
- 追问:钩子里想改本次操作的数据怎么办?——在 BeforeXxx 中直接改传入的结构体字段即可生效。
- 记忆点:链式复用要 Session;软删记住唯一索引坑;Updates 零值不生效;生产开慢日志。