GORM-学习

GORM想要传达的一些概念

学习GORM以前须要理解ORM中的一些概念, 理解这些概念对于流畅使用GORM函数会有很是大的帮助, 尝试带着SQL的理念在这里使用, 最后很容易混淆, 对于GORM的使用也止步于简单操做, RAW SQLsql

  1. 怎么用ORM模型设置关联关系 跳转
  2. 外键是怎么表示的, 如何设置默认外键 跳转
  3. 如何取消主键限制, 如何使用自定义外键 跳转
  4. 什么是Association, 如何管理你的关联关系 跳转
  5. 利用Many2Many建立关联表 跳转
  6. 利用Preload作联表 跳转

观念上的变化

场景: 假设存在卡片card-1/2/3, 卡片之间存在某种关联关系数组

  • 在Go程序中分别建立结构体 Cards 以及 CardAssociation
  • 在SQL中建立表: cards + card_associations
  • 当咱们须要建立卡片的时候执行create cards.., 当咱们须要建立关联的时候咱们执行create associations..

→ 所以在传统SQL观念里, 咱们单独设置出两张表, 而后有须要来了, 咱们会单独对这两张表操做. 下面看看在GORM里是怎么作的函数

type Card struct {
  gorm.Model
  Attribute1 string
  Attribute2 string 
  AssociatedCards []Card 
}
复制代码

→ 与传统SQL最大的不一样出现了,在Card结构体里, 咱们嵌套了一些其余Card结构体, 传统SQL中"相互关联"的概念, 在ORM中成为了"相互拥有"的概念.工具

  • 传统SQL: card-1 ~ card-2 关联关系
  • GORM: card-1 > card-2 拥有关系

外键设定

一旦能接受新的模式, 就能够说一说外键设定了. 两个结构体之间相互关联, 最直接的想法是, 我怎么从一个结构体 出发而后去得到另外一个结构体,学习

我怎么去声明一条外键

若是我须要经过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

  • User表, 默认外键名user_id
  • abc表, 默认外键名称abc_id

手动指定外键

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

一旦理解外键是怎么设置的, 咱们就能够开始用上外键了, 概念: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里面填充的是数组,而不是一个单个的对象.

  • 若是一对一的关联关系, 填充单个对象
  • 若是是一对多的关系, 必定要填充对象组(&[]Array). 不然会报错找不到, 默认策略中找不到会建立新的, 来替代, 从而数据被洗掉
    • 取消找不到建立新的替代, 策略
    • 先尝试Find再去Append关联关系

学会利用many2many管理关联关系

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}})
复制代码

联表 - Preload

事实上,理解了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)
复制代码
相关文章
相关标签/搜索