Gorm中文文档

关联模式

Preview
  • 自动创建、更新
  • 跳过自动创建、更新
  • Select/Omit 关联字段
  • 关联模式
  • 查找关联
  • 添加关联
  • 替换关联
  • 删除关联
  • 清空关联
  • 关联计数
  • 批量处理数据
  • Delete Association Record
  • Delete with Select
  • Association Tags

自动创建、更新

在创建、更新记录时,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);
// ...

跳过自动创建、更新

若要在创建、更新时跳过自动保存,您可以使用 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 关联,GORM 在创建连接表引用之前,会先 upsert 关联。如果你想跳过关联的 upsert,你可以这样做:

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

下面的代码将跳过创建关联及其引用

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

Select/Omit 关联字段

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

// 创建 user 及其 BillingAddress、ShippingAddress
// 在创建 BillingAddress 时,仅使用其 address1、address2 字段,忽略其它字段
db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user)

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

关联模式

关联模式包含一些在处理关系时有用的方法

// 开始关联模式
var user User
db.Model(&user).Association("Languages")
// `user` 是源模型,它的主键不能为空
// 关系的字段名是 `Languages`
// 如果匹配了上面两个要求,会开始关联模式,否则会返回错误
db.Model(&user).Association("Languages").Error

查找关联

查找所有匹配的关联记录

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)

添加关联

many to manyhas 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"})

替换关联

用一个新的关联替换当前的关联

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

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

删除关联

如果存在,则删除源模型与参数之间的关系,只会删除引用,不会从数据库中删除这些对象。

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

清空关联

删除源模型与关联之间的所有引用,但不会删除这些关联

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

关联计数

返回当前关联的计数

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

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

批量处理数据

关联模式也支持批量处理,例如:

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

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

// 获取去重的用户所属 team 数量
db.Model(&users).Association("Team").Count()

// 对于批量数据的 `Append`、`Replace`,参数的长度必须与数据的长度相同,否则会返回 error
var users = []User{user1, user2, user3}
// 例如:现在有三个 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})

Delete Association Record

By default, Replace/Delete/Clear in gorm.Association only delete the reference, that is, set old associations's foreign key to null.

You can delete those objects with Unscoped (it has nothing to do with ManyToMany).

How to delete is decided by gorm.DB.

// Soft delete
// UPDATE `languages` SET `deleted_at`= ...
db.Model(&user).Association("Languages").Unscoped().Clear()

// Delete permanently
// DELETE FROM `languages` WHERE ...
db.Unscoped().Model(&item).Association("Languages").Unscoped().Clear()

Delete with Select

You are allowed to delete selected has one/has many/many2many relations with Select when deleting records, for example:

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

NOTE: Associations will only be deleted if the deleting records's primary key is not zero, GORM will use those primary keys as conditions to delete selected associations

// 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

Association Tags

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