溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

利用golang怎么對數(shù)據(jù)庫進行完整性約束

發(fā)布時間:2020-12-21 15:16:00 來源:億速云 閱讀:232 作者:Leah 欄目:開發(fā)技術(shù)

利用golang怎么對數(shù)據(jù)庫進行完整性約束?針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

1、實體完整性:

每個關(guān)系(表)有且僅有一個主鍵,每一個主鍵值必須唯一,而且不允許為“空”(NULL)或重復(fù)。

type Product struct {
Code string `gorm:"primary_key"`
Price uint
UserID uint //`sql:"type:bigint REFERENCES users(id) on update no action on delete cascade"`
UserCode string //`sql:"type:bigint REFERENCES users(code) on update no action on delete cascade"`
User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`
gorm.Model
}

在利用gorm的db對象創(chuàng)建表,其使用的SQL如下:

CREATE TABLE public.products
(
 code text COLLATE pg_catalog."default" NOT NULL,
 price integer,
 user_id integer,
 user_code text COLLATE pg_catalog."default",
 id integer NOT NULL DEFAULT nextval('products_id_seq'::regclass),
 created_at timestamp with time zone,
 updated_at timestamp with time zone,
 deleted_at timestamp with time zone,
 CONSTRAINT products_pkey PRIMARY KEY (code, id)
)
WITH (
 OIDS = FALSE
)
TABLESPACE pg_default; 
ALTER TABLE public.products
 OWNER to postgres; 
 
-- Index: idx_products_deleted_at 
 
-- DROP INDEX public.idx_products_deleted_at; 
 
CREATE INDEX idx_products_deleted_at
 ON public.products USING btree
 (deleted_at)
 TABLESPACE pg_default;

說明:

1.1、grom.Model是gorm預(yù)定義的結(jié)構(gòu),用于實現(xiàn)軟刪除,定義如下:

type Model struct {
 ID uint `gorm:"primary_key"`
 CreatedAt time.Time
 UpdatedAt time.Time
 DeletedAt *time.Time `sql:"index"`
}

它里面已經(jīng)定義了主鍵,在本例中,我們還定義了一個主鍵:

Code string `gorm:"primary_key"`

從SQL輸出我們看到:

CONSTRAINT products_pkey PRIMARY KEY (code, id)

因此,Gorm實現(xiàn)了完全的實體完整性支持,即可以支持字段主鍵,也可以支持聯(lián)合主鍵。

1.2、對比結(jié)構(gòu)體和sql語句可以看出

1.2.1 表名=結(jié)構(gòu)體名小寫的復(fù)數(shù) 例子:Product變?yōu)?products

1.2.2 字段名=結(jié)構(gòu)體成員名大寫分隔的子串小寫形式用下劃線連接 例子:ID變?yōu)閕d CreatedAt變?yōu)閏reated_at

1.3、前述1.1和1.2構(gòu)成了Gorm的convention,它的文檔里有,默認情況下,就是這么處理,但是用戶可以不用gorm.Model,自定義表名、字段名,都可以支持。

2、域完整性:

是指數(shù)據(jù)庫表中的列必須滿足某種特定的數(shù)據(jù)類型或約束,又叫用戶定義完整性。包括:字段類型、值域、小數(shù)位數(shù)、CHECK、FOREIGN KEY 約束和DEFAULT、 NOT NULL。它們有的定義在字段上,有的定義在表上。例如:FOREIGN KEY 約束在PostgresSQL中,就是在表級別定義的;而,字段類型、長度、小數(shù)位數(shù)就是在字段上定義的。

2.1 通過結(jié)構(gòu)體tag

`gorm:"xxx"` ,在字段上可以使用:type、size、precision、not null、default,Gorm就可以完成這些域完整性的定義

2.2 FOREIGN KEY 約束

2.2.1 單字段外鍵約束

type Product struct {
 Code string //`gorm:"primary_key"`
 Price uint
 UserID uint //`sql:"type:bigint REFERENCES users(id) on update no action on delete cascade"`
  //UserCode string //`sql:"type:bigint REFERENCES users(code) on update no action on delete cascade"`
 User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
}
type User struct {
  //Code string `gorm:"primary_key"`
 Name string
 gorm.Model
  //Product Product //`gorm:"EMBEDDED"`
}

上面的代碼按照gorm文檔,創(chuàng)建了一個products belongs to user關(guān)系。執(zhí)行的sql是:

CREATE TABLE "users" ("code" text,"name" text,"id" serial,"created_at" timestamp with time zone,"updated_at" timestamp with time zone,"deleted_at" timestamp with time zone , PRIMARY KEY ("code","id"))
CREATE TABLE "users" ("code" text,"name" text,"id" serial,"created_at" timestamp with time zone,"updated_at" timestamp with time zone,"deleted_at" timestamp with time zone , PRIMARY KEY ("code","id"))

我們看到,gorm沒有添加任何約束。按照Gorm文檔,這就是belongs to標準定義。

它不添加外鍵約束。

那么,改為顯式標準的形式,采用foreignkey 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 cascade"`
  //UserCode string //`sql:"type:bigint REFERENCES users(code) on update no action on delete cascade"`
  //UserID uint
 User User `gorm:"foreignkey:UserID;association_foreignkey:ID"` //`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
}
type User struct {
  //Code string `gorm:"primary_key"`
 Name string
 gorm.Model
  //Product Product //`gorm:"EMBEDDED"`
}

執(zhí)行的sql是:

CREATE TABLE "users" ("name" text,"id" serial,"created_at" timestamp with time zone,"updated_at" timestamp with time zone,"deleted_at" timestamp with time zone , PRIMARY KEY ("id")) 
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 tag 的 foreignkey 和 association_foreignkey并不會添加外鍵約束。

但是,我們可以用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"`
  //UserCode string //`sql:"type:bigint REFERENCES users(code) on update no action on delete cascade"`
  //UserID uint
 User User `gorm:"foreignkey:UserID;association_foreignkey:ID"` //`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
}
type User struct {
  //Code string `gorm:"primary_key"`
 Name string
 gorm.Model
  //Product Product //`gorm:"EMBEDDED"`
}

創(chuàng)建products表的語句:

 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"))

注意,當使用sql tag時,不像gorm tag,它要你用數(shù)據(jù)庫表名和字段名,而gorm就只需要你使用結(jié)構(gòu)體和其成員名即可。

外鍵被定義了,此時,可以滿足外鍵約束,如前述,具體是:

子表外鍵字段的值必須在主表被參照字段值得范圍內(nèi),或者為NULL;外鍵參照的必須是主表的主鍵或唯一鍵;主表主鍵/唯一鍵被子表參照時,主表相應(yīng)記錄不允許被刪除

此時外鍵約束的名字是數(shù)據(jù)庫自己取的,可能長了,你可以自定義:

UserID uint `sql:"type:integer constraint ref REFERENCES users(id) on update no action on delete no action"`

加上 constraint xxx,就可以為約束取名為xx了。

上述外鍵約束是在定義結(jié)構(gòu)體時,在結(jié)構(gòu)體成員上定義的,因此翻譯為sql語句就變成了對字段的外鍵約束,那如果要定義參照聯(lián)合主鍵之類的外鍵呢?就不能在結(jié)構(gòu)體中定義,而要使用gorm的api了。

2.2.2 多字段外鍵約束

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"`
  //UserCode string //`sql:"type:bigint REFERENCES users(code) on update no action on delete cascade"`
 UserCode string
 UserID uint
 User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`//`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
}
type User struct {
 Code string `gorm:"primary_key"`
 Name string
 gorm.Model
  //Product Product //`gorm:"EMBEDDED"`
}

在程序中使用:

postgres. Model( &Product{}). AddForeignKey( "user_id,user_code", "users(id,code)", "no action", "no action")

這樣,products表就有約束:

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

如此就OK了。這里約束的名字就很長了,api沒有給你自己取名字的機會。

2.3 check約束

type Product struct {
 Code string //`gorm:"primary_key"`
 Price uint
 UserID uint `sql:"type:integer check(code!='')"`
 UserCode string //`sql:"type:bigint constraint ref REFERENCES users(code) on update no action on delete cascade"`
  //UserCode string
  //UserID uint
 User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`//`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
}

這樣就行??雌饋磉@個check和userID沒有什么關(guān)系,是的,check會被定義到表上:

ALTER TABLE public.products

ADD CONSTRAINT products CHECK (code <> ''::text);

因此,Check也完美了,找個結(jié)構(gòu)體的字段,然后加上check就行了。

3、參照完整性:

對于永久關(guān)系的相關(guān)表,在更新、插入或刪除記錄時,如果只改其一,就會影響數(shù)據(jù)的完整性。對于更新、插入或刪除表間數(shù)據(jù)的完整性,統(tǒng)稱為參照完整性。

對于外鍵約束,插入?yún)⒄胀暾员粷M足。因此,如前述:

UserID uint `sql:"type:integer REFERENCES users(id) on update no action on delete no action"`

定義好on update 和 on delete的參數(shù),就可以滿足參照完整性。

具體改為:

UserID uint `sql:"type:integer REFERENCES users(id) on update cascade on delete cascade"`

即可,而且數(shù)據(jù)庫還允許有別的選擇,這里是級聯(lián)更新和級聯(lián)刪除,主表已刪除,子表就跟著刪,這是數(shù)據(jù)庫參照完整性的原初定義。

ps. gorm不默認實施參照完整性,不加約束的原因查了其git issue,主要是因為postgresql要求被關(guān)聯(lián)的表要先存在。而這會導(dǎo)致創(chuàng)建表和自動升級表migration的順序依賴,所以用戶要sqltag或者調(diào)用api手動實施。

4、*1對多 和 多對多關(guān)系

這不屬于完整性范疇。

4.1 1對多

1對多不需要實施完整性約束,因為用戶可以對應(yīng)0到多個產(chǎn)品。因此,表結(jié)構(gòu)里無需添加額外的約束。

type Product struct {
 Code string //`gorm:"primary_key"`
 Price uint
  //UserID uint `sql:"type:integer constraint ref REFERENCES users(id) on update no action on delete no action check(code!='')"`
  //UserCode string //`sql:"type:bigint constraint ref REFERENCES users(code) on update no action on delete cascade"`
  //UserCode string
  //UserID uint
  //User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`//`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
 UserID uint
}
type User struct {
 Code string //`gorm:"primary_key"`
 Name string
 gorm.Model
 Products []Product
  //Product Product //`gorm:"EMBEDDED"`
}

上面是gorm一對多的典型定義,users表不會多任何字段,product表會多user_id字段。這里UserID是外鍵。也可以顯式定義,foreignkey 和Association ForeignKey 上例相當于:

type Product struct {
 Code string //`gorm:"primary_key"`
 Price uint
  //UserID uint `sql:"type:integer constraint ref REFERENCES users(id) on update no action on delete no action check(code!='')"`
  //UserCode string //`sql:"type:bigint constraint ref REFERENCES users(code) on update no action on delete cascade"`
  //UserCode string
  //UserID uint
  //User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`//`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
 UserID uint
}
type User struct {
 Code string //`gorm:"primary_key"`
 Name string
 gorm.Model
 Products []Product `gorm:"foreignkey:UserID"`
  //Product Product //`gorm:"EMBEDDED"`
}

4.2 多對多

關(guān)系型數(shù)據(jù)庫中,多對多關(guān)系需要多一張表,總共3張表,完整性grom是如何保證的?

type Product struct {
 Code string //`gorm:"primary_key"`
 Price uint
  //UserID uint `sql:"type:integer constraint ref REFERENCES users(id) on update no action on delete no action check(code!='')"`
  //UserCode string //`sql:"type:bigint constraint ref REFERENCES users(code) on update no action on delete cascade"`
  //UserCode string
  //UserID uint
  //User User //`gorm:"foreignkey:UserID;association_foreignkey:ID"`//`gorm:"foreignkey:UserID;association_foreignkey:ID"`
 gorm.Model
  //UserID uint
}
type User struct {
 Code string //`gorm:"primary_key"`
 Name string
 gorm.Model
 Products []Product `gorm:"many2many:user_language"`
  //Product Product //`gorm:"EMBEDDED"`
}

此時,會多一個表(jointtable連接表):

CREATE TABLE "user_language" ("user_id" integer,"product_id" integer, PRIMARY KEY ("user_id","product_id"))

products和users表的主鍵,被聯(lián)合作為新表的主鍵。在新表中,user_id和product_id也是外鍵,在Gorm中,是可以在many2many關(guān)系中自定義外鍵、關(guān)聯(lián)外鍵的。當然,外鍵約束就不要想了。

那么,在上例中,按照grom的語法,對于Products成員,外鍵和關(guān)聯(lián)外鍵分別是什么呢?簡言之,在gorm所有情況下,將嵌入結(jié)構(gòu)體和其父結(jié)構(gòu)體關(guān)聯(lián)起來的那個字段,就是外鍵;關(guān)聯(lián)外鍵是寫入外鍵的值的來源對應(yīng)的鍵,通常就是父結(jié)構(gòu)體的主鍵。在多對多情況下,如上例,連接表的user_id是外鍵,而寫入時,并沒有將user_id寫入Products []Product,寫入的是product_id代表的數(shù)據(jù),因此product_id是associate_foreignkey,這是gorm的約定,很費解,解釋也牽強。

下面是多對多自引用:

type User struct {
 gorm.Model
 Friends []*User `gorm:"many2many:friendships;association_jointable_foreignkey:friend_id"`
}

用association_jointable_foreignkey在連接表里創(chuàng)建了一個字段。也比較費解。

綜上:

1、字段的基本約束,通過gorm tag基本都可以設(shè)置。

2、gorm支持實體完整性約束。

3、域完整性約束中,外鍵約束需要通過 sql tag或調(diào)用api實現(xiàn),check約束可以直接在字段上定義。

4、參照完整性gorm不能默認實現(xiàn),必須通過sql tag或者調(diào)用api實現(xiàn)。

關(guān)于利用golang怎么對數(shù)據(jù)庫進行完整性約束問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI