当前位置: 首页 > article >正文

GORM 基础 -- Associations

1、Belongs To

属于(belongs to)关联设置与另一个模型建立一对一的连接,这样声明模型的每个实例都“属于(belongs to)”另一个模型的一个实例。

例如,如果您的应用程序包括用户(users )和公司(companies),并且每个用户只能分配给一个公司,那么以下类型表示这种关系。注意这里,在User对象上,有一个CompanyID和一个Company。默认情况下,CompanyID被隐式用于在UserCompany表之间创建外键关系,因此必须包含在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 )的类型名加上它的主字段名。

对于上面的例子,要定义属于CompanyUser模型,按照约定外键应该是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 onehas 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 Joinshas 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通常使用所有者的主键作为外键的值,对于上面的例子,它是UserID

当您将信用卡分配给用户时,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允许使用Preloadhas 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 AutoMigrateUser创建表时,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允许使用Preloadmany2many 关联预加载,请参阅预加载(快速加载)了解详细信息

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 跳过自动创建/更新

要在创建/更新时跳过自动保存,你可以使用SelectOmit,例如:

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 manyhas many 附加新关联,为has onebelongs 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

TagDescription
foreignKeySpecifies column name of the current model that is used as a foreign key to the join table
referencesSpecifies column name of the reference’s table that is mapped to the foreign key of the join table
polymorphicSpecifies polymorphic type such as model name
polymorphicValueSpecifies polymorphic value, default table name
many2manySpecifies join table name
joinForeignKeySpecifies foreign key column name of join table that maps to the current table
joinReferencesSpecifies foreign key column name of join table that maps to the reference’s table
constraintRelations 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)


http://www.kler.cn/news/10629.html

相关文章:

  • 这7种常见的JavaScript错误,你知道吗?
  • 规模化敏捷框架:Scrum@Scale
  • 他98年的,我真的玩不过他...
  • 请我为详细讲解C11的新增原子操作
  • Oracle-主备切换问题(BUG-31747989)
  • 论文阅读 - ANEMONE: Graph Anomaly Detection with Multi-Scale Contrastive Learning
  • 大数据 | 实验一:大数据系统基本实验 | MapReduce 初级编程
  • JAVA经典之递归测试01-----JAVA入门基础教程
  • #详细介绍!!! 造成死锁的原因以及解决方案!
  • L2-042 老板的作息表(极短代码)
  • JavaScript【六】JavaScript中的字符串(String)
  • python+vue 在线考试系统的设计与实现
  • Quartz框架详解分析
  • 技术创业者必读:从验证想法到技术产品商业化的全方位解析
  • 算法训练Day29:491.递增子序列, 46.全排列 ,47.全排列 II
  • selenium自动化测试面试题【含答案】
  • 关于“复活节Easter”知识,你了解多少?
  • SadTalker项目上手教程
  • yolov8训练自己的数据集
  • 数据库系列之MySQL线程ID和操作系统线程ID对应关系
  • 雪花算法ID冲突问题与解决方案
  • 如何从 PowerPoint 导出高分辨率(高 dpi)幻灯片
  • Chocolatey 包管理器安装配置
  • Python 3.x教程:如何使用Python将图片转为漫画风格?
  • Linux内核源码下载方式
  • 用Claude和Stable Diffusion绘制《武松打虎》
  • ChatGPT文本框再次升级,打造出新型操作系统
  • 系统复杂度之【高可用】
  • 科普:c语言与C++的区别
  • Symble