5月28日 02:13

什么是 GORM,它的核心特性有哪些?

GORM 是什么

GORM 是 Go 语言中应用最广泛的 ORM 库,基于反射机制将结构体映射为数据库表,将方法调用转换为 SQL 语句。它遵循约定优于配置的原则——结构体名的蛇形复数即为表名,ID 字段默认为主键,CreatedAt / UpdatedAt 自动管理时间戳。

核心特性

  • 关联关系:支持 Has One、Has Many、Belongs To、Many To Many 四种关联,通过 Preload 预加载或 Joins 联表查询获取关联数据
  • 钩子机制:在 Create / Update / Delete / Find 前后可注册回调,例如 BeforeCreate 中做字段默认值填充,AfterDelete 中清理关联资源
  • 事务支持db.Transaction(func(tx *gorm.DB) error { ... }) 提供闭包式事务,返回 error 自动回滚,返回 nil 自动提交
  • 自动迁移db.AutoMigrate(&User{}) 根据结构体定义同步表结构(新增列、索引),但不会删除已有列
  • 链式调用db.Where(...).Order(...).Limit(...).Find(&results) 风格的查询构建器,中间态可复用

基本 CRUD 示例

go
type User struct { gorm.Model // 内置 ID、CreatedAt、UpdatedAt、DeletedAt Name string Email string `gorm:"type:varchar(100);uniqueIndex"` Age int } // 创建 db.Create(&User{Name: "Alice", Email: "alice@test.com", Age: 28}) // 查询 var user User db.First(&user, 1) // 按主键 db.Where("age > ?", 20).Find(&users) // 条件查询 // 更新 db.Model(&user).Update("age", 29) // 单字段 db.Model(&user).Updates(map[string]interface{}{"age": 29, "name": "Alice W"}) // 多字段 // 删除(软删除,DeletedAt 非空) db.Delete(&user) db.Unscoped().Delete(&user) // 硬删除

面试高频追问

Q1:GORM 的软删除是如何实现的?如何查询被软删除的记录?

GORM 在模型中嵌入 gorm.Model 后会包含 DeletedAt 字段(类型为 gorm.DeletedAt)。调用 Delete 时 GORM 将 DeletedAt 设为当前时间而非执行 DELETE。所有查询自动追加 WHERE deleted_at IS NULL。使用 db.Unscoped() 可跳过此条件查询到已删除记录,Unscoped().Delete() 则执行硬删除。

Q2:N+1 查询问题是什么?GORM 如何解决?

查询主表 N 条记录后,遍历每条记录单独查询关联表,产生 1 + N 次 SQL。GORM 通过 Preload("Orders") 在一次查询中批量加载关联数据(生成两条 SQL:一条查主表,一条用 WHERE id IN (...) 查关联),也可用 Joins("Orders") 生成单条 JOIN SQL。Preload 适合一对多场景,Joins 适合过滤关联条件的场景。

Q3:GORM 钩子的执行顺序是什么?

以 Create 为例:BeforeSaveBeforeCreate → 执行插入 → AfterCreateAfterSave。如果 BeforeSaveBeforeCreate 返回 error,整个流程中断。注意:批量操作(如 CreateInBatches)中钩子对每条记录单独触发。

Q4:GORM 的事务有几种用法?

三种:

  1. 闭包事务:db.Transaction(func(tx *gorm.DB) error { ... }),最推荐,返回 error 自动回滚
  2. 手动事务:db.Begin()tx.Commit() / tx.Rollback(),需要自行处理 panic
  3. 嵌套事务:通过 SavePoint / RollbackTo 实现,适用于需要部分回滚的场景

Q5:GORM 的 First 和 Find 有什么区别?

First 查询一条记录(追加 LIMIT 1),记录不存在时返回 ErrRecordNotFoundFind 查询多条记录,记录不存在时不报错,只返回空切片。如果只需要一条数据,用 First 更明确。

GORM 的局限与注意事项

  • 反射开销:基于反射的字段映射在高频写入场景下有性能损耗,极端场景可考虑 sqlxsqlc
  • 复杂 SQL 受限:窗口函数、CTE 等复杂查询需要手写原生 SQL(db.Raw()
  • 自动迁移只增不删AutoMigrate 不会删除列或修改列类型,生产环境应使用专业迁移工具
  • 软删除陷阱Unique 约束与软删除冲突——软删除的记录仍占唯一索引位,需用复合唯一索引或 WHERE 条件索引
标签:Gorm