You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GORM多对多关联下通过分类ID查询产品的最优方案

问题:GORM多对多关联下通过分类ID查询产品的最优实现

我正在使用Go 1.19及ORM框架GORM,需要通过分类ID查询对应的产品。产品表与分类表为多对多关系,关联表为product_categories。当收到带分类ID的GET请求时,需返回该分类下的所有产品。

模型结构

Product模型

// Product模型
// Categories many2many:product_categories

type Product struct {
    ID               uint           `gorm:"primarykey" json:"id"`
    Slug             string         `gorm:"unique;size:255;" json:"slug"`
    Title            string         `gorm:"size:255;not null" json:"title"`
    Code             string         `gorm:"size:255;not null" json:"code"`
    BrandID          uint           `json:"-"`
    Brand            Brand          `json:"brand"`
    ShortDescription string         `gorm:"not null" json:"short_description"`
    Description      string         `json:"description"`
    Price            uint           `gorm:"not null" json:"price"`
    Quantity         uint           `json:"qnt"`
    DiscountPrice    uint           `json:"discount_price"`
    Categories       []Category     `gorm:"many2many:product_categories;" json:"categories"`
    Attributes       []Attribute    `json:"attributes"`
    ProductImages    []ProductImage `json:"product_images"`
    CreatedAt        time.Time      `json:"-"`
    UpdatedAt        time.Time      `json:"-"`
}

Category模型

// Category模型
// Products many2many:product_categories

type Category struct {
    ID        uint      `gorm:"primarykey" json:"id"`
    Name      string    `gorm:"size:255;not null" json:"name"`
    Icon      string    `gorm:"size:255;not null" json:"icon"`
    Image     string    `gorm:"size:255;not null" json:"image"`
    Weight    int32     `gorm:"AUTO_INCREMENT" json:"weight"`
    Products  []Product `gorm:"many2many:product_categories;" json:"products"`
    CreatedAt time.Time `json:"-"`
    UpdatedAt time.Time `json:"-"`
}

ProductCategory模型

// ProductCategory模型
// 该表由GORM自动生成

type ProductCategory struct {
    CategoryID int  `gorm:"primaryKey" json:"-"`
    ProductID  uint `gorm:"primaryKey" json:"-"`
}

现有实现(非最优)

我目前采用间接方式实现:先查询ProductCategory表获取产品ID,存入切片后再查询产品,代码如下:

func (q *Queries) GetProductsbyCat(id uint) ([]models.Product, error) {
    // 定义products和product_cat变量
    products := []models.Product{}
    product_cats := []models.ProductCategory{}

    // 查询product_cat并赋值
    err := q.Model(&product_cats).Limit(10).Find(&product_cats, "category_id = ?", id).Error
    if err != nil {
        // 返回空对象和错误
        return nil, err
    }

    // 定义产品ID切片
    productIds := []int{}
    // 遍历product_cats,将产品ID添加到productIds中
    for _, v := range product_cats {
        productIds = append(productIds, int(v.ProductID))
    }

    // 查询产品
    err = q.Model(&products).Order("id desc").Preload("ProductImages").Find(&products, productIds).Error
    if err != nil {
        // 返回空对象和错误
        return nil, err
    }

    return products, nil
}

请问在该场景下,使用GORM实现多对多关联查询产品的最优方式是什么?


最优实现方案

方案1:通过Category模型关联查询(推荐,符合GORM关联设计)

利用GORM的预加载能力,直接从分类模型获取关联的产品集合,代码更简洁,且减少数据库查询次数:

import "sort"

func (q *Queries) GetProductsbyCat(id uint) ([]models.Product, error) {
    var category models.Category
    // 查询分类并预加载关联的产品及产品图片
    err := q.Model(&models.Category{}).
        Preload("Products.ProductImages").
        Preload("Products.Brand"). // 若需要品牌信息可添加
        Where("id = ?", id).
        First(&category).Error
    
    if err != nil {
        return nil, err
    }

    // 按产品ID倒序排序(保留原有逻辑)
    sort.Slice(category.Products, func(i, j int) bool {
        return category.Products[i].ID > category.Products[j].ID
    })

    return category.Products, nil
}

方案2:直接查询Product模型(适合仅需产品数据的场景)

通过JOIN关联表直接过滤产品,逻辑更直接,便于添加分页、排序等条件:

func (q *Queries) GetProductsbyCat(id uint) ([]models.Product, error) {
    var products []models.Product
    err := q.Model(&models.Product{}).
        Preload("ProductImages").
        Preload("Brand").
        // 关联中间表过滤分类ID
        Joins("JOIN product_categories ON product_categories.product_id = products.id").
        Where("product_categories.category_id = ?", id).
        Order("products.id desc").
        Limit(10). // 保留原有分页限制
        Find(&products).Error
    
    if err != nil {
        return nil, err
    }

    return products, nil
}

方案优势对比

原有方案需要两次独立数据库查询,存在额外的数据库往返开销;上述两种方案均通过一次主查询(配合预加载的关联查询,GORM会自动优化)完成逻辑,不仅代码更简洁易维护,性能也更优,完全贴合GORM多对多关联的设计意图。


内容的提问来源于stack exchange,提问作者Hardy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:35:22