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)
複製代碼
相關文章
相關標籤/搜索