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}]}]}]
通过显式预加载,您可以同时加载 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)
}
}
使用dbConnection.Set("gorm:auto_preload", true)
,您只能加载子关系,但如果您还想加载子关系的子关系,则必须使用嵌套预加载。喜欢
db.Preload("Sections").Preload("Sections.Fields").Find(&modules)
我需要从 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}]}]}]
通过显式预加载,您可以同时加载 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)
}
}
使用dbConnection.Set("gorm:auto_preload", true)
,您只能加载子关系,但如果您还想加载子关系的子关系,则必须使用嵌套预加载。喜欢
db.Preload("Sections").Preload("Sections.Fields").Find(&modules)