目錄
文章目錄
- 目錄
- 前言
- 物體完整性(主鍵約束)
- 用戶定義完整性(非空約束、唯一約束、檢查約束和默認值)
- 參照完整性(外鍵約束)
- 關聯關系
- 一對一、一對多關聯
- 多對多關聯
- 示例
前言
本文基于 PostgreSQL 和 GORM 1.9 版本,
物體完整性(主鍵約束)
每個關系(表)至少存在一個主鍵(Primary Key),主鍵值必須唯一,且不允許為 NULL,
type Product struct {
gorm.Model
Code string `gorm:"primary_key"`
Price uint
...
}
grom.Model 是 GORM 內建的 Struct,用于實作軟洗掉,如下:
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
可見,Model Struct Product 具有兩個 primary_key:CONSTRAINT products_pkey PRIMARY KEY (code, id),
因此,GORM 實作了完全的物體完整性支持,即可以支持欄位主鍵,也可以支持聯合主鍵,
用戶定義完整性(非空約束、唯一約束、檢查約束和默認值)
又稱為域完整性,指資料庫表中的列必須滿足某種特定的資料型別或約束,包括:欄位型別、值域、小數位數、CHECK、FOREIGN KEY 約束和 DEFAULT、 NOT NULL,它們有的定義在欄位上,有的定義在表上,例如:FOREIGN KEY 約束在 PostgresSQL 中,就是在表級別定義的;而欄位型別、長度、小數位數就是在欄位上定義的,
GORM 通過 Struct Tag 來支持用戶定義完整性:
`gorm:"xxx"`
xxx 可以使用 type、size、precision、not null、default 等 Tags 型別,
其中 Check 約束需要使用到 sql tag,例如:
UserID uint `sql:"type:integer check(code!='')"`
它會被定義到表上:
ALTER TABLE public.products
ADD CONSTRAINT products CHECK (code <> ''::text);
參照完整性(外鍵約束)
通過定義 Model Struct 創建了一個 products belongs to user 的 Belong to 一對一關系,
// 主表
type User struct {
gorm.Model
Code string `gorm:"primary_key"`
Name string
}
// 從表
type Product struct {
gorm.Model
Code string `gorm:"primary_key"`
Price uint
UserID uint
User User
}
AutoMigrate 的時候會執行 SQL 陳述句創建 products(從)表:
CREATE TABLE "products"
(
"code" text,
"price" integer,
"user_id" integer,
"id" serial,
"created_at" timestamp with time zone,
"updated_at" timestamp with time zone,
"deleted_at" timestamp with time zone ,
PRIMARY KEY ("id")
)
可見,GORM 沒有添加任何約束,按照 GORM 的檔案,這就是 belongs to 的標準定義,它不添加外鍵約束,
嘗試顯式的指定 foreignkey Tag:
type Product struct {
Code string `gorm:"primary_key"`
Price uint
UserID uint
User User `gorm:"foreignkey:UserID;association_foreignkey:ID"`
gorm.Model
}
type User struct {
Code string `gorm:"primary_key"`
Name string
gorm.Model
}
執行的 SQL 是:
CREATE TABLE "products"
(
"code" text,
"price" integer,
"user_id" integer,
"id" serial,
"created_at" timestamp with time zone,
"updated_at" timestamp with time zone,
"deleted_at" timestamp with time zone ,
PRIMARY KEY ("id")
)
可見,GORM 還是沒有添加任何外鍵約束,
因此,可以確定 GORM 的 foreignkey、association_foreignkey tag 并不會添加外鍵約束,
嘗試顯式指定 GORM 的 sql tag 來添加外鍵約束:
type Product struct {
Code string `gorm:"primary_key"`
Price uint
UserID uint `sql:"type:integer REFERENCES users(id) on update no action on delete no action"` // no action 模式外鍵約束
User User `gorm:"foreignkey:UserID;association_foreignkey:ID"`
gorm.Model
}
type User struct {
Code string `gorm:"primary_key"`
Name string
gorm.Model
}
執行的 SQL 陳述句:
CREATE TABLE "products"
(
"code" text,
"price" integer,
"user_id" integer REFERENCES users(id) on update no action on delete no action,
"id" serial,"created_at" timestamp with time zone,
"updated_at" timestamp with time zone,
"deleted_at" timestamp with time zone ,
PRIMARY KEY ("id")
)
可見,從表的外鍵約束被定義了,也就是說 GORM 1.9 版本如果希望創建表時定義外鍵(References,參照),那么就需要使用到 sql tag,
注意,sql tag 與 gorm tag 有區別,前者需要硬編碼相應的資料庫 TableName和 ColumnName,而后者就只需要你使用結構體和其成員名即可,
除了 no action 模式之外,sql tag 同樣支持:
- CASCADE(級聯)約束方式
UserID uint `sql:"type:integer REFERENCES users(id) on update cascade on delete cascade"`
- SET NULL(設空)約束方式
- RESTRICT(禁止)方式:在 PostgreSQl 中與 no action 具有類似的語意,
另外,使用 sql tag 還可以使用 constraint xxx 自定義外鍵約束名,即參考名稱:
UserID uint `sql:"type:integer constraint ref_name REFERENCES users(id) on update no action on delete no action"`
同樣的,GORM 也支持聯合外鍵,這時候就需要使用到 GORM 提供的介面了:
db.Model(&Product{}).AddForeignKey( "user_id,user_code", "users(id,code)", "no action", "no action")
執行 SQL 陳述句:
CONSTRAINT products_user_id_user_code_users_id_code_foreign FOREIGN KEY (user_code, user_id)
REFERENCES public.users (code, id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION
關聯關系
一對一、一對多關聯,多對多關聯不屬于完整性范疇,即:RDBMS 不會自動完成資料完整性檢查,包括參考的可用性檢查,資料的一致性檢查等,這些作業都需要有應用層業務邏輯來實作,所以,在邏輯代碼中也不需要實作任何完整性約束定義,因此 Model Struct 里也無需添加額外的約束,
一對一、一對多關聯
type User struct {
gorm.Model
Code string `gorm:"primary_key"`
Name string
Products []Product
}
type Product struct {
gorm.Model
Code string `gorm:"primary_key"`
Price uint
UserID uint
}
這是典型的一對多定義,users 表無需添加約束欄位,product 表也只需要添加 user_id 欄位作為外鍵,這里可以省略,也可以顯式的定義 gorm tag:foreignkey 或 association_foreignkey,例如:
type User struct {
gorm.Model
Code string `gorm:"primary_key"`
Name string
Products []Product `gorm:"foreignkey:UserID"`
}
多對多關聯
在關系型資料庫中,多對多關系需要一張中間表,
type User struct {
gorm.Model
Code string `gorm:"primary_key"`
Name string
Products []Product `gorm:"many2many:user_language"`
}
type Product struct {
gorm.Model
Code string `gorm:"primary_key"`
Price uint
}
會執行 SQL:
CREATE TABLE "user_language"
(
"user_id" integer,
"product_id" integer,
PRIMARY KEY ("user_id","product_id")
)
GORM 會自動創建一張 user_language 連接表(Join Table),products、users 表的主鍵,被聯合作為 user_language 表的主鍵,GORM 也會自動的完成 user_id 和 product_id 作為外鍵的關聯,但正如上述所言,外鍵約束是不會自動完成的,
示例
// 文章表
type Article struct {
ID int `json:"id"`
Title string `json:"title"`
CategoryId int `json:"category_id"`
Category Category `json:"category";gorm:"foreignkey:CategoryID"` // 一對多關系
Tag []Tag `gorm:"many2many:article_tag" json:"tag"` // 多對多關系
}
// 文章_標簽多對多中間表
// 默認的,article_id 欄位對應 article 表 id,tag_id 欄位對應 tag 表 id
type ArticleTag struct {
ID int `json:"id"`
ArticleId string `json:"article_id"`
TagId string `json:"tag_id"`
}
// 標簽表
type Tag struct {
ID int `json:"id" `
TagName string `json:"tag_name"`
}
// 分類表
type Category struct {
ID int `json:"id"`
CategoryName string `json:"category_name"`
Status int `json:"status"`
}
- 查一列:
func (a *Article) ListArticle(title string) (Article, error) {
query := database.GormPool
var article Article
query.Where("title like ?", "%"+title+"%").First(&article)
fmt.Println(article)
err := query.Model(&article).
Related(&article.Category).
Related(&article.Tag, "tag").
Find(&article).Error
if err != nil && err != gorm.ErrRecordNotFound {
return article, nil
}
return article, err
}
通過 Related 方法,可以查找 belongs to、has one、has many、many to many 關系,
查找一列時,首先是需要先把特定的一條 Article 查詢到,然后根據 Article 定義中指定的 CategoryID 去查找 Category 和 Tag,
- 查多串列:
func (a *Article) ListArticle(title string) (articles []Article, err error) {
query := database.GormPool
err = query.Model(articles).
Where("title like ?", "%"+title+"%").
Preload("Category").
Preload("Tag").Find(&articles).Error
if err != nil && err != gorm.ErrRecordNotFound {
return
}
return
}
查看多列時,使用 Preload 方法可以完成多表關系的預加載,然后再自動執行選擇(WHERE)運算,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/167407.html
標籤:其他
上一篇:國慶
下一篇:ClickHouse壓測
