1629. 按鍵持續(xù)時(shí)間最長的鍵
1028
2025-04-01
目錄
文章目錄
目錄
前言
實(shí)體完整性(主鍵約束)
用戶定義完整性(非空約束、唯一約束、檢查約束和默認(rèn)值)
參照完整性(外鍵約束)
關(guān)聯(lián)關(guān)系
一對一、一對多關(guān)聯(lián)
多對多關(guān)聯(lián)
示例
前言
本文基于 postgresql 和 GORM 1.9 版本。GORM v2 對下文中的不足進(jìn)行了優(yōu)化。
全新的 Migrator:允許為關(guān)系創(chuàng)建數(shù)據(jù)庫外鍵,更智能的 AutoMigrate,支持約束、檢查器,增強(qiáng)索引支持。
實(shí)體完整性(主鍵約束)
每個(gè)關(guān)系(表)至少存在一個(gè)主鍵(Primary Key),主鍵值必須唯一,且不允許為 NULL。
type Product struct { gorm.Model Code string `gorm:"primary_key"` Price uint ... }
1
2
3
4
5
6
grom.Model 是 GORM 內(nèi)建的 Struct,用于實(shí)現(xiàn)軟刪除,如下:
type Model struct { ID uint `gorm:"primary_key"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time `sql:"index"` }
1
2
3
4
5
6
可見,Model Struct Product 具有兩個(gè) primary_key:CONSTRAINT products_pkey PRIMARY KEY (code, id)。
因此,GORM 實(shí)現(xiàn)了完全的實(shí)體完整性支持,即可以支持字段主鍵,也可以支持聯(lián)合主鍵。
用戶定義完整性(非空約束、唯一約束、檢查約束和默認(rèn)值)
又稱為域完整性。指數(shù)據(jù)庫表中的列必須滿足某種特定的數(shù)據(jù)類型或約束,包括:字段類型、值域、小數(shù)位數(shù)、CHECK、FOREIGN KEY 約束和 DEFAULT、 NOT NULL。它們有的定義在字段上,有的定義在表上。例如:FOREIGN KEY 約束在 PostgresSQL 中,就是在表級(jí)別定義的;而字段類型、長度、小數(shù)位數(shù)就是在字段上定義的。
GORM 通過 Struct Tag 來支持用戶定義完整性:
`gorm:"xxx"`
1
xxx 可以使用 type、size、precision、not null、default 等 Tags 類型。
其中 Check 約束需要使用到 sql tag,例如:
UserID uint `sql:"type:integer check(code!='')"`
1
它會(huì)被定義到表上:
ALTER TABLE public.products ADD CONSTRAINT products CHECK (code <> ''::text);
1
2
參照完整性(外鍵約束)
通過定義 Model Struct 創(chuàng)建了一個(gè) products belongs to user 的 Belong to 一對一關(guān)系。
// 主表 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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AutoMigrate 的時(shí)候會(huì)執(zhí)行 SQL 語句創(chuàng)建 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") )
1
2
3
4
5
6
7
8
9
10
11
可見,GORM 沒有添加任何約束。按照 GORM 的文檔,這就是 belongs to 的標(biāo)準(zhǔn)定義,它不添加外鍵約束。
嘗試顯式的指定 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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
執(zhí)行的 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") )
1
2
3
4
5
6
7
8
9
10
11
可見,GORM 還是沒有添加任何外鍵約束。
因此,可以確定 GORM 的 foreignkey、association_foreignkey tag 并不會(huì)添加外鍵約束。
嘗試顯式指定 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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
執(zhí)行的 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") )
1
2
3
4
5
6
7
8
9
10
可見,從表的外鍵約束被定義了。也就是說 GORM 1.9 版本如果希望創(chuàng)建表時(shí)定義外鍵(References,參照),那么就需要使用到 sql tag。
注意,sql tag 與 gorm tag 有區(qū)別,前者需要硬編碼相應(yīng)的數(shù)據(jù)庫 TableName和 ColumnName,而后者就只需要你使用結(jié)構(gòu)體和其成員名即可。
除了 no action 模式之外,sql tag 同樣支持:
CASCADE(級(jí)聯(lián))約束方式
UserID uint `sql:"type:integer REFERENCES users(id) on update cascade on delete cascade"`
1
SET NULL(設(shè)空)約束方式
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"`
1
同樣的,GORM 也支持聯(lián)合外鍵,這時(shí)候就需要使用到 GORM 提供的接口了:
db.Model(&Product{}).AddForeignKey( "user_id,user_code", "users(id,code)", "no action", "no action")
1
執(zhí)行 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
1
2
關(guān)聯(lián)關(guān)系
一對一、一對多關(guān)聯(lián),多對多關(guān)聯(lián)不屬于完整性范疇,即:RDBMS 不會(huì)自動(dòng)完成數(shù)據(jù)完整性檢查,包括引用的可用性檢查,數(shù)據(jù)的一致性檢查等,這些工作都需要有應(yīng)用層業(yè)務(wù)邏輯來實(shí)現(xiàn)。所以,在邏輯代碼中也不需要實(shí)現(xiàn)任何完整性約束定義,因此 Model Struct 里也無需添加額外的約束。
一對一、一對多關(guān)聯(lián)
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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
這是典型的一對多定義,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"` }
1
2
3
4
5
6
多對多關(guān)聯(lián)
在關(guān)系型數(shù)據(jù)庫中,多對多關(guān)系需要一張中間表。
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 }
1
2
3
4
5
6
7
8
9
10
11
12
會(huì)執(zhí)行 SQL:
CREATE TABLE "user_language" ( "user_id" integer, "product_id" integer, PRIMARY KEY ("user_id","product_id") )
1
2
3
4
5
6
GORM 會(huì)自動(dòng)創(chuàng)建一張 user_language 連接表(Join Table)。products、users 表的主鍵,被聯(lián)合作為 user_language 表的主鍵。GORM 也會(huì)自動(dòng)的完成 user_id 和 product_id 作為外鍵的關(guān)聯(lián)。但正如上述所言,外鍵約束是不會(huì)自動(dòng)完成的。
示例
// 文章表 type Article struct { ID int `json:"id"` Title string `json:"title"` CategoryId int `json:"category_id"` Category Category `json:"category";gorm:"foreignkey:CategoryID"` // 一對多關(guān)系 Tag []Tag `gorm:"many2many:article_tag" json:"tag"` // 多對多關(guān)系 } // 文章_標(biāo)簽多對多中間表 // 默認(rèn)的,article_id 字段對應(yīng) article 表 id,tag_id 字段對應(yīng) tag 表 id type ArticleTag struct { ID int `json:"id"` ArticleId string `json:"article_id"` TagId string `json:"tag_id"` } // 標(biāo)簽表 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"` }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
查一列:
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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
通過 Related 方法,可以查找 belongs to、has one、has many、many to many 關(guān)系。
查找一列時(shí),首先是需要先把特定的一條 Article 查詢到,然后根據(jù) 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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
查看多列時(shí),使用 Preload 方法可以完成多表關(guān)系的預(yù)加載,然后再自動(dòng)執(zhí)行選擇(WHERE)運(yùn)算。
Go SQL
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。