Gorm Preload не работает с коллекцией объектов? - PullRequest
0 голосов
/ 29 марта 2020

Мне нужно извлечь все модули из базы данных, где status != Complete.

Моя таблица модулей имеет отображение многих ко многим с таблицей разделов, а таблица разделов имеет отображение многих ко многим с таблицей полей.

Я пытаюсь получить все модули в одном запросе к БД с помощью следующего утверждения.

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

Но он не возвращает разделы и поля. Если я использую приведенное ниже утверждение, то оно возвращает вложенные модели.

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

Означает ли это, что оно работает только для одной записи, а не для коллекции?

Ответы [ 2 ]

0 голосов
/ 30 марта 2020

Используя dbConnection.Set("gorm:auto_preload", true), вы можете загружать только дочерние отношения, но если вы хотите загрузить дочерние и дочерние элементы, то вы должны использовать вложенную предварительную загрузку. Как

db.Preload("Sections").Preload("Sections.Fields").Find(&modules)
0 голосов
/ 29 марта 2020

Я написал пример кода на основе вашего описания, вы можете найти полный код внизу. Позволяет go через различные опции:

// 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}]}]}]

При явной предварительной загрузке вы можете загрузить как Sections, так и Sections.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)
    }
}
...