605 字
3 分钟
关联关系
GORM 关联关系
常见关系
| 关系 | 示例 | 外键通常位于 |
|---|---|---|
| Belongs To | Order belongs to User | orders.user_id |
| Has One | User has one Profile | profiles.user_id |
| Has Many | User has many Order | orders.user_id |
| Many To Many | User belongs to many Role | 中间表,如 user_roles |
模型示例
type User struct { ID uint Name string Profile Profile Orders []Order Roles []Role `gorm:"many2many:user_roles;"`}
type Profile struct { ID uint UserID uint Bio string}
type Order struct { ID uint UserID uint User User Amount int64}
type Role struct { ID uint Name string `gorm:"uniqueIndex"`}明确写出 UserID 和 User,比完全依赖推断更容易阅读和排查。
Preload 预加载
var users []Usererr := db.Preload("Profile"). Preload("Orders"). Where("status = ?", "active"). Find(&users).Error预加载通常会额外执行查询,避免了循环中逐条加载关联记录,但仍要关注返回数据量。可以限制预加载字段或条件:
db.Preload("Orders", func(tx *gorm.DB) *gorm.DB { return tx.Select("id", "user_id", "amount"). Where("amount > ?", 0). Order("id DESC")}).Find(&users)嵌套预加载:
db.Preload("Orders.Items.Product").Find(&users)嵌套层级越深,越需要确认查询数量、数据量和 API 返回结构。
Joins 与 Join Preload
需要按关联表字段筛选、排序或只返回扁平结果时,可以使用 Joins:
var orders []Orderdb.Joins("User"). Where("users.name = ?", "Alice"). Find(&orders)Preload 主要用于加载关联对象;Joins 主要用于把关联表加入当前 SQL。二者生成的结果结构和 SQL 形态不同,不能只按“哪个查询次数少”机械选择。
创建关联
user := User{ Name: "Alice", Profile: Profile{Bio: "Go developer"}, Orders: []Order{{Amount: 100}},}if err := db.Create(&user).Error; err != nil { return err}不希望自动保存某个关联时:
db.Omit("Profile").Create(&user)db.Omit("Roles").Create(&user)关联写入涉及多条 SQL,关键业务应放在显式事务中,不要只依赖调用方“通常会成功”。
Association Mode
需要单独管理关联时:
db.Model(&user).Association("Orders").Append(&order)db.Model(&user).Association("Orders").Delete(&order)count := db.Model(&user).Association("Orders").Count()使用前确认主键已经存在,且关联名称与模型字段一致。
关联删除策略
删除用户时,关联订单是级联删除、软删除、保留还是转移,需要由业务明确决定。数据库外键的 OnDelete 行为和 GORM 的 Select 删除行为不是同一层概念,迁移和测试必须覆盖。
常见坑
- 忘记
Preload,导致返回 JSON 中关联为空。 - 在循环中逐条查询关联,形成 N+1。
- Many2Many 中间表的唯一约束缺失,重复关联难以发现。
- 用
Preload加载巨大集合,导致内存和响应体爆炸。 - 关联字段和外键命名不一致却没有显式
foreignKey、references标签。
官方资料
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
最后更新于 ,距今已过 77 天
部分内容可能已过时