学习GORM以前须要理解ORM中的一些概念, 理解这些概念对于流畅使用GORM函数会有很是大的帮助, 尝试带着SQL的理念在这里使用, 最后很容易混淆, 对于GORM的使用也止步于简单操做, RAW SQLsql
场景: 假设存在卡片card-1/2/3, 卡片之间存在某种关联关系数组
create cards..
, 当咱们须要建立关联的时候咱们执行create associations..
→ 所以在传统SQL观念里, 咱们单独设置出两张表, 而后有须要来了, 咱们会单独对这两张表操做. 下面看看在GORM里是怎么作的函数
type Card struct {
gorm.Model
Attribute1 string
Attribute2 string
AssociatedCards []Card
}
复制代码
→ 与传统SQL最大的不一样出现了,在Card结构体里, 咱们嵌套了一些其余Card结构体, 传统SQL中"相互关联"的概念, 在ORM中成为了"相互拥有"的概念.工具
一旦能接受新的模式, 就能够说一说外键设定了. 两个结构体之间相互关联, 最直接的想法是, 我怎么从一个结构体 出发而后去得到另外一个结构体,学习
若是我须要经过User去查找它拥有哪些CreditCard, 那么实际上我作的事情 = "拿着User主键去CreditCard表查询". 这个User的主键, 在CreditCard表里叫什么名字? 这个名字就是咱们即将设置的外键. 请牢记这个概念ui
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
type User struct {
gorm.Model
CreditCards []CreditCard
}
复制代码
在GORM里咱们只要建立这两个表, 关联关系就生成了, User就会包含不少个CreditCard对象.假设如今你但愿经过User去查询它 关联了那些卡片, 你能够这么作:spa
target := []CreditCard{}
source := &User{
Model:gorm.Model{
ID:1,
},
}
database.Model(source).Related(&target)
复制代码
继续外键设置的问题, 若是咱们不去指定外键名称, 那么咱们拿着User主键去CreditCard查询的时候, 默认使用的属性名:user_id
, 执行:code
select * from credit_cards where user_id = 123;
复制代码
请注意一个坑, 默认外键是: 表名+id,orm
type User struct {
gorm.Model
MemberNumber string
CreditCards []CreditCard `gorm:"foreignkey:UserMemberNumber;association_foreignkey:MemberNumber"`
}
type CreditCard struct {
gorm.Model
Number string
UserMemberNumber string
}
复制代码
foreignkey
在此以前,CreditCard表里必需要有User_ID, 做为外键, 可是指定foreign_key以后, 就不是必定要有User_id字段了association_foreignkey
, 在以前咱们都是规定, 必须使用 UserID(主键) 去CreditCard表里查找, 可是使用了这个Tag之后你就能够经过MemberNumber (非主键) 去查找了一旦理解外键是怎么设置的, 咱们就能够开始用上外键了, 概念:Association
是一个笼统的工具, 用于管理全部关联关系,以上面的Card&User为例, 咱们来管理以上 两表之间的关联关系(上面两表,采用默认外键/主键的方式相互关联)对象
cs := []CreditCard{}
xiaohan := &User{
Model:gorm.Model{
ID:1,
},
}
// 全部与xiaohan(id=1)相关联的CreditCard,找出来
d.Model(xiaohan).Association("CreditCards").Find(&cs)
// xiaohan数据取消与ID=1的CreditCard取消关联
d.Model(xiaohan).Association("CreditCards").Delete(&CreditCard{
Model:gorm.Model{
ID:1,
},
})
// xiaohan与CreditCard之间添加关联
d.Model(xiaohan).Association("CreditCards").Append(&cards)
// 取消全部关联
db.Model(xiaohan).Association("Languages").Clear()
// 对象关联计数
db.Model(xiaohan).Association("Languages").Count()
复制代码
关联建立的时候存在一个坑,这个坑不注意可能会致使你的数据被抹掉 意识到,User→CreditCard时, 一个User面对的是多个CreditCard →所以在Append里面填充的是数组,而不是一个单个的对象.
card_id | association_id |
---|---|
card-1 | card-2 |
card-1 | card-3 |
在你建立表之后, AssociationCards
虽然也是一条属性, 可是并不会像别的字段同样出如今表里, 由于在这里咱们讨论的是关联 这个字段被忽略了, 不会出如今表中.取而代之的是出现了一张表: 名字就叫作card_associations
正如你想表示的那样, 一个Card对象关联着许多别的Card对象, 在这张关联表card_associations
中, 一方面是你的card_id
另外一方面也是你设置的association_id
.
简单说, 就是你不用手动去设置了,通过这样描述之后, 表会为你建立
// 建立一张卡片, 不作任何关联关系, ID自增
database.Create(&Card{})
// 建立一张卡片, 同时关联一张卡片
// 这里若是卡片存在, 直接关联, 若是不存在则会为你关联
database.Create(&Card{
AssociatedCards: []Card{
{
Model:gorm.Model{ID:2},
},
},
})
// 只关联两张卡片,将4&7两张卡片关联起来
database.Model(&Card{Model:gorm.Model{ID:7}}).
Association("AssociatedCards").
Append(&Card{Model:gorm.Model{ID:4}})
复制代码
事实上,理解了Association这种思想之后,再去理解Preload就会容易一些, 在GORM里关联/外键这样的概念被转换成结构体之间的相互包含. 继续上面的例子,聊聊Card之间的"自关联", 怎么去查询id=3
卡片所关联的卡片?
type Card struct {
ID -> 3
AssociatedCards -> ?
}
// 咱们这样作:
item := &Card{
Model:gorm.Model{
ID:3,
},
}
database.Preload("AssociatedCards").Find(item)
fmt.Printf("Here the item is %v \n",item)
复制代码
一级操做:Find 这行代码的执行过程是这样的,上来先执行Find(&item)
, 也就是咱们要先查询出ID=3的对象出来, 既然查出来了, 接下来就能够查看它关联了那些卡片了.
二级操做:最外Preload 咱们作Preload, AssociatedCards
是一个属性, 同时在这里也象征了,全部关联的卡片, 咱们只要取出这一行, 就能拿出全部关联的属性
三级操做:次外Preload ...
若是理解以上Preload的执行原理, 以及执行顺序之后,咱们就能够开始在上面玩一些花样了,咱们查出来的是一些Card对象, 玩的原理是对各级操做作限制, 诸如 order , where , not in 之类的操做
// 对一级操做Find上限制,这里的where是做用于Find的
database.Where(xxx)
Preload("AssociatedCards").
Find(item)
// 对二级操做上限制 - 排序
database.Preload("AssociatedCards", func(db *gorm.DB) *gorm.DB {
return db.Order("cards.id DESC")
}).Find(item)
// 对二级操做上限制 - 操做
database.Preload("AssociatedCards", "id not in (?)","1,2").Find(item)
复制代码