預載入 (熱切載入)

預載入

GORM 允許透過 Preload 在其他 SQL 中熱切載入關聯,例如

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

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

// Preload Orders when find users
db.Preload("Orders").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4);

db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

關聯預載入

Preload 會在個別查詢中載入關聯資料,關聯預載入 會使用左關聯載入關聯資料,例如

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)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;

關聯巢狀模型

db.Joins("Manager").Joins("Manager.Company").Find(&users)
// SELECT "users"."id","users"."created_at","users"."updated_at","users"."deleted_at","users"."name","users"."age","users"."birthday","users"."company_id","users"."manager_id","users"."active","Manager"."id" AS "Manager__id","Manager"."created_at" AS "Manager__created_at","Manager"."updated_at" AS "Manager__updated_at","Manager"."deleted_at" AS "Manager__deleted_at","Manager"."name" AS "Manager__name","Manager"."age" AS "Manager__age","Manager"."birthday" AS "Manager__birthday","Manager"."company_id" AS "Manager__company_id","Manager"."manager_id" AS "Manager__manager_id","Manager"."active" AS "Manager__active","Manager__Company"."id" AS "Manager__Company__id","Manager__Company"."name" AS "Manager__Company__name" FROM "users" LEFT JOIN "users" "Manager" ON "users"."manager_id" = "Manager"."id" AND "Manager"."deleted_at" IS NULL LEFT JOIN "companies" "Manager__Company" ON "Manager"."company_id" = "Manager__Company"."id" WHERE "users"."deleted_at" IS NULL

注意 關聯預載入 適用於一對一關聯,例如:has onebelongs to

預載入全部

clause.Associations 可以與 Preload 搭配使用,類似於建立/更新時的 Select,您可以使用它來 預載入 所有關聯,例如

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 允許使用條件預載入關聯,它的運作方式類似於 內嵌條件

// Preload Orders with conditions
db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');

db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users WHERE state = 'active';
// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');

自訂預載入 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)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;

巢狀預載入

GORM 支援巢狀預載入,例如

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

// Customize Preload conditions for `Orders`
// And GORM won't preload unmatched order's OrderItems then
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
}
}

// Only preload Org.Address and Org.Address.Country
db.Preload("Address.Country") // "Address" is has_one, "Country" is belongs_to (nested association)

// Only preload Org.VisitingAddress
db.Preload("PostalAddress.Country") // "PostalAddress.Country" is belongs_to (embedded association)

// Only preload Org.NestedAddress
db.Preload("NestedAddress.Address.Country") // "NestedAddress.Address.Country" is belongs_to (embedded association)

// All preloaded include "Address" but exclude "Address.Country", because it won't preload nested associations.
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") // omit "Address" because there is no ambiguity

注意 內嵌預載入 僅適用於 belongs to 關聯。
其他關聯的值在資料庫中相同,我們無法區分它們。

白金贊助商

黃金贊助商

白金贊助商

黃金贊助商