這篇文章將為大家詳細(xì)講解有關(guān)如何在Golang中利用gorm添加一個(gè)數(shù)據(jù)庫(kù)排他鎖,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
10年的沿河網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都全網(wǎng)營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整沿河建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“沿河網(wǎng)站設(shè)計(jì)”,“沿河網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。適用于先讀后更新的數(shù)據(jù)競(jìng)爭(zhēng)場(chǎng)景,且應(yīng)該將加鎖操作放到事務(wù)中,防止鎖被自動(dòng)釋放,原因參考mysql doc
func UpdateUser(db *gorm.DB, id int64) error { tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Error; err != nil { return err } user := User{} // 鎖住指定 id 的 User 記錄 if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&user, id).Error; err != nil { tx.Rollback() return err } // 更新操作... // commit事務(wù),釋放鎖 if err := tx.Commit().Error; err != nil { return err } return nil }
sync.Mutex解法(效率較低):
var lock sync.Mutex func UpdateUser(db *gorm.DB, id int64) error { lock.Lock() // 數(shù)據(jù)庫(kù)操作... lock.Unlock() return nil }
參考
doc
補(bǔ)充:Golang數(shù)據(jù)庫(kù)編程之GORM模型定義與數(shù)據(jù)庫(kù)遷移
在開(kāi)發(fā)應(yīng)用程序時(shí),一般而言,我們是先設(shè)計(jì)好數(shù)據(jù)表,再使用開(kāi)發(fā)語(yǔ)言建立對(duì)應(yīng)的數(shù)據(jù)模型,不過(guò),我們今天要講的是一個(gè)逆向操作的過(guò)程,即如何通定義GORM框架的數(shù)據(jù)模型,然后再通過(guò)執(zhí)行GROM框架編寫(xiě)的應(yīng)用程序,用定義好數(shù)據(jù)模型在數(shù)據(jù)庫(kù)中創(chuàng)建對(duì)應(yīng)的數(shù)據(jù)表。
因此需要先講講怎么定義GORM的數(shù)據(jù)模型。
一般來(lái)說(shuō),我們說(shuō)GROM的模型定義,是指定義代表一個(gè)數(shù)據(jù)表的結(jié)構(gòu)體(struct),然后我們可以使用GROM框架可以將結(jié)構(gòu)體映射為相對(duì)應(yīng)的關(guān)系數(shù)據(jù)庫(kù)的數(shù)據(jù)表,或者查詢數(shù)據(jù)表中的數(shù)據(jù)來(lái)填充結(jié)構(gòu)體,如下所示,我們定義了一個(gè)名為Post的結(jié)構(gòu)體。
type Post struct { PostId int Uid int Title string Content string Type int CreatedAt time.Time UpdatedAt time.Time }
創(chuàng)建好一個(gè)結(jié)構(gòu)體只是第一步,不過(guò)先不著急要怎么去創(chuàng)建數(shù)據(jù)表,我們要先了解一下結(jié)構(gòu)體與數(shù)據(jù)表之間的映射規(guī)則,主要有以下幾點(diǎn):
Struct tags
我們知道,Go語(yǔ)言的結(jié)構(gòu)體支持使用tags為結(jié)構(gòu)體的每個(gè)字段擴(kuò)展額外的信息,如使用標(biāo)準(zhǔn)庫(kù)encoding/json包進(jìn)行JSON編碼時(shí),便可以使用tags進(jìn)行編碼額外信息的擴(kuò)展。
GROM框架有自己的一個(gè)tags約定,如下所示:
Column 指定列名
Type 指定列數(shù)據(jù)類型
Size 指定列大小, 默認(rèn)值255
PRIMARY_KEY 將列指定為主鍵
UNIQUE 將列指定為
DEFAULT 指定列默認(rèn)值
PRECISION 指定列精度
NOT NULL 將列指定為非 NULL
AUTO_INCREMENT 指定列是否為自增類型
INDEX 創(chuàng)建具有或不帶名稱的索引, 如果多個(gè)索引同名則創(chuàng)建復(fù)合索引
UNIQUE_INDEX 和 INDEX 類似,只不過(guò)創(chuàng)建的是索引
EMBEDDED 將結(jié)構(gòu)設(shè)置為嵌入
EMBEDDED_PREFIX 設(shè)置嵌入結(jié)構(gòu)的前綴
- 忽略此字段
GROM還支持一些關(guān)聯(lián)數(shù)據(jù)表的tags約定,有機(jī)會(huì)我講講GROM數(shù)據(jù)表關(guān)聯(lián)的時(shí)候,會(huì)說(shuō)到的。
上面列出的GORM支持的tags,方便我們定制結(jié)構(gòu)體字段到數(shù)據(jù)表字段之間的映射規(guī)則,下面的代碼,我們給Post結(jié)構(gòu)體定制一些tags擴(kuò)展,如下:
type Post struct { PostId int `gorm:"primary_key;auto_increment"` Uid int `gorm:"type:int;not null"` Title string `gorm:"type:varchar(255);not null"` Content string `gorm:"type:text;not null"` Type uint8 `gorm:"type:tinyint;default 1;not null"` CreatedAt time.Time UpdatedAt time.Time DeletedAt time.Time }
從上面的例子我們可以看出GORM為數(shù)據(jù)模型的字段定義tags的格式,每個(gè)字段可以用多個(gè)類型的tags信息,不同的tag之間用分號(hào)分隔。
除了上面講的tags定義了字段之間的映射規(guī)則外,Go將結(jié)構(gòu)體映射為關(guān)系型數(shù)據(jù)表時(shí),還有自己的一套慣例,或稱為約定,主要有以下幾點(diǎn):
GROM的約定中,一般將數(shù)據(jù)模型中的ID字段映射為數(shù)據(jù)表的主鍵,如下面定義的TestModel,ID為主鍵,TestModel的ID的數(shù)據(jù)類型為string,如果ID的數(shù)據(jù)類型為int,則GROM還會(huì)為該設(shè)置AUTO_INCREMENT,使用ID成為自增主鍵。
type TestModel struct{ ID int Name string }
當(dāng)然,我們也可以自定義主鍵字段的名稱,如上面的Post結(jié)構(gòu)體,我們?cè)O(shè)置了PostId字段為主鍵,如果我們定義了其他字段為主鍵,那么,就算結(jié)構(gòu)體中仍有ID字段,GROM框架也不會(huì)把ID字段當(dāng)作主鍵了。
type Post struct { ID int PostId int `gorm:"primary_key;auto_increment"` Uid int `gorm:"type:int;not null"` Title string `gorm:"type:varchar(255);not null"` Content string `gorm:"type:text;not null"` Type uint8 `gorm:"type:tinyint;default 1;not null"` CreatedAt time.Time UpdatedAt time.Time DeletedAt time.Time }
所以,我們?cè)赑ost結(jié)構(gòu)體中加一個(gè)ID字段,PostId字段仍是主鍵,下面是在數(shù)據(jù)中使用desc posts語(yǔ)句打印出來(lái)的結(jié)果:
當(dāng)我們使用結(jié)構(gòu)體創(chuàng)建數(shù)據(jù)表時(shí),數(shù)據(jù)表的名稱默認(rèn)為結(jié)構(gòu)體的小寫(xiě)復(fù)數(shù)形式,如結(jié)構(gòu)體Post對(duì)應(yīng)的數(shù)據(jù)表名稱為posts,當(dāng)然我們也可以自己指定結(jié)構(gòu)體對(duì)應(yīng)的數(shù)據(jù)表名稱,而不是用默認(rèn)的。
為結(jié)構(gòu)體加上TableName()方法,通過(guò)這個(gè)方法可以返回自定義的數(shù)據(jù)表名,如下:
//指定Post結(jié)構(gòu)體對(duì)應(yīng)的數(shù)據(jù)表為my_posts func (p Post) TableName() string{ return "my_posts" }
除了指定數(shù)據(jù)表名外,我們也可以重寫(xiě)gorm.DefaultTableNameHandler這個(gè)變量,這樣可以為所有數(shù)據(jù)表指定統(tǒng)一的數(shù)據(jù)表前綴,如下:
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string { return "tb_" + defaultTableName; }
這樣的話,通過(guò)結(jié)構(gòu)體Post創(chuàng)建的數(shù)據(jù)表名稱則為tb_posts。
結(jié)構(gòu)體到數(shù)據(jù)表的名稱映射規(guī)則為結(jié)構(gòu)體名稱的復(fù)數(shù),而結(jié)構(gòu)體的字段到數(shù)據(jù)表字段的默認(rèn)映射規(guī)則是用下劃線分隔每個(gè)大寫(xiě)字母開(kāi)頭的單詞,如下:
type Prize struct { ID int PrizeName string }
上面的結(jié)構(gòu)體Prize中的PrizeName字段對(duì)應(yīng)的數(shù)據(jù)表為prize_name,但我們把PrizeName改為Prizename時(shí),則對(duì)應(yīng)的數(shù)據(jù)表字段名稱為prizename,這是為因?yàn)橹环指舸髮?xiě)字段開(kāi)頭的單詞。
當(dāng)然,我們也可以為結(jié)構(gòu)體的某個(gè)字段定義tags擴(kuò)展信息,這樣結(jié)構(gòu)體字段到數(shù)據(jù)表字段的映規(guī)則就在tags中定義。
前面我們說(shuō)過(guò),如果結(jié)構(gòu)體中有名稱為ID字段,則GORM框架會(huì)把該字段作為數(shù)據(jù)表的主鍵,除此之外,如果結(jié)構(gòu)體中有CreatedAt,UpdatedAt,DeletedAt這幾個(gè)字段的話,則GROM框架也會(huì)作一些特殊處理,規(guī)則如下:
CreatedAt:新增數(shù)據(jù)表記錄的時(shí)候,會(huì)自動(dòng)寫(xiě)入這個(gè)字段。 UpdatedAt:更新數(shù)據(jù)表記錄的時(shí)候,會(huì)自動(dòng)更新這個(gè)字段。 DeletedAt:當(dāng)執(zhí)行軟刪除的時(shí)候,會(huì)自動(dòng)更新這個(gè)字段,表示刪除時(shí)間
gorm.Model
由于如果結(jié)構(gòu)體中有ID,CreatedAt,UpdatedAt,DeletedAt這幾個(gè)比較通用的字段,GORM框架會(huì)自動(dòng)處理這幾個(gè)字段,所以如果我們結(jié)構(gòu)體需要這幾個(gè)字段時(shí),我們可以直接在自定義結(jié)構(gòu)體中嵌入gorm.Model結(jié)構(gòu)體,gorm.Model的結(jié)構(gòu)體如下:
type Model struct { ID uint `gorm:"primary_key"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time `sql:"index"` }
所以,如果我們?cè)诮Y(jié)構(gòu)體Prize中嵌入gorm.Model,如下:
type Prize struct{ gorm.Model Name string }
這樣的話,則結(jié)構(gòu)體Prize包含有五個(gè)字段了。
我們這里所說(shuō)的數(shù)據(jù)庫(kù)遷移,即通過(guò)使用GROM提供的一系列方法,根據(jù)數(shù)據(jù)模型定義好的規(guī)則,進(jìn)行創(chuàng)建、刪除數(shù)據(jù)表等操作,也就是數(shù)據(jù)庫(kù)的DDL操作。
GORM提供對(duì)數(shù)據(jù)庫(kù)進(jìn)行DDL操作的方法,主要以下幾類:
數(shù)據(jù)表操作
//根據(jù)模型自動(dòng)創(chuàng)建數(shù)據(jù)表 func (s *DB) AutoMigrate(values ...interface{}) *DB //根據(jù)模型創(chuàng)建數(shù)據(jù)表 func (s *DB) CreateTable(models ...interface{}) *DB //刪除數(shù)據(jù)表,相當(dāng)于drop table語(yǔ)句 func (s *DB) DropTable(values ...interface{}) *DB //相當(dāng)于drop table if exsist 語(yǔ)句 func (s *DB) DropTableIfExists(values ...interface{}) *DB //根據(jù)模型判斷數(shù)據(jù)表是否存在 func (s *DB) HasTable(value interface{}) bool
列操作
//刪除數(shù)據(jù)表字段 func (s *DB) DropColumn(column string) *DB //修改數(shù)據(jù)表字段的數(shù)據(jù)類型 func (s *DB) ModifyColumn(column string, typ string) *DB
索引操作
//添加外鍵 func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate string) *DB //給數(shù)據(jù)表字段添加索引 func (s *DB) AddIndex(indexName string, columns ...string) *DB //給數(shù)據(jù)表字段添加索引 func (s *DB) AddUniqueIndex(indexName string, columns ...string) *DB
注意,下面示例程序中db變量代表gorm.DB對(duì)象,其初始化過(guò)程本篇不講了。
type User struct { Id int //對(duì)應(yīng)數(shù)據(jù)表的自增id Username string Password string Email string Phone string } func main(){ db.AutoMigrate(&Post{},&User{})//創(chuàng)建posts和users數(shù)據(jù)表 db.CreateTable(&Post{})//創(chuàng)建posts數(shù)據(jù)表 db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&Post{})//創(chuàng)建posts表時(shí)指存在引擎 db.DropTable(&Post{},"users")//刪除posts和users表數(shù)據(jù)表 db.DropTableIfExists(&Post{},"users")//刪除前會(huì)判斷posts和users表是否存在 //先判斷users表是否存在,再刪除users表 if db.HasTable("users") { db.DropTable("users") } //刪除數(shù)據(jù)表字段 db.Model(&Post{}).DropColumn("id") //修改字段數(shù)據(jù)類型 db.Model(&Post{}).ModifyColumn("id","varchar(255)") //建立posts與users表之間的外鍵關(guān)聯(lián) db.Model(&Post{}).AddForeignKey("uid", "users(id)", "RESTRICT", "RESTRICT") //給posts表的title字段添加索引 db.Model(&Post{}).AddIndex("index_title","title") //給users表的phone字段添加索引 db.Model(&User{}).AddUniqueIndex("index_phone","phone") }
關(guān)于如何在Golang中利用gorm添加一個(gè)數(shù)據(jù)庫(kù)排他鎖就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。