gorm 预加载不适用于对象集合吗?

does gorm preload not work with collection of objects?

我需要从 status != Complete.

的数据库中获取所有模块

我的模块 table 与部分 table 有多对多映射,部分 table 与字段 table.

有多对多映射

我正在尝试使用以下语句在单个数据库查询中获取所有模块。

dbConnection.Set("gorm:auto_preload", true).Where("status != ?", enum.Completed).Find(&modules)

但它没有 return 部分和字段。 如果我使用下面的语句,那么它会 return 嵌套模型。

dbConnection.Set("gorm:auto_preload", true).Where("status != ?", enum.Completed).First(&modules)

这是否意味着它只适用于单个记录而不适用于集合?

我根据你的描述写了一个示例代码,你可以在底部找到完整的代码。让我们来看看不同的选择:

// Use auto_preload and .First
db.Set("gorm:auto_preload", true).Where("name = ?", "m0").First(&modules)

Output:
[{ID:1 Name:m0 Status:0 Sections:[{ID:2 Name:s1 Fields:[]}]}]

所以 .First 你得到的部分而不是 Sections.Fields

// Use auto_preload and .Find
db.Set("gorm:auto_preload", true).Find(&modules)

Output:
[{ID:1 Name:m0 Status:0 Sections:[{ID:2 Name:s1 Fields:[]}]} {ID:2 Name:m1 Status:0 Sections:[{ID:1 Name:s0 Fields:[]}]}]

使用 .Find 你会得到与 .First

相同的行为
// Use .Preload
db.Preload("Sections").Preload("Sections.Fields").Find(&modules)

Output:
[{ID:1 Name:m0 Status:0 Sections:[{ID:2 Name:s1 Fields:[{ID:1 Name:f0} {ID:2 Name:f1}]}]} {ID:2 Name:m1 Status:0 Sections:[{ID:1 Name:s0 Fields:[{ID:1 Name:f0} {ID:3 Name:f2}]}]}]

通过显式预加载,您可以同时加载 SectionsSections.Fields。使用 auto_preload 仅预加载直接字段,而不是更深的字段。通常,IMO 使用显式预加载是一种更好的做法。

完整代码:

package main

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "log"
)

type Module struct {
    ID       int `gorm:"primary_key"`
    Name     string
    Status   int
    Sections []Section `gorm:"many2many:module_sections;"`
}

type Section struct {
    ID     int `gorm:"primary_key"`
    Name   string
    Fields []Field `gorm:"many2many:section_fields;"`
}

type Field struct {
    ID   int `gorm:"primary_key"`
    Name string
}

func preloadingSample() error {
    db, err := gorm.Open("sqlite3", "test.db")
    if err != nil {
        return fmt.Errorf("open DB failed: %w", err)
    }
    defer db.Close()

    err = db.AutoMigrate(
        &Module{},
        &Section{},
        &Field{},
    ).Error
    if err != nil {
        return fmt.Errorf("migration failed: %w", err)
    }

    // Delete everything in DB
    db.Delete(&Module{})
    db.Delete(&Section{})
    db.Delete(&Field{})

    // Put some sample data in DB
    sampleFields := []Field{
        {Name: "f0"},
        {Name: "f1"},
        {Name: "f2"},
        {Name: "f3"},
    }
    for idx := range sampleFields {
        err = db.Create(&sampleFields[idx]).Error
        if err != nil {
            return fmt.Errorf("failed to create: %w", err)
        }
    }

    sampleSections := []Section{
        {Name: "s0", Fields: []Field{sampleFields[0], sampleFields[2]}},
        {Name: "s1", Fields: []Field{sampleFields[0], sampleFields[1]}},
    }
    for idx := range sampleSections {
        err = db.Create(&sampleSections[idx]).Error
        if err != nil {
            return fmt.Errorf("failed to create: %w", err)
        }
    }

    sampleModules := []Module{
        {Name: "m0", Sections: []Section{sampleSections[1]}},
        {Name: "m1", Sections: []Section{sampleSections[0]}},
    }
    for idx := range sampleModules {
        err = db.Create(&sampleModules[idx]).Error
        if err != nil {
            return fmt.Errorf("failed to create: %w", err)
        }
    }

    var modules []Module

    // Load just one module with auto_preload
    err = db.Set("gorm:auto_preload", true).Where("name = ?", "m0").First(&modules).Error
    if err != nil {
        return fmt.Errorf("auto_preload first: %w", err)
    }
    fmt.Printf("%+v\n", modules)

    // Load all modules with auto_preload
    err = db.Set("gorm:auto_preload", true).Find(&modules).Error
    if err != nil {
        return fmt.Errorf("auto_preload find: %w", err)
    }
    fmt.Printf("%+v\n", modules)

    // Explicitly load
    err = db.Preload("Sections").Preload("Sections.Fields").Find(&modules).Error
    if err != nil {
        return fmt.Errorf("preload find: %w", err)
    }
    fmt.Printf("%+v\n", modules)

    return nil
}

func main() {
    err := preloadingSample()
    if err != nil {
        log.Fatal(err)
    }
}

使用dbConnection.Set("gorm:auto_preload", true),您只能加载子关系,但如果您还想加载子关系的子关系,则必须使用嵌套预加载。喜欢

db.Preload("Sections").Preload("Sections.Fields").Find(&modules)