Gorm 關(guān)聯(lián)關(guān)系介紹與基本使用

一 Belongs To(一對一)

1.1 Belongs To

belongs to 會與另一個模型建立了一對一的連接桨踪。 這種模型的每一個實例都“屬于”另一個模型的一個實例。

例如绝葡,您的應(yīng)用包含 user 和 company薄辅,并且每個 user 能且只能被分配給一個 company糙置。下面的類型就表示這種關(guān)系琢蛤。 注意,在 User 對象中绿淋,有一個和 Company 一樣的 CompanyID。 默認(rèn)情況下尝盼, CompanyID 被隱含地用來在 UserCompany 之間創(chuàng)建一個外鍵關(guān)系吞滞, 因此必須包含在 User 結(jié)構(gòu)體中才能填充 Company 內(nèi)部結(jié)構(gòu)體。

// `User` 屬于 `Company`,`CompanyID` 是外鍵
type User struct {
  gorm.Model
  Name      string
  CompanyID int
  Company   Company
}

type Company struct {
  ID   int
  Name string
}

db.AutoMigrate(&User{},&Company{})

請參閱 預(yù)加載 以了解內(nèi)部結(jié)構(gòu)的詳細(xì)信息裁赠。

1.2 重寫外鍵

要定義一個 belongs to 關(guān)系殿漠,數(shù)據(jù)庫的表中必須存在外鍵。默認(rèn)情況下佩捞,外鍵的名字绞幌,使用擁有者的類型名稱加上表的主鍵的字段名字

例如,定義一個User實體屬于Company實體一忱,那么外鍵的名字一般使用CompanyID莲蜘。

GORM同時提供自定義外鍵名字的方式,如下例所示帘营。

type User struct {
  gorm.Model
  Name         string
  CompanyRefer int // 外鍵改名字為CompanyRefer
  Company      Company `gorm:"foreignKey:CompanyRefer"`
  // 使用 CompanyRefer 作為外鍵
}

type Company struct {
  ID   int
  Name string
}

1.3 重寫引用(一般不用)

對于 belongs to 關(guān)系票渠,GORM 通常使用數(shù)據(jù)庫表,主表(擁有者)的主鍵值作為外鍵參考芬迄。 正如上面的例子问顷,我們使用主表Company中的主鍵字段ID作為外鍵的參考值。

如果在Company實體中設(shè)置了User實體禀梳,那么GORM會自動把Company中的ID屬性保存到User的CompanyID屬性中杜窄。

同樣的,您也可以使用標(biāo)簽 references 來更改它算途,例如:

type User struct {
  gorm.Model
  Name      string
  CompanyCode int
  Company   Company `gorm:"foreignKey:CompanyCode;references:Code"` // 指定外鍵字段為CompanyCode羞芍,指定與公司表中Code字段關(guān)聯(lián)
}

type Company struct {
  ID   int
  Code int `gorm:"primarykey"`
  Name string
}

1.4 Belongs to 的 CRUD

點擊 關(guān)聯(lián)模式 鏈接獲取 belongs to 相關(guān)的用法

1.5 預(yù)加載

GORM允許通過使用Preload或者Joins來主動加載實體的關(guān)聯(lián)關(guān)系,具體內(nèi)容請參考郊艘,預(yù)加載(主動加載)

1.6 外鍵約束

你可以通過OnUpdate, OnDelete配置標(biāo)簽來增加關(guān)聯(lián)關(guān)系的級聯(lián)操作,如下面的例子唯咬,通過GORM可以完成用戶和公司的級聯(lián)更新和級聯(lián)刪除操作:

type User struct {
    gorm.Model
    Name      string
    Age int
    UserDetailID int
    UserDetail   UserDetail `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` // 級聯(lián)更新纱注,刪除時置空
}

type UserDetail struct {
    ID   int
    Addr string
}

// Navicat的設(shè)計表中可以查看

二、Has One

2.1 Has One

has one 與另一個模型建立一對一的關(guān)聯(lián)胆胰,但它和一對一關(guān)系有些許不同狞贱。 這種關(guān)聯(lián)表明一個模型的每個實例都包含或擁有另一個模型的一個實例。

例如蜀涨,您的應(yīng)用包含 user 和 credit card 模型瞎嬉,且每個 user 只能有一張 credit card。

// User 有一張 CreditCard厚柳,UserID 是外鍵
type User struct {
    gorm.Model
    CreditCard CreditCard // 與CreditCard表有關(guān)聯(lián)關(guān)系
}

type CreditCard struct {
    gorm.Model
    Number string
    UserID uint  //關(guān)聯(lián)字段在此表
}

2.2 重寫外鍵

對于 has one 關(guān)系氧枣,同樣必須存在外鍵字段。擁有者將把屬于它的模型的主鍵保存到這個字段别垮。

這個字段的名稱通常由 has one 模型的類型加上其 主鍵 生成便监,對于上面的例子,它是 UserID

為 user 添加 credit card 時烧董,它會將 user 的 ID 保存到自己的 UserID 字段毁靶。

如果你想要使用另一個字段來保存該關(guān)系,你同樣可以使用標(biāo)簽 foreignKey 來更改它逊移,例如:

type User struct {
    gorm.Model
    CreditCard CreditCard `gorm:"foreignKey:UserName"`
    // 使用 UserName 作為外鍵
}

type CreditCard struct {
    gorm.Model
    Number   string
    UserName string // 使用 UserName 作為外鍵
}

2.3 重寫引用

默認(rèn)情況下预吆,擁有者實體會將 has one 對應(yīng)模型的主鍵保存為外鍵,您也可以修改它胳泉,用另一個字段來保存拐叉,例如下個這個使用 Name 來保存的例子。

您可以使用標(biāo)簽 references 來更改它胶背,例如:

type User struct {
    gorm.Model
    Name       int     `gorm:"index"`  // name建是索引
    CreditCard CreditCard `gorm:"foreignkey:UserName;references:name"`  // 信用卡表的UserName字段跟用戶表Name字段關(guān)聯(lián)
}

type CreditCard struct {
    gorm.Model
    Number   string
    UserName int
}

2.4 多態(tài)關(guān)聯(lián)

GORM 為 has onehas many 提供了多態(tài)關(guān)聯(lián)支持巷嚣,它會將擁有者實體的表名、主鍵值都保存到多態(tài)類型的字段中钳吟。

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")

您可以使用標(biāo)簽 polymorphicValue 來更改多態(tài)類型的值廷粒,例如:

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.5 Has One 的 CURD

查看 關(guān)聯(lián)模式 獲取 has one 相關(guān)的用法

2.6 預(yù)加載

GORM 可以通過 PreloadJoins 預(yù)加載 has one 關(guān)聯(lián)的記錄红且,查看 預(yù)加載 獲取詳情

2.7 自引用 Has One

type User struct {
  gorm.Model
  Name      string
  ManagerID *uint
  Manager   *User
}

2.8 外鍵約束

你可以通過為標(biāo)簽 constraint 配置 OnUpdate坝茎、OnDelete 實現(xiàn)外鍵約束,在使用 GORM 進(jìn)行遷移時它會被創(chuàng)建暇番,例如:

type User struct {
  gorm.Model
  CreditCard CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}

type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}

你也可以在刪除記錄時通過 Select 來刪除關(guān)聯(lián)的記錄嗤放,查看 Delete with Select 獲取詳情

三、Has Many

3.1 Has Many

has many 與另一個模型建立了一對多的連接壁酬。 不同于 has one次酌,擁有者可以有零或多個關(guān)聯(lián)模型。

例如舆乔,您的應(yīng)用包含 user 和 credit card 模型岳服,且每個 user 可以有多張 credit card。

// User 有多張 CreditCard希俩,UserID 是外鍵
type User struct {
  gorm.Model
  CreditCards []CreditCard
}

type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}

3.2 重寫外鍵

要定義 has many 關(guān)系吊宋,同樣必須存在外鍵。 默認(rèn)的外鍵名是擁有者的類型名加上其主鍵字段名

例如颜武,要定義一個屬于 User 的模型璃搜,則其外鍵應(yīng)該是 UserID

此外鳞上,想要使用另一個字段作為外鍵这吻,您可以使用 foreignKey 標(biāo)簽自定義它:

type User struct {
  gorm.Model
  CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}

type CreditCard struct {
  gorm.Model
  Number    string
  UserRefer uint
}

3.3 重寫引用

GORM 通常使用擁有者的主鍵作為外鍵的值。 對于上面的例子因块,它是 UserID 字段橘原。

為 user 添加 credit card 時,GORM 會將 user 的 ID 字段保存到 credit card 的 UserID 字段。

同樣的趾断,您也可以使用標(biāo)簽 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.4 多態(tài)關(guān)聯(lián)

GORM 為 has onehas many 提供了多態(tài)關(guān)聯(lián)支持,它會將擁有者實體的表名芋酌、主鍵都保存到多態(tài)類型的字段中增显。

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")

您可以使用標(biāo)簽 polymorphicValue 來更改多態(tài)類型的值,例如:

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.5 Has Many 的 CURD

查看 關(guān)聯(lián)模式 獲取 has many 相關(guān)的用法

3.6 預(yù)加載

GORM 可以通過 Preload 預(yù)加載 has many 關(guān)聯(lián)的記錄脐帝,查看 預(yù)加載 獲取詳情

3.7自引用 Has Many

type User struct {
  gorm.Model
  Name      string
  ManagerID *uint
  Team      []User `gorm:"foreignkey:ManagerID"`
}

3.8 外鍵約束

你可以通過為標(biāo)簽 constraint 配置 OnUpdate同云、OnDelete 實現(xiàn)外鍵約束,在使用 GORM 進(jìn)行遷移時它會被創(chuàng)建堵腹,例如:

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 關(guān)聯(lián)的記錄炸站,查看 Delete with Select 獲取詳情

四、Many To Many(多對多)

4.1 Many To Many

Many to Many 會在兩個 model 中添加一張連接表疚顷。

例如旱易,您的應(yīng)用包含了 user 和 language,且一個 user 可以說多種 language腿堤,多個 user 也可以說一種 language阀坏。

// User 擁有并屬于多種 language,`user_languages` 是連接表
type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}

當(dāng)使用 GORM 的 AutoMigrateUser 創(chuàng)建表時笆檀,GORM 會自動創(chuàng)建連接表

4.2 反向引用

// User 擁有并屬于多種 language忌堂,`user_languages` 是連接表
type User struct {
  gorm.Model
  Languages []*Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
  Users []*User `gorm:"many2many:user_languages;"`
}

4.3 重寫外鍵

對于 many2many 關(guān)系,連接表會同時擁有兩個模型的外鍵酗洒,例如:

type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}

// 連接表:user_languages
//   foreign key: user_id, reference: users.id
//   foreign key: language_id, reference: languages.id

若要重寫它們士修,可以使用標(biāo)簽 foreignKeyreferences樱衷、joinforeignKey李命、joinReferences。當(dāng)然箫老,您不需要使用全部的標(biāo)簽,你可以僅使用其中的一個重寫部分的外鍵黔州、引用耍鬓。

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"`
}

// 會創(chuàng)建連接表:user_profiles
//   foreign key: user_refer_id, reference: users.refer
//   foreign key: profile_refer, reference: profiles.user_refer

注意: 某些數(shù)據(jù)庫只允許在唯一索引字段上創(chuàng)建外鍵,如果您在遷移時會創(chuàng)建外鍵流妻,則需要指定 unique index 標(biāo)簽牲蜀。

4.4 自引用 Many2Many

自引用 many2many 關(guān)系

type User struct {
  gorm.Model
    Friends []*User `gorm:"many2many:user_friends"`
}

// 會創(chuàng)建連接表:user_friends
//   foreign key: user_id, reference: users.id
//   foreign key: friend_id, reference: users.id

4.5 預(yù)加載

GORM 可以通過 Preload 預(yù)加載 has many 關(guān)聯(lián)的記錄,查看 預(yù)加載 獲取詳情

4.6 Many2Many 的 CURD

查看 關(guān)聯(lián)模式 獲取 many2many 相關(guān)的用法

4.7 自3定義連接表

連接表 可以是一個全功能的模型绅这,支持 Soft Delete涣达、鉤子、更多的字段,就跟其它模型一樣度苔。您可以通過 SetupJoinTable 指定它匆篓,例如:

注意: 自定義連接表要求外鍵是復(fù)合主鍵或復(fù)合唯一索引

type Person struct {
  ID        int
  Name      string
  Addresses []Address `gorm:"many2many:person_addresses;"`
}

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 {
  // ...
}

// 修改 Person 的 Addresses 字段的連接表為 PersonAddress
// PersonAddress 必須定義好所需的外鍵,否則會報錯
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})

4.8 外鍵約束

你可以通過為標(biāo)簽 constraint 配置 OnUpdate寇窑、OnDelete 實現(xiàn)外鍵約束鸦概,在使用 GORM 進(jìn)行遷移時它會被創(chuàng)建,例如:

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 關(guān)系的記錄甩骏,查看 Delete with Select 獲取詳情

4.9 復(fù)合外鍵

如果您的模型使用了 復(fù)合主鍵窗市,GORM 會默認(rèn)啟用復(fù)合外鍵。

您也可以覆蓋默認(rèn)的外鍵饮笛、指定多個外鍵咨察,只需用逗號分隔那些鍵名,例如:

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"`
}

// 連接表: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

// 連接表:locale_blog_tags
//   foreign key: blog_id, reference: blogs.id
//   foreign key: blog_locale, reference: blogs.locale
//   foreign key: tag_id, reference: tags.id

// 連接表:shared_blog_tags
//   foreign key: blog_id, reference: blogs.id
//   foreign key: tag_id, reference: tags.id

查看 復(fù)合主鍵 獲取詳情

五福青、實體關(guān)聯(lián)

5.1 自動創(chuàng)建摄狱、更新

在創(chuàng)建、更新記錄時素跺,GORM 會通過 Upsert 自動保存關(guān)聯(lián)及其引用記錄二蓝。

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)

如果您想要更新關(guān)聯(lián)的數(shù)據(jù),您應(yīng)該使用 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 跳過自動創(chuàng)建指厌、更新

若要在創(chuàng)建刊愚、更新時跳過自動保存,您可以使用 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

NOTE: 對于 many2many 關(guān)聯(lián)鸥诽,GORM 在創(chuàng)建連接表引用之前,會先 upsert 關(guān)聯(lián)箕憾。如果你想跳過關(guān)聯(lián)的 upsert牡借,你可以這樣做:

db.Omit("Languages.*").Create(&user)

下面的代碼將跳過創(chuàng)建關(guān)聯(lián)及其引用

db.Omit("Languages").Create(&user)

5.3 Select/Omit 關(guān)聯(lián)字段

user := User{
  Name:            "jinzhu",
  BillingAddress:  Address{Address1: "Billing Address - Address 1", Address2: "addr2"},
  ShippingAddress: Address{Address1: "Shipping Address - Address 1", Address2: "addr2"},
}

// 創(chuàng)建 user 及其 BillingAddress、ShippingAddress
// 在創(chuàng)建 BillingAddress 時袭异,僅使用其 address1钠龙、address2 字段,忽略其它字段
db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user)

db.Omit("BillingAddress.Address2", "BillingAddress.CreatedAt").Create(&user)

5.4 關(guān)聯(lián)模式

關(guān)聯(lián)模式包含一些在處理關(guān)系時有用的方法

// 開始關(guān)聯(lián)模式
var user User
db.Model(&user).Association("Languages")
// `user` 是源模型御铃,它的主鍵不能為空
// 關(guān)系的字段名是 `Languages`
// 如果匹配了上面兩個要求碴里,會開始關(guān)聯(lián)模式,否則會返回錯誤
db.Model(&user).Association("Languages").Error

5.4.1 查找關(guān)聯(lián)

查找所有匹配的關(guān)聯(lián)記錄

db.Model(&user).Association("Languages").Find(&languages)

查找?guī)l件的關(guān)聯(lián)

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 添加關(guān)聯(lián)

many to many上真、has many 添加新的關(guān)聯(lián)咬腋;為 has one, belongs to 替換當(dāng)前的關(guān)聯(lián)

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 替換關(guān)聯(lián)

用一個新的關(guān)聯(lián)替換當(dāng)前的關(guān)聯(lián)

db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})

db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)

5.4.4 刪除關(guān)聯(lián)

如果存在,則刪除源模型與參數(shù)之間的關(guān)系睡互,只會刪除引用根竿,不會從數(shù)據(jù)庫中刪除這些對象陵像。

db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Delete(languageZH, languageEN)

5.4.5 清空關(guān)聯(lián)

刪除源模型與關(guān)聯(lián)之間的所有引用,但不會刪除這些關(guān)聯(lián)

db.Model(&user).Association("Languages").Clear()

5.4.6 關(guān)聯(lián)計數(shù)

返回當(dāng)前關(guān)聯(lián)的計數(shù)

db.Model(&user).Association("Languages").Count()

// 條件計數(shù)
codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Count()

5.4.7 批量處理數(shù)據(jù)

關(guān)聯(lián)模式也支持批量處理寇壳,例如:

// 查詢所有用戶的所有角色
db.Model(&users).Association("Role").Find(&roles)

// 從所有 team 中刪除 user A
db.Model(&users).Association("Team").Delete(&userA)

// 獲取去重的用戶所屬 team 數(shù)量
db.Model(&users).Association("Team").Count()

// 對于批量數(shù)據(jù)的 `Append`醒颖、`Replace`,參數(shù)的長度必須與數(shù)據(jù)的長度相同九巡,否則會返回 error
var users = []User{user1, user2, user3}
// 例如:現(xiàn)在有三個 user图贸,Append userA 到 user1 的 team,Append userB 到 user2 的 team冕广,Append userA疏日、userB 和 userC 到 user3 的 team
db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC})
// 重置 user1 team 為 userA,重置 user2 的 team 為 userB撒汉,重置 user3 的 team 為 userA沟优、 userB 和 userC
db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})

5.5 帶 Select 的刪除

你可以在刪除記錄時通過 Select 來刪除具有 has one、has many睬辐、many2many 關(guān)系的記錄挠阁,例如:

// 刪除 user 時,也刪除 user 的 account
db.Select("Account").Delete(&user)

// 刪除 user 時溯饵,也刪除 user 的 Orders侵俗、CreditCards 記錄
db.Select("Orders", "CreditCards").Delete(&user)

// 刪除 user 時,也刪除用戶所有 has one/many丰刊、many2many 記錄
db.Select(clause.Associations).Delete(&user)

// 刪除 users 時隘谣,也刪除每一個 user 的 account
db.Select("Account").Delete(&users)

注意: 只有當(dāng)記錄的主鍵不為空時,關(guān)聯(lián)才會被刪除啄巧,GORM 會使用這些主鍵作為條件來刪除關(guān)聯(lián)記錄

// DOESN'T WORK
db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{})
// 會刪除所有 name=`jinzhu` 的 user寻歧,但這些 user 的 account 不會被刪除

db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{ID: 1})
// 會刪除 name = `jinzhu` 且 id = `1` 的 user,并且 user `1` 的 account 也會被刪除

db.Select("Account").Delete(&User{ID: 1})
// 會刪除 id = `1` 的 user秩仆,并且 user `1` 的 account 也會被刪除

5.6 關(guān)聯(lián)標(biāo)簽(tag)

標(biāo)簽 描述
foreignKey 指定當(dāng)前模型的列作為連接表的外鍵
references 指定引用表的列名码泛,其將被映射為連接表外鍵
polymorphic 指定多態(tài)類型,比如模型名
polymorphicValue 指定多態(tài)值澄耍、默認(rèn)表名
many2many 指定連接表表名
joinForeignKey 指定連接表的外鍵列名噪珊,其將被映射到當(dāng)前表
joinReferences 指定連接表的外鍵列名,其將被映射到引用表
constraint 關(guān)系約束齐莲,例如:OnUpdate卿城、OnDelete

六 預(yù)加載

6.1 預(yù)加載

GORM 允許在 Preload 的其它 SQL 中直接加載關(guān)系,例如:

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

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

// 查找 user 時預(yù)加載相關(guān) Order
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 預(yù)加載

Preload 在一個單獨查詢中加載關(guān)聯(lián)數(shù)據(jù)铅搓。而 Join Preload 會使用 inner join 加載關(guān)聯(lián)數(shù)據(jù),例如:

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

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 適用于一對一的關(guān)系搀捷,例如: has one, belongs to

6.3 預(yù)加載全部

與創(chuàng)建星掰、更新時使用 Select 類似多望,clause.Associations 也可以和 Preload 一起使用,它可以用來 預(yù)加載 全部關(guān)聯(lián)氢烘,例如:

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

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

clause.Associations 不會預(yù)加載嵌套的關(guān)聯(lián)怀偷,但你可以使用嵌套預(yù)加載 例如:

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

6.4 帶條件的預(yù)加載

GORM 允許帶條件的 Preload 關(guān)聯(lián),類似于內(nèi)聯(lián)條件

// 帶條件的預(yù)加載 Order
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 自定義預(yù)加載 SQL

您可以通過 func(db *gorm.DB) *gorm.DB 實現(xiàn)自定義預(yù)加載 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;

6.6 嵌套預(yù)加載

GORM 支持嵌套預(yù)加載椎工,例如:

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

// 自定義預(yù)加載 `Orders` 的條件
// 這樣,GORM 就不會加載不匹配的 order 記錄
db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜀踏,一起剝皮案震驚了整個濱河市维蒙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌果覆,老刑警劉巖颅痊,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異局待,居然都是意外死亡斑响,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門钳榨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舰罚,“玉大人,你說我怎么就攤上這事薛耻∮眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵昭卓,是天一觀的道長愤钾。 經(jīng)常有香客問我,道長候醒,這世上最難降的妖魔是什么能颁? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮倒淫,結(jié)果婚禮上伙菊,老公的妹妹穿的比我還像新娘。我一直安慰自己敌土,他們只是感情好镜硕,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著返干,像睡著了一般兴枯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矩欠,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天财剖,我揣著相機(jī)與錄音悠夯,去河邊找鬼。 笑死躺坟,一個胖子當(dāng)著我的面吹牛沦补,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咪橙,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼夕膀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了美侦?” 一聲冷哼從身側(cè)響起产舞,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎音榜,沒想到半個月后庞瘸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡赠叼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年擦囊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘴办。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞬场,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涧郊,到底是詐尸還是另有隱情贯被,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布妆艘,位于F島的核電站彤灶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏批旺。R本人自食惡果不足惜幌陕,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汽煮。 院中可真熱鬧搏熄,春花似錦、人聲如沸暇赤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞋囊。三九已至止后,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間溜腐,已是汗流浹背译株。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工微饥, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人古戴。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像矩肩,于是被迫代替她去往敵國和親现恼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容