GORM 基础 -- Associations
1、Belongs To
属于(belongs to
)关联设置与另一个模型建立一对一的连接,这样声明模型的每个实例都“属于(belongs to)”另一个模型的一个实例。
例如,如果您的应用程序包括用户(users )和公司(companies),并且每个用户只能分配给一个公司,那么以下类型表示这种关系。注意这里,在User
对象上,有一个CompanyID
和一个Company
。默认情况下,CompanyID
被隐式用于在User
和Company
表之间创建外键关系,因此必须包含在User
结构体中以填充Company
内部结构。
// `User` belongs to `Company`, `CompanyID` is the foreign key
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
}
type Company struct {
ID int
Name string
}
有关填充内部结构的详细信息,请参阅预先加载。
1.1 覆写外键
要定义一个属于关系,外键必须存在,默认外键使用所有者(owner’s )的类型名加上它的主字段名。
对于上面的例子,要定义属于Company
的User
模型,按照约定外键应该是CompanyID
GORM提供了一种自定义外键的方法,例如:
type User struct {
gorm.Model
Name string
CompanyRefer int
Company Company `gorm:"foreignKey:CompanyRefer"`
// use CompanyRefer as foreign key
}
type Company struct {
ID int
Name string
}
1.2 覆写引用(References)
对于属于关系,GORM通常使用所有者的主键字段作为外键的值,对于上面的例子,它是Company
的字段ID
。
当您将用户分配给公司时,GORM将把公司ID
保存到用户的CompanyID
字段中。
你可以通过标签references
来改变它,例如:
type User struct {
gorm.Model
Name string
CompanyID string
Company Company `gorm:"references:Code"` // use Code as references
}
type Company struct {
ID int
Code string
Name string
}
GORM通常猜测关系是
has one
,如果覆写外键名称已经存在于所有者类型中,我们需要在belongs to
关系中指定references
。
type User struct {
gorm.Model
Name string
CompanyID string
Company Company `gorm:"references:CompanyID"` // use Company.CompanyID as references
}
type Company struct {
CompanyID int
Code string
Name string
}
1.3 CRUD with Belongs To
要使用 belongs to
关系的关联模式(Association Mode )
1.4 预先加载 (Eager Loading)
GORM允许使用Preload
和Joins
预先加载属belongs to
关联,请参阅预先加载(即时加载)了解详细信息
1.5 外键约束
你可以用标签constraint
设置OnUpdate
, OnDelete
约束,它将创建与GORM
迁移时,例如:
type User struct {
gorm.Model
Name string
CompanyID int
Company Company `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type Company struct {
ID int
Name string
}
2、Has One
has one
关联,与另一个模型建立一对一的连接,但是语义(和结果)略有不同。这种关联表明一个模型的每个实例包含或拥有另一个模型的一个实例。
例如,如果您的应用程序包括用户和信用卡,并且每个用户只能拥有一张信用卡。
2.1 声明
// User has one CreditCard, UserID is the foreign key
type User struct {
gorm.Model
CreditCard CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
2.2 检索
// Retrieve user list with eager loading credit card
func GetAll(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("CreditCard").Find(&users).Error
return users, err
}
2.3 覆盖外键
对于has one
关系,外键字段必须也存在,所有者将属于它的模型的主键保存到这个字段中。
字段的名称通常是由has one
模型的类型加上它的主键生成的,对于上面的例子来说,它是UserID
。
当您向用户提供信用卡时,它会将用户的ID
保存到UserID
字段中。
如果你想用另一个字段来保存关系,你可以用标签foreignKey
来改变它,例如:
type User struct {
gorm.Model
CreditCard CreditCard `gorm:"foreignKey:UserName"`
// use UserName as foreign key
}
type CreditCard struct {
gorm.Model
Number string
UserName string
}
2.4 覆盖引用
默认情况下,拥有的实体将拥有has one
模型的主键保存为外键,您可以更改为保存另一个字段的值,例如下面的示例使用Name
。
你可以通过标签references
来改变它,例如:
type User struct {
gorm.Model
Name string `gorm:"index"`
CreditCard CreditCard `gorm:"foreignKey:UserName;references:name"`
}
type CreditCard struct {
gorm.Model
Number string
UserName string
}
2.5 多态性关联
GORM对于has one
和has many
支持多态关联,它将拥有实体的表名保存到多态类型的字段中,主键保存到多态字段中
type Cat struct {
ID int
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
}
type Dog struct {
ID int
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
}
type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
}
db.Create(&Dog{Name: "dog1", Toy: Toy{Name: "toy1"}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs")
你可以用标签polymorphicValue
改变多态类型的值,例如:
type Dog struct {
ID int
Name string
Toy Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
}
type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
}
db.Create(&Dog{Name: "dog1", Toy: Toy{Name: "toy1"}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master")
2.6 CRUD with Has One
请参考关联模型的has one
2.7 预加载
GORM允许使用Preload
和Joins
为has one
关联预加载,请参阅预加载(即时加载)了解详细信息
Self-Referential Has One
type User struct {
gorm.Model
Name string
ManagerID *uint
Manager *User
}
2.8 外键约束
你可以用标签constraint
设置OnUpdate
, OnDelete
约束,当GORM迁移时它将创建,例如:
type User struct {
gorm.Model
CreditCard CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
您还可以删除用Select
选中的has one
关联, Delete with Select的详细信息
3、Has Many
3.1 Has Many
has many
关联,与另一个模型建立一对多(one-to-many)的连接,与拥有has one
模型不同,所有者可以拥有0个或多个模型实例。
例如,如果您的应用程序包括用户和信用卡,并且每个用户可以有许多信用卡。
3.2 Declare
// User has many CreditCards, UserID is the foreign key
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
3.3 检索
// Retrieve user list with eager loading credit cards
func GetAll(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("CreditCards").Find(&users).Error
return users, err
}
3.4 覆盖外键
要定义一个has many
关系,外键必须存在。默认外键的名称是所有者的类型名称加上其主键字段的名称
例如,要定义一个属于User
的模型,外键应该是UserID
。
使用其他字段作为外键,你可以自定义一个foreignKey
标签,例如:
type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}
type CreditCard struct {
gorm.Model
Number string
UserRefer uint
}
3.5 覆盖引用
GORM通常使用所有者的主键作为外键的值,对于上面的例子,它是User
的ID
,
当您将信用卡分配给用户时,GORM将把用户的ID
保存到信用卡的UserID
字段中。
你可以通过标签references
来改变它,例如:
type User struct {
gorm.Model
MemberNumber string
CreditCards []CreditCard `gorm:"foreignKey:UserNumber;references:MemberNumber"`
}
type CreditCard struct {
gorm.Model
Number string
UserNumber string
}
3.6 多态关联
GORM支持多态关联,将拥有实体的表名保存到多态类型(type)的字段中,主键值保存到多态字段中
type Dog struct {
ID int
Name string
Toys []Toy `gorm:"polymorphic:Owner;"`
}
type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
}
db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs"), ("toy2","1","dogs")
你可以用标签polymorphicValue
改变多态类型的值,例如:
type Dog struct {
ID int
Name string
Toys []Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
}
type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
}
db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master"), ("toy2","1","master")
3.7 CRUD with Has Many
请参考关联模型的has many
3.8 预加载
GORM允许使用Preload
为has many
关联预加载,请参阅预加载(快速加载)了解详细信息
Self-Referential Has Many
type User struct {
gorm.Model
Name string
ManagerID *uint
Team []User `gorm:"foreignkey:ManagerID"`
}
3.9 外键约束
你可以用不用标签constraint
设置OnUpdate
, OnDelete
约束,在GORM迁移时它将创建,例如:
type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
您还可以删除用Select
选中的has many
关联, Delete with Select的详细信息
4、Many To Many
多对多在两个模型之间添加一个连接表。
例如,如果您的应用程序包括用户和语言,一个用户可以说多种语言,而许多用户可以说一种指定的语言。
// User has and belongs to many languages, `user_languages` is the join table
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
当使用GORM AutoMigrate
为User
创建表时,GORM将自动创建连接表
4.1 Back-Reference
Declare
// User has and belongs to many languages, use `user_languages` as join table
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages;"`
}
检索
// Retrieve user list with eager loading languages
func GetAllUsers(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("Languages").Find(&users).Error
return users, err
}
// Retrieve language list with eager loading users
func GetAllLanguages(db *gorm.DB) ([]Language, error) {
var languages []Language
err := db.Model(&Language{}).Preload("Users").Find(&languages).Error
return languages, err
}
4.2 覆盖外键
对于多对多关系,连接表拥有引用两个模型的外键,例如:
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
// Join Table: user_languages
// foreign key: user_id, reference: users.id
// foreign key: language_id, reference: languages.id
要覆盖它们,你可以使用标签foreignKey
, references
, joinForeignKey
, joinReferences
,没有必要一起使用,你可以只使用其中一个来覆盖一些外键/引用
type User struct {
gorm.Model
Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;joinReferences:ProfileRefer"`
Refer uint `gorm:"index:,unique"`
}
type Profile struct {
gorm.Model
Name string
UserRefer uint `gorm:"index:,unique"`
}
// Which creates join table: user_profiles
// foreign key: user_refer_id, reference: users.refer
// foreign key: profile_refer, reference: profiles.user_refer
注意:
有些数据库只允许创建引用具有唯一索引的字段的数据库外键,因此如果在迁移时创建数据库外键,则需要指定unique index
标记
4.3 Self-Referential Many2Many
type User struct {
gorm.Model
Friends []*User `gorm:"many2many:user_friends"`
}
// Which creates join table: user_friends
// foreign key: user_id, reference: users.id
// foreign key: friend_id, reference: users.id
4.4 Eager Loading
GORM允许使用Preload
为many2many
关联预加载,请参阅预加载(快速加载)了解详细信息
4.5 CRUD with Many2Many
请参考关联模型的has many
4.6 自定义 JoinTable
JoinTable
可以是一个功能齐全的模型,比如有软删除(Soft Delete
),钩子(Hooks
)支持和更多的字段,你可以用SetupJoinTable
来设置它,例如:
注意:
自定义连接表的外键必须是组合主键或组合唯一索引
type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_address;"`
}
type Address struct {
ID uint
Name string
}
type PersonAddress struct {
PersonID int `gorm:"primaryKey"`
AddressID int `gorm:"primaryKey"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
func (PersonAddress) BeforeCreate(db *gorm.DB) error {
// ...
}
// Change model Person's field Addresses' join table to PersonAddress
// PersonAddress must defined all required foreign keys or it will raise error
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
4.7 外键约束
你可以使用标签constraint
设置OnUpdate
, OnDelete
约束,在GORM迁移时它将创建,例如:
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_speaks;"`
}
type Language struct {
Code string `gorm:"primarykey"`
Name string
}
// CREATE TABLE `user_speaks` (`user_id` integer,`language_code` text,PRIMARY KEY (`user_id`,`language_code`),CONSTRAINT `fk_user_speaks_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE,CONSTRAINT `fk_user_speaks_language` FOREIGN KEY (`language_code`) REFERENCES `languages`(`code`) ON DELETE SET NULL ON UPDATE CASCADE);
您还可以删除用Select
选中的many2many
关联, Delete with Select的详细信息
4.8 复合外键
如果您的模型使用复合主键,GORM将默认启用复合外键
你可以重写默认的外键,指定多个外键,只是用逗号分隔这些键的名称,例如:
你可以重写默认的外键,指定多个外键,只是用逗号分隔这些键的名称,例如:
type Tag struct {
ID uint `gorm:"primaryKey"`
Locale string `gorm:"primaryKey"`
Value string
}
type Blog struct {
ID uint `gorm:"primaryKey"`
Locale string `gorm:"primaryKey"`
Subject string
Body string
Tags []Tag `gorm:"many2many:blog_tags;"`
LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
}
// Join Table: blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id
// foreign key: tag_locale, reference: tags.locale
// Join Table: locale_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id
// Join Table: shared_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: tag_id, reference: tags.id
5、关联模型
5.1 自动创建/更新
在创建/更新记录时,GORM将使用Upsert
自动保存关联及其引用。
user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "jinzhu@example.com"},
{Email: "jinzhu-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
db.Create(&user)
// BEGIN TRANSACTION;
// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "languages" ("name") VALUES ('ZH'), ('EN') ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "user_languages" ("user_id","language_id") VALUES (111, 1), (111, 2) ON DUPLICATE KEY DO NOTHING;
// COMMIT;
db.Save(&user)
如果你想要更新关联的数据,你应该使用FullSaveAssociations
模式:
db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
// ...
// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY SET address1=VALUES(address1);
// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY SET email=VALUES(email);
// ...
5.2 跳过自动创建/更新
要在创建/更新时跳过自动保存,你可以使用Select
或Omit
,例如:
user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "jinzhu@example.com"},
{Email: "jinzhu-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
db.Select("Name").Create(&user)
// INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2);
db.Omit("BillingAddress").Create(&user)
// Skip create BillingAddress when creating a user
db.Omit(clause.Associations).Create(&user)
// Skip all associations when creating a user
注意:
对于many2many
关联,GORM会在创建连接表引用之前插入关联,如果你想跳过关联的插入,你可以像这样跳过:
db.Omit("Languages.*").Create(&user)
下面的代码将跳过关联及其引用的创建
db.Omit("Languages").Create(&user)
5.3 选择/省略(Select/Omit
)关联字段
user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1", Address2: "addr2"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1", Address2: "addr2"},
}
// Create user and his BillingAddress, ShippingAddress
// When creating the BillingAddress only use its address1, address2 fields and omit others
db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user)
db.Omit("BillingAddress.Address2", "BillingAddress.CreatedAt").Create(&user)
5.4 关联模式
关联模式包含一些常用的帮助器方法来处理关系
// Start Association Mode
var user User
db.Model(&user).Association("Languages")
// `user` is the source model, it must contains primary key
// `Languages` is a relationship's field name
// If the above two requirements matched, the AssociationMode should be started successfully, or it should return error
db.Model(&user).Association("Languages").Error
5.4.1 查找关联
匹配的关联
db.Model(&user).Association("Languages").Find(&languages)
有条件查找关联
codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Find(&languages)
db.Model(&user).Where("code IN ?", codes).Order("code desc").Association("Languages").Find(&languages)
5.4.2 Append 关联
为many to many
,has many
附加新关联,为has one
,belongs to
替换现有关联
db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Append(&Language{Name: "DE"})
db.Model(&user).Association("CreditCard").Append(&CreditCard{Number: "411111111111"})
5.4.3 Replace 关联
用新的关联替换当前的关联
db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)
5.4.4 Delete 关联
如果存在,删除源和参数之间的关系,只删除引用,不会从DB中删除这些对象。
db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Delete(languageZH, languageEN)
5.4.5 Clear 关联
删除源和关联之间的所有引用,不会删除这些关联
db.Model(&user).Association("Languages").Clear()
5.4.6 Count 关联
返回当前关联的计数
db.Model(&user).Association("Languages").Count()
// Count with conditions
codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Count()
5.4.7 Batch Data
关联模式支持批量数据,例如:
// Find all roles for all users
db.Model(&users).Association("Role").Find(&roles)
// Delete User A from all user's team
db.Model(&users).Association("Team").Delete(&userA)
// Get distinct count of all users' teams
db.Model(&users).Association("Team").Count()
// For `Append`, `Replace` with batch data, the length of the arguments needs to be equal to the data's length or else it will return an error
var users = []User{user1, user2, user3}
// e.g: we have 3 users, Append userA to user1's team, append userB to user2's team, append userA, userB and userC to user3's team
db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC})
// Reset user1's team to userA,reset user2's team to userB, reset user3's team to userA, userB and userC
db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})
5.5 Delete with Select
在删除记录时,可以使用Select
删除选中的有has one/has many/many2many
关系。例如:
// delete user's account when deleting user
db.Select("Account").Delete(&user)
// delete user's Orders, CreditCards relations when deleting user
db.Select("Orders", "CreditCards").Delete(&user)
// delete user's has one/many/many2many relations when deleting user
db.Select(clause.Associations).Delete(&user)
// delete each user's account when deleting users
db.Select("Account").Delete(&users)
注意:
只有当删除记录的主键不为零时,关联才会被删除,GORM将使用这些主键作为删除所选关联的条件
// DOESN'T WORK
db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{})
// will delete all user with name `jinzhu`, but those user's account won't be deleted
db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{ID: 1})
// will delete the user with name = `jinzhu` and id = `1`, and user `1`'s account will be deleted
db.Select("Account").Delete(&User{ID: 1})
// will delete the user with id = `1`, and user `1`'s account will be deleted
5.6 Association Tags
Tag | Description |
---|---|
foreignKey | Specifies column name of the current model that is used as a foreign key to the join table |
references | Specifies column name of the reference’s table that is mapped to the foreign key of the join table |
polymorphic | Specifies polymorphic type such as model name |
polymorphicValue | Specifies polymorphic value, default table name |
many2many | Specifies join table name |
joinForeignKey | Specifies foreign key column name of join table that maps to the current table |
joinReferences | Specifies foreign key column name of join table that maps to the reference’s table |
constraint | Relations constraint, e.g: OnUpdate,OnDelete |
6、Preloading (Eager Loading)
6.1 Preload
GORM允许在其他SQL中使用Preload
快速加载关联,例如:
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
6.2 Joins Preloading
Preload
在一个单独的查询中加载关联数据,Join 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})
Join with conditions
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;
Join Preload
工作在一对一的关系下,例如:has one
,belong to
6.3 Preload All
当创建/更新时,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
won’t preload nested associations, but you can use it with Nested Preloading together, e.g:
db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)
6.4 Preload with conditions
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');
6.5 Custom Preloading SQL
You are able to custom preloading SQL by passing in func(db *gorm.DB) *gorm.DB
, for example:
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;
6.6 Nested Preloading
GORM supports nested preloading, for example:
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)