Golang中使用GORM进行关系映射详解

Golang中使用GORM进行关系映射详解 大家好。有人知道如何在GORM查询中建立关联关系吗?我的意思是,我想通过一次查询获取所有包含关联关系的数据。我阅读了关于预加载(preload)或连接(join)方法的资料,但不太理解。

2 回复

Diego_Alberto_Gonzal:

如何在 GORM 查询中建立关联关系

GORM – 27 Dec 23

Query

检索单个对象 GORM 提供了 FirstTakeLast 方法来从数据库中检索单个对象,它在查询数据库时会添加 LIMIT 1 条件,并且会返回错误 ErrR

使用 Join。免责声明:我对 GORM 一无所知。只懂“原生 SQL”。只是用谷歌搜索了一下 🙂

更多关于Golang中使用GORM进行关系映射详解的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在GORM中处理关联关系查询主要有两种方式:预加载(Preload)和连接(Join)。以下是具体实现示例:

1. 预加载(Preload)方式

预加载通过额外查询加载关联数据,适合大多数场景:

// 定义模型
type User struct {
    ID       uint
    Name     string
    Profile  Profile
    Orders   []Order
}

type Profile struct {
    ID     uint
    UserID uint
    Email  string
}

type Order struct {
    ID     uint
    UserID uint
    Amount float64
}

// 预加载单个关联
var user User
db.Preload("Profile").First(&user, 1)
// 这会执行两条SQL:先查用户,再查关联的Profile

// 预加载多个关联
db.Preload("Profile").Preload("Orders").First(&user, 1)

// 预加载嵌套关联
type OrderItem struct {
    ID      uint
    OrderID uint
    Product string
}

type Order struct {
    ID         uint
    UserID     uint
    Amount     float64
    OrderItems []OrderItem
}

var user User
db.Preload("Orders.OrderItems").First(&user, 1)

// 带条件的预加载
db.Preload("Orders", "amount > ?", 100).First(&user, 1)

2. 连接(Join)方式

连接通过SQL JOIN一次性获取所有数据,适合需要过滤关联数据的场景:

// 内连接查询
var users []User
db.Joins("Profile").Find(&users)
// 生成的SQL: SELECT * FROM users INNER JOIN profiles ON users.id = profiles.user_id

// 左连接查询
db.Joins("LEFT JOIN profiles ON users.id = profiles.user_id").Find(&users)

// 带条件的连接查询
db.Joins("Profile").Where("profiles.email LIKE ?", "%@gmail.com").Find(&users)

// 多个连接
db.Joins("Profile").Joins("LEFT JOIN orders ON users.id = orders.user_id").Find(&users)

// 选择特定字段
db.Select("users.name, profiles.email").
    Joins("Profile").
    Where("users.id = ?", 1).
    Scan(&result)

3. 关联预加载的高级用法

// 预加载所有关联
db.Preload(clause.Associations).First(&user, 1)

// 链式预加载
db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
    return db.Order("created_at DESC")
}).First(&user, 1)

// 使用结构体标签定义关联
type User struct {
    ID       uint
    Name     string
    Profile  Profile `gorm:"foreignKey:UserID"`
    Orders   []Order `gorm:"foreignKey:UserID"`
}

// 多对多关联预加载
type Product struct {
    ID    uint
    Name  string
    Tags  []Tag `gorm:"many2many:product_tags;"`
}

type Tag struct {
    ID   uint
    Name string
}

var product Product
db.Preload("Tags").First(&product, 1)

4. 性能对比示例

// 预加载方式(N+1查询优化)
var users []User
db.Preload("Orders").Find(&users)
// 执行2条SQL:1条查用户,1条批量查所有订单

// 连接方式(单次查询)
var result []struct {
    UserID    uint
    UserName  string
    OrderID   uint
    OrderAmount float64
}
db.Table("users").
    Select("users.id as user_id, users.name as user_name, orders.id as order_id, orders.amount as order_amount").
    Joins("LEFT JOIN orders ON users.id = orders.user_id").
    Scan(&result)

选择预加载还是连接取决于具体需求:预加载更简单且能避免重复数据,连接则在需要基于关联表条件过滤时更高效。

回到顶部