关联

预加载 (Eager Loading)

qianmoQqianmoQ· 更新于 2026-09-23· 阅读 10 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

Preload

GORM 允许通过 Preload 在其他 SQL 中进行预加载,例如:

泛型 API

type User struct {
  gorm.Model
  Username string
  Orders   []Order
}

type Order struct {
  gorm.Model
  UserID uint
  Price  float64
}



user, err := gorm.G[User](db).Preload("Order", nil).Find(ctx)





user, err = gorm.G[User](db).Preload("Orders", func(db gorm.PreloadBuilder) error {
  db.Order("orders.price DESC")
  return nil
}).Find(ctx)


传统 API


db.Preload("Orders").Find(&users)



db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)



Joins 预加载

Preload 通过单独的查询加载关联数据,Join Preload 则使用左连接加载关联数据,例如:

泛型 API

type User struct {
  gorm.Model
  Username string
  Order    Order
}

type Order struct {
  gorm.Model
  UserID uint
  Price  float64
}

users, err := gorm.G[User](db).Joins(clause.JoinTarget{Association: "Order"}, nil).Find(ctx)



users, err := gorm.G[User](db).Joins(
  clause.JoinTarget{Association: "Order"},
  func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {
    db.Where(Order{Price: 1000})
    return nil
}).Find(ctx)

传统 API

db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
db.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})

带条件的关联

db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)

嵌套模型关联

db.Joins("Manager").Joins("Manager.Company").Find(&users)

注意 Join Preload 适用于一对一关系,例如:has one、belongs to

预加载所有关联

clause.Associations 可以与 Preload 一起使用,类似于创建/更新时的 Select,你可以用它来 Preload 所有关联,例如:

type User struct {
  gorm.Model
  Name       string
  CompanyID  uint
  Company    Company
  Role       Role
  Orders     []Order
}

db.Preload(clause.Associations).Find(&users)

clause.Associations 不会预加载嵌套关联,但你可以与嵌套预加载一起使用,例如:

db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)

带条件的预加载

GORM 允许带条件地预加载关联,其用法类似于内联条件


db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)



db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)

自定义预加载 SQL

你可以通过传入 func(db *gorm.DB) *gorm.DB 来自定义预加载 SQL,例如:

db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
  return db.Order("orders.amount DESC")
}).Find(&users)

嵌套预加载

GORM 支持嵌套预加载,例如:

db.Preload("Orders.OrderItems.Product").Preload("CreditCard").Find(&users)



db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)

内嵌预加载

内嵌预加载用于 内嵌结构体,特别是相同的结构体。内嵌预加载的语法与嵌套预加载类似,它们用点号分隔。

例如:

type Address struct {
  CountryID int
  Country   Country
}

type Org struct {
  PostalAddress   Address `gorm:"embedded;embeddedPrefix:postal_address_"`
  VisitingAddress Address `gorm:"embedded;embeddedPrefix:visiting_address_"`
  Address         struct {
    ID int
    Address
  } `gorm:"embedded;embeddedPrefix:nested_address_"`
}


db.Preload("Address.Country")  


db.Preload("PostalAddress.Country") 


db.Preload("NestedAddress.Address.Country") 


db.Preload(clause.Associations)

当不存在歧义时,我们可以省略内嵌部分。

type Address struct {
  CountryID int
  Country   Country
}

type Org struct {
  Address Address `gorm:"embedded"`
}

db.Preload("Address.Country")
db.Preload("Country") 

注意 内嵌预加载 仅适用于 belongs to 关系。
其他关系的值在数据库中是相同的,我们无法区分它们。

GitHub tag (latest SemVer)

评论

登录后参与评论

正在加载评论…