Go 語言編程 — gorm 的數(shù)據(jù)完整性約束

      網(wǎng)友投稿 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 來支持用戶定義完整性:

      Go 語言編程 — gorm 的數(shù)據(jù)完整性約束

      `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)容。

      上一篇:Excel序號(hào)自動(dòng)更新功能的設(shè)置方法(Excel自動(dòng)更新序號(hào))
      下一篇:在Word中快速輸入分?jǐn)?shù)的技巧(怎么在word中輸入分?jǐn)?shù))
      相關(guān)文章
      色噜噜亚洲男人的天堂| 久久亚洲AV成人出白浆无码国产 | 久久青青成人亚洲精品| 国产精品亚洲mnbav网站| 国产亚洲美女精品久久久久| 亚洲色欲色欱wwW在线| 亚洲国产日韩精品| 国产精品亚洲自在线播放页码 | 久久久久久A亚洲欧洲AV冫| 深夜国产福利99亚洲视频| 老子影院午夜伦不卡亚洲| 精品亚洲国产成人av| 亚洲av无码有乱码在线观看| 亚洲av无码专区青青草原| 亚洲爆乳无码精品AAA片蜜桃| 中文字幕乱码亚洲精品一区| 亚洲首页国产精品丝袜| 亚洲日本久久一区二区va| 67194在线午夜亚洲| 在线观看亚洲AV每日更新无码| 国产成人亚洲精品| 亚洲中文字幕久久精品无码VA| 国产AV旡码专区亚洲AV苍井空| 亚洲日韩AV一区二区三区四区| 亚洲人成网站在线在线观看| 亚洲精品av无码喷奶水糖心| 精品国产亚洲AV麻豆 | 亚洲国产成人久久综合一区77| 亚洲精品国精品久久99热| 久久激情亚洲精品无码?V| 国产成人亚洲综合无码精品| 亚洲电影国产一区| 亚洲精品91在线| 亚洲熟妇自偷自拍另欧美| 久久亚洲中文无码咪咪爱| 亚洲色欲久久久久综合网| 亚洲精品自在在线观看| 久久久久亚洲Av无码专| 亚洲乱码在线播放| 亚洲AV综合色区无码一二三区 | 亚洲精品网站在线观看不卡无广告 |