ToB企服应用市场:ToB评测及商务社交产业平台

标题: Gorm 关联关系介绍与基本使用 [打印本页]

作者: 丝    时间: 2024-4-2 14:20
标题: Gorm 关联关系介绍与基本使用
目录

一 Belongs To(一对一)

1.1 Belongs To

belongs to 会与另一个模型建立了一对一的连接。 这种模型的每一个实例都“属于”另一个模型的一个实例。
例如,您的应用包含 user 和 company,并且每个 user 能且只能被分配给一个 company。下面的类型就表示这种关系。 注意,在 User 对象中,有一个和 Company 一样的 CompanyID。 默认情况下, CompanyID 被隐含地用来在 User 和 Company 之间创建一个外键关系, 因此必须包含在 User 结构体中才能填充 Company 内部结构体。
  1. // `User` 属于 `Company`,`CompanyID` 是外键
  2. type User struct {
  3.   gorm.Model
  4.   Name      string
  5.   CompanyID int
  6.   Company   Company
  7. }
  8. type Company struct {
  9.   ID   int
  10.   Name string
  11. }
  12. db.AutoMigrate(&User{},&Company{})
复制代码
请参阅 预加载 以了解内部结构的详细信息。
1.2 重写外键

要定义一个 belongs to 关系,数据库的表中必须存在外键。默认情况下,外键的名字,使用拥有者的类型名称加上表的主键的字段名字
例如,定义一个User实体属于Company实体,那么外键的名字一般使用CompanyID。
GORM同时提供自定义外键名字的方式,如下例所示。
  1. type User struct {
  2.   gorm.Model
  3.   Name         string
  4.   CompanyRefer int // 外键改名字为CompanyRefer
  5.   Company      Company `gorm:"foreignKey:CompanyRefer"`
  6.   // 使用 CompanyRefer 作为外键
  7. }
  8. type Company struct {
  9.   ID   int
  10.   Name string
  11. }
复制代码
1.3 重写引用(一般不用)

对于 belongs to 关系,GORM 通常使用数据库表,主表(拥有者)的主键值作为外键参考。 正如上面的例子,我们使用主表Company中的主键字段ID作为外键的参考值。
如果在Company实体中设置了User实体,那么GORM会自动把Company中的ID属性保存到User的CompanyID属性中。
同样的,您也可以使用标签 references 来更改它,例如:
  1. type User struct {
  2.   gorm.Model
  3.   Name      string
  4.   CompanyCode int
  5.   Company   Company `gorm:"foreignKey:CompanyCode;references:Code"` // 指定外键字段为CompanyCode,指定与公司表中Code字段关联
  6. }
  7. type Company struct {
  8.   ID   int
  9.   Code int `gorm:"primarykey"`
  10.   Name string
  11. }
复制代码
1.4 Belongs to 的 CRUD

点击 关联模式 链接获取 belongs to 相关的用法
1.5 预加载

GORM允许通过使用Preload或者Joins来主动加载实体的关联关系,具体内容请参考,预加载(主动加载)
1.6 外键约束

你可以通过OnUpdate, OnDelete配置标签来增加关联关系的级联操作,如下面的例子,通过GORM可以完成用户和公司的级联更新和级联删除操作:
  1. type User struct {
  2.         gorm.Model
  3.         Name      string
  4.         Age int
  5.         UserDetailID int
  6.         UserDetail   UserDetail `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` // 级联更新,删除时置空
  7. }
  8. type UserDetail struct {
  9.         ID   int
  10.         Addr string
  11. }
  12. // Navicat的设计表中可以查看
复制代码
二、Has One

2.1 Has One

has one 与另一个模型建立一对一的关联,但它和一对一关系有些许不同。 这种关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例。
例如,您的应用包含 user 和 credit card 模型,且每个 user 只能有一张 credit card。
  1. // User 有一张 CreditCard,UserID 是外键
  2. type User struct {
  3.         gorm.Model
  4.         CreditCard CreditCard // 与CreditCard表有关联关系
  5. }
  6. type CreditCard struct {
  7.         gorm.Model
  8.         Number string
  9.         UserID uint  //关联字段在此表
  10. }
复制代码
2.2 重写外键

对于 has one 关系,同样必须存在外键字段。拥有者将把属于它的模型的主键保存到这个字段。
这个字段的名称通常由 has one 模型的类型加上其 主键 生成,对于上面的例子,它是 UserID。
为 user 添加 credit card 时,它会将 user 的 ID 保存到自己的 UserID 字段。
如果你想要使用另一个字段来保存该关系,你同样可以使用标签 foreignKey 来更改它,例如:
  1. type User struct {
  2.         gorm.Model
  3.         CreditCard CreditCard `gorm:"foreignKey:UserName"`
  4.         // 使用 UserName 作为外键
  5. }
  6. type CreditCard struct {
  7.         gorm.Model
  8.         Number   string
  9.         UserName string // 使用 UserName 作为外键
  10. }
复制代码
2.3 重写引用

默认情况下,拥有者实体会将 has one 对应模型的主键保存为外键,您也可以修改它,用另一个字段来保存,例如下个这个使用 Name 来保存的例子。
您可以使用标签 references 来更改它,例如:
  1. type User struct {
  2.         gorm.Model
  3.         Name       int     `gorm:"index"`  // name建是索引
  4.         CreditCard CreditCard `gorm:"foreignkey:UserName;references:name"`  // 信用卡表的UserName字段跟用户表Name字段关联
  5. }
  6. type CreditCard struct {
  7.         gorm.Model
  8.         Number   string
  9.         UserName int
  10. }
复制代码
2.4 多态关联

GORM 为 has one 和 has many 提供了多态关联支持,它会将拥有者实体的表名、主键值都保存到多态类型的字段中。
  1. type Cat struct {
  2.   ID    int
  3.   Name  string
  4.   Toy   Toy `gorm:"polymorphic:Owner;"`
  5. }
  6. type Dog struct {
  7.   ID   int
  8.   Name string
  9.   Toy  Toy `gorm:"polymorphic:Owner;"`
  10. }
  11. type Toy struct {
  12.   ID        int
  13.   Name      string
  14.   OwnerID   int
  15.   OwnerType string
  16. }
  17. db.Create(&Dog{Name: "dog1", Toy: Toy{Name: "toy1"}})
  18. // INSERT INTO `dogs` (`name`) VALUES ("dog1")
  19. // INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs")
复制代码
您可以使用标签 polymorphicValue 来更改多态类型的值,例如:
  1. type Dog struct {
  2.   ID   int
  3.   Name string
  4.   Toy  Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
  5. }
  6. type Toy struct {
  7.   ID        int
  8.   Name      string
  9.   OwnerID   int
  10.   OwnerType string
  11. }
  12. db.Create(&Dog{Name: "dog1", Toy: Toy{Name: "toy1"}})
  13. // INSERT INTO `dogs` (`name`) VALUES ("dog1")
  14. // INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master")
复制代码
2.5 Has One 的 CURD

查看 关联模式 获取 has one 相关的用法
2.6 预加载

GORM 可以通过 Preload、Joins 预加载 has one 关联的记录,查看 预加载 获取详情
2.7 自引用 Has One
  1. type User struct {
  2.   gorm.Model
  3.   Name      string
  4.   ManagerID *uint
  5.   Manager   *User
  6. }
复制代码
2.8 外键约束

你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建,例如:
  1. type User struct {
  2.   gorm.Model
  3.   CreditCard CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
  4. }
  5. type CreditCard struct {
  6.   gorm.Model
  7.   Number string
  8.   UserID uint
  9. }
复制代码
你也可以在删除记录时通过 Select 来删除关联的记录,查看 Delete with Select 获取详情
三、Has Many

3.1 Has Many

has many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。
例如,您的应用包含 user 和 credit card 模型,且每个 user 可以有多张 credit card。
  1. // User 有多张 CreditCard,UserID 是外键
  2. type User struct {
  3.   gorm.Model
  4.   CreditCards []CreditCard
  5. }
  6. type CreditCard struct {
  7.   gorm.Model
  8.   Number string
  9.   UserID uint
  10. }
复制代码
3.2 重写外键

要定义 has many 关系,同样必须存在外键。 默认的外键名是拥有者的类型名加上其主键字段名
例如,要定义一个属于 User 的模型,则其外键应该是 UserID。
此外,想要使用另一个字段作为外键,您可以使用 foreignKey 标签自定义它:
  1. type User struct {
  2.   gorm.Model
  3.   CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
  4. }
  5. type CreditCard struct {
  6.   gorm.Model
  7.   Number    string
  8.   UserRefer uint
  9. }
复制代码
3.3 重写引用

GORM 通常使用拥有者的主键作为外键的值。 对于上面的例子,它是 User 的 ID 字段。
为 user 添加 credit card 时,GORM 会将 user 的 ID 字段保存到 credit card 的 UserID 字段。
同样的,您也可以使用标签 references 来更改它,例如:
  1. type User struct {
  2.   gorm.Model
  3.   MemberNumber string
  4.   CreditCards  []CreditCard `gorm:"foreignKey:UserNumber;references:MemberNumber"`
  5. }
  6. type CreditCard struct {
  7.   gorm.Model
  8.   Number     string
  9.   UserNumber string
  10. }
复制代码
3.4 多态关联

GORM 为 has one 和 has many 提供了多态关联支持,它会将拥有者实体的表名、主键都保存到多态类型的字段中。
  1. type Dog struct {
  2.   ID   int
  3.   Name string
  4.   Toys []Toy `gorm:"polymorphic:Owner;"`
  5. }
  6. type Toy struct {
  7.   ID        int
  8.   Name      string
  9.   OwnerID   int
  10.   OwnerType string
  11. }
  12. db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
  13. // INSERT INTO `dogs` (`name`) VALUES ("dog1")
  14. // INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs"), ("toy2","1","dogs")
复制代码
您可以使用标签 polymorphicValue 来更改多态类型的值,例如:
  1. type Dog struct {
  2.   ID   int
  3.   Name string
  4.   Toys []Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
  5. }
  6. type Toy struct {
  7.   ID        int
  8.   Name      string
  9.   OwnerID   int
  10.   OwnerType string
  11. }
  12. db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
  13. // INSERT INTO `dogs` (`name`) VALUES ("dog1")
  14. // INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master"), ("toy2","1","master")
复制代码
3.5 Has Many 的 CURD

查看 关联模式 获取 has many 相关的用法
3.6 预加载

GORM 可以通过 Preload 预加载 has many 关联的记录,查看 预加载 获取详情
3.7自引用 Has Many
  1. type User struct {
  2.   gorm.Model
  3.   Name      string
  4.   ManagerID *uint
  5.   Team      []User `gorm:"foreignkey:ManagerID"`
  6. }
复制代码
3.8 外键约束

你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建,例如:
  1. type User struct {
  2.   gorm.Model
  3.   CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
  4. }
  5. type CreditCard struct {
  6.   gorm.Model
  7.   Number string
  8.   UserID uint
  9. }
复制代码
你也可以在删除记录时通过 Select 来删除 has many 关联的记录,查看 Delete with Select 获取详情
四、Many To Many(多对多)

4.1 Many To Many

Many to Many 会在两个 model 中添加一张连接表。
例如,您的应用包含了 user 和 language,且一个 user 可以说多种 language,多个 user 也可以说一种 language。
  1. // User 拥有并属于多种 language,`user_languages` 是连接表
  2. type User struct {
  3.   gorm.Model
  4.   Languages []Language `gorm:"many2many:user_languages;"`
  5. }
  6. type Language struct {
  7.   gorm.Model
  8.   Name string
  9. }
复制代码
当使用 GORM 的 AutoMigrate 为 User 创建表时,GORM 会自动创建连接表
4.2 反向引用
  1. // User 拥有并属于多种 language,`user_languages` 是连接表
  2. type User struct {
  3.   gorm.Model
  4.   Languages []*Language `gorm:"many2many:user_languages;"`
  5. }
  6. type Language struct {
  7.   gorm.Model
  8.   Name string
  9.   Users []*User `gorm:"many2many:user_languages;"`
  10. }
复制代码
4.3 重写外键

对于 many2many 关系,连接表会同时拥有两个模型的外键,例如:
  1. type User struct {
  2.   gorm.Model
  3.   Languages []Language `gorm:"many2many:user_languages;"`
  4. }
  5. type Language struct {
  6.   gorm.Model
  7.   Name string
  8. }
  9. // 连接表:user_languages
  10. //   foreign key: user_id, reference: users.id
  11. //   foreign key: language_id, reference: languages.id
复制代码
若要重写它们,可以使用标签 foreignKey、references、joinforeignKey、joinReferences。当然,您不需要使用全部的标签,你可以仅使用其中的一个重写部分的外键、引用。
  1. type User struct {
  2.     gorm.Model
  3.     Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;joinReferences:ProfileRefer"`
  4.     Refer    uint      `gorm:"index:,unique"`
  5. }
  6. type Profile struct {
  7.     gorm.Model
  8.     Name      string
  9.     UserRefer uint `gorm:"index:,unique"`
  10. }
  11. // 会创建连接表:user_profiles
  12. //   foreign key: user_refer_id, reference: users.refer
  13. //   foreign key: profile_refer, reference: profiles.user_refer
复制代码
注意: 某些数据库只允许在唯一索引字段上创建外键,如果您在迁移时会创建外键,则需要指定 unique index 标签。
4.4 自引用 Many2Many

自引用 many2many 关系
  1. type User struct {
  2.   gorm.Model
  3.     Friends []*User `gorm:"many2many:user_friends"`
  4. }
  5. // 会创建连接表:user_friends
  6. //   foreign key: user_id, reference: users.id
  7. //   foreign key: friend_id, reference: users.id
复制代码
4.5 预加载

GORM 可以通过 Preload 预加载 has many 关联的记录,查看 预加载 获取详情
4.6 Many2Many 的 CURD

查看 关联模式 获取 many2many 相关的用法
4.7 自3定义连接表

连接表 可以是一个全功能的模型,支持 Soft Delete、钩子、更多的字段,就跟其它模型一样。您可以通过 SetupJoinTable 指定它,例如:
注意: 自定义连接表要求外键是复合主键或复合唯一索引
  1. type Person struct {
  2.   ID        int
  3.   Name      string
  4.   Addresses []Address `gorm:"many2many:person_addresses;"`
  5. }
  6. type Address struct {
  7.   ID   uint
  8.   Name string
  9. }
  10. type PersonAddress struct {
  11.   PersonID  int `gorm:"primaryKey"`
  12.   AddressID int `gorm:"primaryKey"`
  13.   CreatedAt time.Time
  14.   DeletedAt gorm.DeletedAt
  15. }
  16. func (PersonAddress) BeforeCreate(db *gorm.DB) error {
  17.   // ...
  18. }
  19. // 修改 Person 的 Addresses 字段的连接表为 PersonAddress
  20. // PersonAddress 必须定义好所需的外键,否则会报错
  21. err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
复制代码
4.8 外键约束

你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建,例如:
  1. type User struct {
  2.   gorm.Model
  3.   Languages []Language `gorm:"many2many:user_speaks;"`
  4. }
  5. type Language struct {
  6.   Code string `gorm:"primarykey"`
  7.   Name string
  8. }
  9. // 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.9 复合外键

如果您的模型使用了 复合主键,GORM 会默认启用复合外键。
您也可以覆盖默认的外键、指定多个外键,只需用逗号分隔那些键名,例如:
  1. type Tag struct {
  2.   ID     uint   `gorm:"primaryKey"`
  3.   Locale string `gorm:"primaryKey"`
  4.   Value  string
  5. }
  6. type Blog struct {
  7.   ID         uint   `gorm:"primaryKey"`
  8.   Locale     string `gorm:"primaryKey"`
  9.   Subject    string
  10.   Body       string
  11.   Tags       []Tag `gorm:"many2many:blog_tags;"`
  12.   LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
  13.   SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
  14. }
  15. // 连接表:blog_tags
  16. //   foreign key: blog_id, reference: blogs.id
  17. //   foreign key: blog_locale, reference: blogs.locale
  18. //   foreign key: tag_id, reference: tags.id
  19. //   foreign key: tag_locale, reference: tags.locale
  20. // 连接表:locale_blog_tags
  21. //   foreign key: blog_id, reference: blogs.id
  22. //   foreign key: blog_locale, reference: blogs.locale
  23. //   foreign key: tag_id, reference: tags.id
  24. // 连接表:shared_blog_tags
  25. //   foreign key: blog_id, reference: blogs.id
  26. //   foreign key: tag_id, reference: tags.id
复制代码
查看 复合主键 获取详情
五、实体关联

5.1 自动创建、更新

在创建、更新记录时,GORM 会通过 Upsert 自动保存关联及其引用记录。
  1. user := User{
  2.   Name:            "jinzhu",
  3.   BillingAddress:  Address{Address1: "Billing Address - Address 1"},
  4.   ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
  5.   Emails:          []Email{
  6.     {Email: "jinzhu@example.com"},
  7.     {Email: "jinzhu-2@example.com"},
  8.   },
  9.   Languages:       []Language{
  10.     {Name: "ZH"},
  11.     {Name: "EN"},
  12.   },
  13. }
  14. db.Create(&user)
  15. // BEGIN TRANSACTION;
  16. // INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY DO NOTHING;
  17. // INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
  18. // INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY DO NOTHING;
  19. // INSERT INTO "languages" ("name") VALUES ('ZH'), ('EN') ON DUPLICATE KEY DO NOTHING;
  20. // INSERT INTO "user_languages" ("user_id","language_id") VALUES (111, 1), (111, 2) ON DUPLICATE KEY DO NOTHING;
  21. // COMMIT;
  22. db.Save(&user)
复制代码
如果您想要更新关联的数据,您应该使用 FullSaveAssociations 模式:
  1. db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
  2. // ...
  3. // INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY SET address1=VALUES(address1);
  4. // INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
  5. // INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY SET email=VALUES(email);
  6. // ...
复制代码
5.2 跳过自动创建、更新

若要在创建、更新时跳过自动保存,您可以使用 Select 或 Omit,例如:
  1. user := User{
  2.   Name:            "jinzhu",
  3.   BillingAddress:  Address{Address1: "Billing Address - Address 1"},
  4.   ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
  5.   Emails:          []Email{
  6.     {Email: "jinzhu@example.com"},
  7.     {Email: "jinzhu-2@example.com"},
  8.   },
  9.   Languages:       []Language{
  10.     {Name: "ZH"},
  11.     {Name: "EN"},
  12.   },
  13. }
  14. db.Select("Name").Create(&user)
  15. // INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2);
  16. db.Omit("BillingAddress").Create(&user)
  17. // Skip create BillingAddress when creating a user
  18. db.Omit(clause.Associations).Create(&user)
  19. // Skip all associations when creating a user
复制代码
NOTE: 对于 many2many 关联,GORM 在创建连接表引用之前,会先 upsert 关联。如果你想跳过关联的 upsert,你可以这样做:
  1. db.Omit("Languages.*").Create(&user)
复制代码
下面的代码将跳过创建关联及其引用
  1. db.Omit("Languages").Create(&user)
复制代码
5.3 Select/Omit 关联字段
  1. user := User{
  2.   Name:            "jinzhu",
  3.   BillingAddress:  Address{Address1: "Billing Address - Address 1", Address2: "addr2"},
  4.   ShippingAddress: Address{Address1: "Shipping Address - Address 1", Address2: "addr2"},
  5. }
  6. // 创建 user 及其 BillingAddress、ShippingAddress
  7. // 在创建 BillingAddress 时,仅使用其 address1、address2 字段,忽略其它字段
  8. db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user)
  9. db.Omit("BillingAddress.Address2", "BillingAddress.CreatedAt").Create(&user)
复制代码
5.4 关联模式

关联模式包含一些在处理关系时有用的方法
  1. // 开始关联模式
  2. var user User
  3. db.Model(&user).Association("Languages")
  4. // `user` 是源模型,它的主键不能为空
  5. // 关系的字段名是 `Languages`
  6. // 如果匹配了上面两个要求,会开始关联模式,否则会返回错误
  7. db.Model(&user).Association("Languages").Error
复制代码
5.4.1 查找关联

查找所有匹配的关联记录
  1. db.Model(&user).Association("Languages").Find(&languages)
复制代码
查找带条件的关联
  1. codes := []string{"zh-CN", "en-US", "ja-JP"}
  2. db.Model(&user).Where("code IN ?", codes).Association("Languages").Find(&languages)
  3. db.Model(&user).Where("code IN ?", codes).Order("code desc").Association("Languages").Find(&languages)
复制代码
5.4.2 添加关联

为 many to many、has many 添加新的关联;为 has one, belongs to 替换当前的关联
  1. db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
  2. db.Model(&user).Association("Languages").Append(&Language{Name: "DE"})
  3. db.Model(&user).Association("CreditCard").Append(&CreditCard{Number: "411111111111"})
复制代码
5.4.3 替换关联

用一个新的关联替换当前的关联
  1. db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})
  2. db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)
复制代码
5.4.4 删除关联

如果存在,则删除源模型与参数之间的关系,只会删除引用,不会从数据库中删除这些对象。
  1. db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
  2. db.Model(&user).Association("Languages").Delete(languageZH, languageEN)
复制代码
5.4.5 清空关联

删除源模型与关联之间的所有引用,但不会删除这些关联
  1. db.Model(&user).Association("Languages").Clear()
复制代码
5.4.6 关联计数

返回当前关联的计数
  1. db.Model(&user).Association("Languages").Count()
  2. // 条件计数
  3. codes := []string{"zh-CN", "en-US", "ja-JP"}
  4. db.Model(&user).Where("code IN ?", codes).Association("Languages").Count()
复制代码
5.4.7 批量处理数据

关联模式也支持批量处理,例如:
  1. // 查询所有用户的所有角色
  2. db.Model(&users).Association("Role").Find(&roles)
  3. // 从所有 team 中删除 user A
  4. db.Model(&users).Association("Team").Delete(&userA)
  5. // 获取去重的用户所属 team 数量
  6. db.Model(&users).Association("Team").Count()
  7. // 对于批量数据的 `Append`、`Replace`,参数的长度必须与数据的长度相同,否则会返回 error
  8. var users = []User{user1, user2, user3}
  9. // 例如:现在有三个 user,Append userA 到 user1 的 team,Append userB 到 user2 的 team,Append userA、userB 和 userC 到 user3 的 team
  10. db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC})
  11. // 重置 user1 team 为 userA,重置 user2 的 team 为 userB,重置 user3 的 team 为 userA、 userB 和 userC
  12. db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})
复制代码
5.5 带 Select 的删除

你可以在删除记录时通过 Select 来删除具有 has one、has many、many2many 关系的记录,例如:
  1. // 删除 user 时,也删除 user 的 account
  2. db.Select("Account").Delete(&user)
  3. // 删除 user 时,也删除 user 的 Orders、CreditCards 记录
  4. db.Select("Orders", "CreditCards").Delete(&user)
  5. // 删除 user 时,也删除用户所有 has one/many、many2many 记录
  6. db.Select(clause.Associations).Delete(&user)
  7. // 删除 users 时,也删除每一个 user 的 account
  8. db.Select("Account").Delete(&users)
复制代码
注意: 只有当记录的主键不为空时,关联才会被删除,GORM 会使用这些主键作为条件来删除关联记录
  1. // DOESN'T WORK
  2. db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{})
  3. // 会删除所有 name=`jinzhu` 的 user,但这些 user 的 account 不会被删除
  4. db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{ID: 1})
  5. // 会删除 name = `jinzhu` 且 id = `1` 的 user,并且 user `1` 的 account 也会被删除
  6. db.Select("Account").Delete(&User{ID: 1})
  7. // 会删除 id = `1` 的 user,并且 user `1` 的 account 也会被删除
复制代码
5.6 关联标签(tag)

标签描述foreignKey指定当前模型的列作为连接表的外键references指定引用表的列名,其将被映射为连接表外键polymorphic指定多态类型,比如模型名polymorphicValue指定多态值、默认表名many2many指定连接表表名joinForeignKey指定连接表的外键列名,其将被映射到当前表joinReferences指定连接表的外键列名,其将被映射到引用表constraint关系约束,例如:OnUpdate、OnDelete六 预加载

6.1 预加载

GORM 允许在 Preload 的其它 SQL 中直接加载关系,例如:
  1. type User struct {
  2.   gorm.Model
  3.   Username string
  4.   Orders   []Order
  5. }
  6. type Order struct {
  7.   gorm.Model
  8.   UserID uint
  9.   Price  float64
  10. }
  11. // 查找 user 时预加载相关 Order
  12. db.Preload("Orders").Find(&users)
  13. // SELECT * FROM users;
  14. // SELECT * FROM orders WHERE user_id IN (1,2,3,4);
  15. db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
  16. // SELECT * FROM users;
  17. // SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
  18. // SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
  19. // SELECT * FROM roles WHERE id IN (4,5,6); // belongs to
复制代码
6.2 Joins 预加载

Preload 在一个单独查询中加载关联数据。而 Join Preload 会使用 inner join 加载关联数据,例如:
  1. db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
  2. db.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
  3. db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})
复制代码
带条件的 Join
  1. db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)
  2. // 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, belongs to
6.3 预加载全部

与创建、更新时使用 Select 类似,clause.Associations 也可以和 Preload 一起使用,它可以用来 预加载 全部关联,例如:
  1. type User struct {
  2.   gorm.Model
  3.   Name       string
  4.   CompanyID  uint
  5.   Company    Company
  6.   Role       Role
  7.   Orders     []Order
  8. }
  9. db.Preload(clause.Associations).Find(&users)
复制代码
clause.Associations 不会预加载嵌套的关联,但你可以使用嵌套预加载 例如:
  1. db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)
复制代码
6.4 带条件的预加载

GORM 允许带条件的 Preload 关联,类似于内联条件
  1. // 带条件的预加载 Order
  2. db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
  3. // SELECT * FROM users;
  4. // SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
  5. db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
  6. // SELECT * FROM users WHERE state = 'active';
  7. // SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');
复制代码
6.5 自定义预加载 SQL

您可以通过 func(db *gorm.DB) *gorm.DB 实现自定义预加载 SQL,例如:
  1. db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
  2.   return db.Order("orders.amount DESC")
  3. }).Find(&users)
  4. // SELECT * FROM users;
  5. // SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;
复制代码
6.6 嵌套预加载

GORM 支持嵌套预加载,例如:
  1. db.Preload("Orders.OrderItems.Product").Preload("CreditCard").Find(&users)
  2. // 自定义预加载 `Orders` 的条件
  3. // 这样,GORM 就不会加载不匹配的 order 记录
  4. db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)
复制代码
本文由博客一文多发平台 OpenWrite 发布!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4