gorm 教程二(1)https://developer.aliyun.com/article/1391739
反向关联
// 用户拥有并且属于多种语言,使用 `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;"` } db.Model(&language).Related(&users) SELECT * FROM "users" INNER JOIN "user_languages" ON "user_languages"."user_id" = "users"."id" WHERE ("user_languages"."language_id" IN ('111'))
外键
type CustomizePerson struct { IdPerson string `gorm:"primary_key:true"` Accounts []CustomizeAccount `gorm:"many2many:PersonAccount;association_foreignkey:idAccount;foreignkey:idPerson"` } type CustomizeAccount struct { IdAccount string `gorm:"primary_key:true"` Name string }
外键会为两个结构体创建一个多对多的关系,并且这个关系将通过外键customize_person_id_person 和 customize_account_id_account 保存到中间表 PersonAccount。
中间表外键
如果你想改变中间表的外键,你可以用标签 association_jointable_foreignkey, jointable_foreignkey
type CustomizePerson struct { IdPerson string `gorm:"primary_key:true"` Accounts []CustomizeAccount `gorm:"many2many:PersonAccount;foreignkey:idPerson;association_foreignkey:idAccount;association_jointable_foreignkey:account_id;jointable_foreignkey:person_id;"` } type CustomizeAccount struct { IdAccount string `gorm:"primary_key:true"` Name string }
自引用
为了定义一个自引用的多对多关系,你不得不改变中间表的关联外键。
和来源表外键不同的是它是通过结构体的名字和主键生成的,例如:
type User struct { gorm.Model Friends []*User `gorm:"many2many:friendships;association_jointable_foreignkey:friend_id"` }
GORM 将创建一个带外键 user_id 和 friend_id 的中间表, 并且使用它去保存用户表的自引用关系。
然后你可以像普通关系一样操作它, 例如:
DB.Preload("Friends").First(&user, "id = ?", 1) DB.Model(&user).Association("Friends").Append(&User{Name: "friend1"}, &User{Name: "friend2"}) DB.Model(&user).Association("Friends").Delete(&User{Name: "friend2"}) DB.Model(&user).Association("Friends").Replace(&User{Name: "new friend"}) DB.Model(&user).Association("Friends").Clear() DB.Model(&user).Association("Friends").Count()
使用多对多
db.Model(&user).Related(&languages, "Languages") SELECT * FROM "languages" INNER JOIN "user_languages" ON "user_languages"."language_id" = "languages"."id" WHERE "user_languages"."user_id" = 111 // 当查询用户时预加载 Language db.Preload("Languages").First(&user)
关联
自动创建/更新
GORM 将在创建或保存一条记录的时候自动保存关联和它的引用,如果关联有一个主键, GORM 将调用 Update 来更新它, 不然,它将会被创建。
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@example.com"}, }, Languages: []Language{ {Name: "ZH"}, {Name: "EN"}, }, } db.Create(&user) BEGIN TRANSACTION; INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"); INSERT INTO "addresses" (address1) VALUES ("Shipping Address - Address 1"); 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"); INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu-2@example.com"); INSERT INTO "languages" ("name") VALUES ('ZH'); INSERT INTO user_languages ("user_id","language_id") VALUES (111, 1); INSERT INTO "languages" ("name") VALUES ('EN'); INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2); COMMIT; db.Save(&user)
关闭自动更新
如果你的关联记录已经存在在数据库中, 你可能会不想去更新它。
你可以设置 gorm:association_autoupdate 为 false
// 不更新有主键的关联,但会更新引用 db.Set("gorm:association_autoupdate", false).Create(&user) db.Set("gorm:association_autoupdate", false).Save(&user) 或者使用 GORM 的标签, gorm:"association_autoupdate:false" type User struct { gorm.Model Name string CompanyID uint // 不更新有主键的关联,但会更新引用 Company Company `gorm:"association_autoupdate:false"` }
关闭自动创建
即使你禁用了 AutoUpdating, 仍然会创建没有主键的关联,并保存它的引用。
你可以通过把 gorm:association_autocreate 设置为 false 来禁用这个行为。
// 不创建没有主键的关联,不保存它的引用。 db.Set("gorm:association_autocreate", false).Create(&user) db.Set("gorm:association_autocreate", false).Save(&user) 或者使用 GORM 标签, gorm:"association_autocreate:false" type User struct { gorm.Model Name string // 不创建没有主键的关联,不保存它的引用。 Company1 Company `gorm:"association_autocreate:false"` }
关闭自动创建/更新
禁用 AutoCreate 和 AutoUpdate,你可以一起使用它们两个的设置。
db.Set("gorm:association_autoupdate", false).Set("gorm:association_autocreate", false).Create(&user) type User struct { gorm.Model Name string Company Company `gorm:"association_autoupdate:false;association_autocreate:false"` }
或者使用 gorm:save_associations
db.Set("gorm:save_associations", false).Create(&user) db.Set("gorm:save_associations", false).Save(&user) type User struct { gorm.Model Name string Company Company `gorm:"association_autoupdate:false"` }
关闭保存引用
如果你不想当更新或保存数据的时候保存关联的引用, 你可以使用下面的技巧
db.Set("gorm:association_save_reference", false).Save(&user) db.Set("gorm:association_save_reference", false).Create(&user) 或者使用标签 type User struct { gorm.Model Name string CompanyID uint Company Company `gorm:"association_save_reference:false"` }
关联模式
关联模式包含一些可以轻松处理与关系相关的事情的辅助方法。
// 开启关联模式 var user User db.Model(&user).Association("Languages") // `user` 是源表,必须包含主键 // `Languages` 是源表关系字段名称。 // 只有上面两个条件都能匹配,关联模式才会生效, 检查是否正常: // db.Model(&user).Association("Languages").Error
查找关联
查找匹配的关联
db.Model(&user).Association("Languages").Find(&languages)
增加关联
为 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("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()
预加载
预加载
db.Preload("Orders").Find(&users) SELECT * FROM users; SELECT * FROM orders WHERE user_id IN (1,2,3,4); 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'); 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
自动预加载
始终自动预加载的关联
type User struct { gorm.Model Name string CompanyID uint Company Company `gorm:"PRELOAD:false"` //没有预加载 Role Role // 已经预加载 } db.Set("gorm:auto_preload", true).Find(&users)
嵌套预加载
db.Preload("Orders.OrderItems").Find(&users) db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)
自定义预加载 SQL
您可以通过传入func(db * gorm.DB)* gorm.DB来自定义预加载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;