GORM 关联查询与预加载
本文介绍 GORM 中关联查询的三种核心方式:延迟加载、Preload 预加载和 Joins 连接查询。
关联查询方式
延迟加载(Lazy Loading)
访问关联字段时才执行查询,容易产生 N+1 问题。
Go
// 查询用户
var user User
db.First(&user, 1)
// 访问关联 Profile 时触发额外查询
db.Model(&user).Association("Profile").Find(&user.Profile)
每条主记录产生一次额外查询,数据量大时性能急剧下降。
Preload 预加载
一次性加载所有关联数据,彻底解决 N+1 问题。
Go
// 预加载单一关联
db.Preload("Profile").First(&user, 1)
// 预加载多个关联
db.Preload("Profile").Preload("Orders").First(&user, 1)
// 嵌套预加载
db.Preload("Orders.Items").First(&user, 1)
// 条件预加载
db.Preload("Orders", "status = ?", "paid").First(&user, 1)
语法格式:
Go
db.Preload("关联字段名", "条件?", 参数...).Find(&results)
Joins 连接查询
使用 SQL JOIN 一次性获取关联数据,适合简单关联场景。
Go
// 内连接
db.Joins("JOIN profiles ON profiles.user_id = users.id").Find(&users)
// 带条件连接
db.Joins("JOIN orders ON orders.user_id = users.id AND orders.status = ?", "paid").Find(&users)
// 多个 Join
db.Joins("Profile").Joins("Company").Find(&users)
Joins 方式要求关联字段必须有外键关系,且不支持嵌套预加载。
Preload vs Joins 对比
| 特性 | Preload | Joins |
|---|---|---|
| 查询方式 | 多次查询(IN) | 单次 JOIN 查询 |
| N+1 问题 | 完全避免 | 完全避免 |
| 嵌套关联 | 支持 | 不支持 |
| 性能 | 数据量大时更优 | 数据量小时更优 |
| 外键要求 | 不要求数据库外键 | 不要求数据库外键 |
| 适用场景 | 复杂关联、嵌套加载 | 简单关联、单次查询 |
注意事项
- 生产环境默认使用 Preload 解决 N+1 问题,而非延迟加载。
- 预加载条件中的参数使用位置参数,而非命名参数。
- Joins 方式返回的主记录可能包含重复数据,需配合 Group 或 Distinct 使用。
要点总结
- 延迟加载简单但易产生 N+1 问题,仅适合少量数据场景。
- Preload通过 IN 查询一次性加载关联数据,是解决 N+1 问题的首选方案。
- Joins使用 SQL 连接查询,适合简单关联且数据量较小的场景。
- 优先使用 Preload,复杂查询再考虑 Joins 或 Raw SQL。
存放路径:D:\git2\jwdev\articles\GORM\进阶\关联关系管理\关联查询与预加载.md
📝 发现内容有误?点击此处直接编辑