GO中GORM 使用教程
GORM 是一個用于 Go 語言的 ORM(Object-Relational Mapping) 庫。它將關(guān)系型數(shù)據(jù)庫的表與 Go 語言中的結(jié)構(gòu)體相映射,允許開發(fā)者以面向?qū)ο蟮姆绞讲僮鲾?shù)據(jù)庫,而不需要直接編寫 SQL 語句。通過 GORM,開發(fā)者可以利用 Go 語言的結(jié)構(gòu)體和方法來執(zhí)行常見的數(shù)據(jù)庫操作(如查詢、插入、更新、刪除等),大大簡化了與數(shù)據(jù)庫的交互過程。
1. 安裝 GORM
首先,你需要安裝 GORM 庫。打開終端并運行以下命令:
go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite # 示例數(shù)據(jù)庫,可以根據(jù)需求更換為其他數(shù)據(jù)庫驅(qū)動
2. 創(chuàng)建基礎(chǔ)結(jié)構(gòu)體
ORM的一個核心概念是結(jié)構(gòu)體,它代表數(shù)據(jù)庫表的一個映射。例如,假設(shè)你有一個“用戶”表,我們可以創(chuàng)建一個 User
結(jié)構(gòu)體來表示它。
package main import ( "gorm.io/driver/sqlite" "gorm.io/gorm" ) type User struct { ID uint `gorm:"primaryKey"` // 主鍵 Name string `gorm:"size:100"` // 用戶名字段,限制長度為100 Age uint // 用戶年齡 CreatedAt time.Time // 創(chuàng)建時間(GORM 會自動管理) } func main() { // 創(chuàng)建數(shù)據(jù)庫連接 db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{}) if err != nil { panic("failed to connect to the database") } // 自動遷移:通過GORM根據(jù)結(jié)構(gòu)體自動創(chuàng)建表 db.AutoMigrate(&User{}) }
在 GORM 中,結(jié)構(gòu)體字段的標(biāo)簽(tags)用于定義和控制如何將 Go 結(jié)構(gòu)體映射到數(shù)據(jù)庫表的列。GORM 支持很多標(biāo)簽,可以配置數(shù)據(jù)庫表的列屬性、索引、關(guān)系等。以下是常見的 GORM 字段標(biāo)簽和它們的作用:
標(biāo)簽 | 描述 | 示例 |
---|---|---|
primaryKey | 指定字段為主鍵。 | gorm:"primaryKey" |
size | 指定字段的大小,通常用于字符串字段。 | gorm:"size:100" |
unique | 創(chuàng)建唯一索引,確保字段值唯一。 | gorm:"unique" |
not null | 指定字段不能為空。 | gorm:"not null" |
default | 指定字段的默認值。 | gorm:"default:0" |
autoIncrement | 設(shè)置字段為自增字段。通常用于整數(shù)類型的主鍵。 | gorm:"autoIncrement" |
index | 為字段創(chuàng)建索引。 | gorm:"index" |
uniqueIndex | 為字段創(chuàng)建唯一索引。 | gorm:"uniqueIndex" |
index:<name> | 創(chuàng)建帶有自定義名稱的索引。 | gorm:"index:idx_name" |
foreignKey | 指定外鍵字段,在一對多或多對多關(guān)系中使用。 | gorm:"foreignKey:UserID" |
references | 指定外鍵關(guān)系中引用的字段。 | gorm:"references:ID" |
embedded | 嵌入一個結(jié)構(gòu)體,扁平化嵌入的結(jié)構(gòu)體字段到父結(jié)構(gòu)體中。 | gorm:"embedded" |
preload | 在查詢時預(yù)加載關(guān)聯(lián)數(shù)據(jù)。 | gorm:"preload" |
createdAt | 自動生成的創(chuàng)建時間戳字段。 | gorm:"autoCreateTime" |
updatedAt | 自動生成的更新時間戳字段。 | gorm:"autoUpdateTime" |
deletedAt | 自動生成的刪除時間戳字段,支持軟刪除。 | gorm:"softDelete" |
softDelete | 用于支持軟刪除功能。通常與 DeletedAt 配合使用。 | gorm:"index" |
column | 指定數(shù)據(jù)庫中的列名,當(dāng)字段名與列名不一致時使用。 | gorm:"column:db_column_name" |
type | 指定字段在數(shù)據(jù)庫中的類型,通常用于特殊類型(如 JSON)。 | gorm:"type:text" |
many2many | 用于多對多關(guān)系,指定連接表的名稱。 | gorm:"many2many:user_posts" |
這個表格展示了 GORM 中常用的字段標(biāo)簽以及它們?nèi)绾斡绊憯?shù)據(jù)庫表結(jié)構(gòu),幫助開發(fā)者更好地理解和使用 GORM 進行數(shù)據(jù)庫操作。
3. 數(shù)據(jù)庫連接與配置
在上面的示例中,我們使用了 gorm.Open()
來連接 SQLite 數(shù)據(jù)庫。如果你使用 MySQL 或 Postgres 等其他數(shù)據(jù)庫,可以更換相應(yīng)的驅(qū)動。
例如,連接到 MySQL 數(shù)據(jù)庫的代碼如下:
db, err := gorm.Open(mysql.Open("user:password@tcp(localhost:3306)/dbname"), &gorm.Config{})
4. 數(shù)據(jù)庫操作
4.1 創(chuàng)建記錄
你可以使用 Create
方法來插入數(shù)據(jù)。下面是如何插入一個新用戶:
func createUser(db *gorm.DB) { user := User{Name: "John Doe", Age: 30} result := db.Create(&user) if result.Error != nil { panic("Failed to create user") } fmt.Println("User created:", user.ID) }
代碼中db.Create(&user)
使用 &
符號是因為我們要傳遞結(jié)構(gòu)體 user
的 指針 給 Create
方法。具體來說,這樣做有以下幾個原因:
指針傳遞可以修改原結(jié)構(gòu)體
GORM 的
Create
方法接受結(jié)構(gòu)體的 指針,這樣它可以直接修改原始結(jié)構(gòu)體的值,而不僅僅是副本。通過傳遞指針,GORM 能夠在插入數(shù)據(jù)庫的過程中修改結(jié)構(gòu)體(例如,給結(jié)構(gòu)體的字段賦值,例如數(shù)據(jù)庫自動生成的ID
或CreatedAt
字段),確保結(jié)構(gòu)體反映數(shù)據(jù)庫中的最新數(shù)據(jù)。例如,
user
的ID
字段會在插入數(shù)據(jù)庫時由 GORM 自動賦值(通常是自增的主鍵),如果你傳遞的是結(jié)構(gòu)體的指針,Create
方法可以直接更新user
結(jié)構(gòu)體中的ID
字段。避免復(fù)制大結(jié)構(gòu)體
如果你傳遞的是結(jié)構(gòu)體的副本,GORM 會先創(chuàng)建一個結(jié)構(gòu)體的拷貝并將其插入數(shù)據(jù)庫。這對于較大的結(jié)構(gòu)體來說可能會浪費內(nèi)存并降低性能。而傳遞指針避免了復(fù)制整個結(jié)構(gòu)體,只是傳遞了結(jié)構(gòu)體的內(nèi)存地址,性能更高。
GORM 的工作方式
GORM 內(nèi)部使用了指針來標(biāo)識結(jié)構(gòu)體字段的變化。通過傳遞指針,GORM 可以確定結(jié)構(gòu)體的變化并進行相應(yīng)的處理。例如,在執(zhí)行
Create
時,GORM 會檢查結(jié)構(gòu)體的指針,判斷該字段是否已經(jīng)賦值、是否需要自動填充等。
4.2 查詢記錄
GORM 提供了多種查詢方式,可以通過結(jié)構(gòu)體查詢、條件查詢等方式來獲取數(shù)據(jù)。
獲取單條記錄
func getUser(db *gorm.DB) { var user User result := db.First(&user, 1) // 查找 user 表中主鍵為 1 的記錄,并將其填充到 user 結(jié)構(gòu)體中 if result.Error != nil { panic("User not found") } fmt.Println("User:", user) }
db.First
是 GORM 提供的一個查詢方法,用于從數(shù)據(jù)庫中獲取 第一條 滿足條件的記錄。它通常用于根據(jù)主鍵或其他條件查詢數(shù)據(jù)。
db.First
的基本語法:
db.First(&model, conditions...)
&model
是一個指針參數(shù),表示查詢的結(jié)果將會填充到這個結(jié)構(gòu)體中。conditions...
是查詢的條件,可以是主鍵或其他字段。
如果查詢成功,db.First
會把查詢到的記錄填充到 model
指針?biāo)赶虻慕Y(jié)構(gòu)體里。如果沒有找到記錄,它會返回一個錯誤。
在 db.First(&user, 1)
中,&user
是指向 user
結(jié)構(gòu)體的指針。這里傳遞指針是因為 GORM 要修改 user
結(jié)構(gòu)體的值(即填充查詢結(jié)果)。
- 通過傳遞結(jié)構(gòu)體的指針,GORM 可以將查詢結(jié)果直接賦值到
user
結(jié)構(gòu)體中。 - 如果你傳遞的是結(jié)構(gòu)體本身,而不是指針,查詢結(jié)果將不會填充到結(jié)構(gòu)體中,因為結(jié)構(gòu)體會作為副本傳遞到
db.First
方法,而 GORM 需要能夠修改原始結(jié)構(gòu)體的字段值。
獲取多個記錄
func getUsers(db *gorm.DB) { var users []User result := db.Find(&users) if result.Error != nil { panic("No users found") } fmt.Println("Users:", users) }
db.Find
是 GORM 提供的查詢方法之一,用于查找多個記錄并將其存儲到傳入的切片結(jié)構(gòu)體中。
Find
方法會根據(jù)傳入的條件來查找記錄,可以是簡單的查詢(如所有記錄),也可以是有條件的查詢(如按字段值過濾)。- 傳遞給
Find
的參數(shù)是一個指針,它會將查詢到的記錄填充到指向的切片中。
db.Find(&users)
會從數(shù)據(jù)庫中查找所有記錄(或者根據(jù)傳入的查詢條件查找記錄)并將它們填充到 users
切片中。查詢的結(jié)果會是一個結(jié)構(gòu)體的集合。Find 方法默認返回所有滿足條件的記錄。
- 如果查詢沒有條件,
Find
將返回數(shù)據(jù)庫表中的所有記錄。 - 如果你傳遞了查詢條件,
Find
將根據(jù)條件過濾結(jié)果。
Find 方法的其他功能
- 查詢條件:你可以通過傳遞查詢條件來限制查詢的結(jié)果。例如,如果你想查找年齡大于 30 的所有用戶,可以這么寫:
db.Find(&users, "age > ?", 30)
這個查詢會返回所有年齡大于 30 的用戶。
- 分頁:
Find
還支持分頁查詢。你可以通過Limit
和Offset
方法來實現(xiàn)分頁查詢。例如,查詢前 10 條記錄:
db.Limit(10).Find(&users)
- 排序:你也可以通過
Order
方法來指定查詢結(jié)果的排序方式。例如,按年齡排序:
db.Order("age desc").Find(&users)
- 返回記錄數(shù):
Find
方法還會返回查詢的結(jié)果,包括查詢到的記錄數(shù)。如果沒有記錄,它會返回一個空的切片。
4.3 更新記錄
在 GORM 中,更新記錄是一個常見的操作。你可以通過 GORM 提供的幾種方法來更新記錄。以下將詳細介紹 GORM 中更新記錄的方式,包含基本更新、部分更新、批量更新等操作,并解釋每種方法的具體用法和注意事項。
基本更新:db.Save 方法
db.Save
方法用于保存(或更新)結(jié)構(gòu)體中的數(shù)據(jù)。如果結(jié)構(gòu)體的主鍵已經(jīng)存在,GORM 會執(zhí)行 更新操作;如果主鍵不存在,GORM 會執(zhí)行 插入操作(也稱為 “upsert”)。因此,db.Save
不僅適用于更新已有記錄,也適用于插入新記錄。
示例
func main() { var user User db.First(&user, 1) // 查找主鍵為 1 的用戶 user.Name = "Alice Updated" // 修改字段 user.Age = 30 db.Save(&user) // 更新記錄 }
db.Save(&user)
會檢查user
是否已有主鍵值(假設(shè)主鍵存在)。如果存在,它將執(zhí)行更新操作,將user
結(jié)構(gòu)體中修改的字段更新到數(shù)據(jù)庫中。- 如果主鍵不存在,它會將
user
插入到數(shù)據(jù)庫中。
注意:
Save
會更新所有非零字段(即結(jié)構(gòu)體中的字段如果是空值,可能不會被更新),并且會更新所有字段,即使你沒有顯式修改某個字段。- 如果你希望只更新某些字段,應(yīng)該使用
Updates
或Update
方法。
更新部分字段:db.Updates 方法
db.Updates
方法允許你更新結(jié)構(gòu)體中的 部分字段,而不是全部字段。它是一個更精確的更新方法,通常用于僅更新結(jié)構(gòu)體中某些修改了的字段。
示例
func main() { var user User db.First(&user, 1) // 查找主鍵為 1 的用戶 db.Model(&user).Updates(User{Name: "Bob Updated", Age: 35}) }
在這個例子中:
db.Model(&user).Updates(User{Name: "Bob Updated", Age: 35})
只會更新user
結(jié)構(gòu)體中的Name
和Age
字段。db.Model(&user)
表明更新的是user
結(jié)構(gòu)體對應(yīng)的數(shù)據(jù)庫記錄。Updates
方法中的參數(shù)可以是一個結(jié)構(gòu)體(如User{Name: "Bob Updated"}
),也可以是一個map[string]interface{}
(鍵是字段名,值是要更新的值)。
注意:
Updates
會忽略零值字段(如空字符串、零整數(shù)等),如果某個字段的值為零,它不會被更新。db.Model(&user)
用于指定要更新的模型或表。Updates
會將修改過的字段進行更新,但不會更新模型中未指定的字段。
單個字段更新:db.Update 方法
如果你只需要更新某個單獨的字段,可以使用 db.Update
方法。該方法用于 更新單個字段,是 db.Updates
的簡化版本,適合只更新單一字段的場景。
示例
func main() { var user User db.First(&user, 1) // 查找主鍵為 1 的用戶 db.Model(&user).Update("Age", 40) // 只更新 Age 字段 }
db.Model(&user).Update("Age", 40)
會將Age
字段更新為40
,其他字段保持不變。Update
方法適用于你只需要更新單個字段的情況。
注意:
Update
方法只更新指定的字段,不會影響其他字段。- 如果字段的值為零值,
Update
也會更新該字段(沒有零值忽略
的機制)。
批量更新:db.UpdateColumn 和 db.UpdateColumns
GORM 還提供了 UpdateColumn
和 UpdateColumns
方法,主要用于 批量更新 字段。這些方法與 Update
方法類似,但它們不會觸發(fā) GORM 的鉤子(如 BeforeSave
、AfterSave
等)。
UpdateColumn 示例
db.Model(&user).UpdateColumn("Age", 45)
UpdateColumn
不會觸發(fā) GORM 的 BeforeSave
和 AfterSave
鉤子,因此適用于需要繞過這些鉤子的情況。
UpdateColumns 示例
db.Model(&user).UpdateColumns(map[string]interface{}{"Age": 50, "Name": "Charlie Updated"})
UpdateColumns
會根據(jù)傳入的字段進行批量更新。與 Update
不同,它不會觸發(fā)模型的鉤子。
注意:
- 這兩個方法直接更新字段,不會對字段的零值進行忽略。
- 它們只進行 單字段的原子更新,不會涉及到多表關(guān)聯(lián)等操作。
條件更新:db.Where 和 db.Updates
你可以在更新時通過 Where
方法指定更新的條件。Where
方法可以與 Updates
或 Update
一起使用,以便進行條件更新。
示例
db.Model(&User{}).Where("age > ?", 30).Updates(User{Name: "Updated Name"})
- 這個示例中,
Where("age > ?", 30)
限定了更新條件,只有年齡大于 30 的用戶才會被更新。 Updates(User{Name: "Updated Name"})
更新所有符合條件的用戶的Name
字段。
注意:
Where
可以幫助你構(gòu)造復(fù)雜的更新條件,但也可以根據(jù)需要單獨使用(例如,按 ID 更新某些記錄)。
批量更新(多個記錄)
你可以使用 db.Model()
方法和 db.Updates()
方法來批量更新多個記錄。下面是一個批量更新的示例:
示例
db.Model(&User{}).Where("age > ?", 30).Updates(User{Name: "Batch Update"})
- 這個例子會更新所有
age > 30
的用戶,將它們的Name
字段修改為"Batch Update"
。
注意:
Updates
會更新所有符合條件的記錄,而不是只更新一個記錄。
使用事務(wù)更新多個記錄
如果你需要確保多個更新操作的原子性,可以將更新操作放入一個事務(wù)中。在 GORM 中,事務(wù)通過 db.Begin()
開始,db.Commit()
提交,db.Rollback()
回滾。
示例
tx := db.Begin() // 執(zhí)行多個更新操作 tx.Model(&User{}).Where("age > ?", 30).Updates(User{Name: "Transactional Update"}) tx.Model(&User{}).Where("name = ?", "Bob").Update("Age", 40) if err := tx.Commit().Error; err != nil { tx.Rollback() fmt.Println("Error:", err) return }
db.Begin()
開始一個事務(wù)。tx.Commit()
提交事務(wù),tx.Rollback()
在出錯時回滾事務(wù),確保所有操作的原子性。
4.4 刪除記錄
刪除記錄可以使用 Delete
方法:
func deleteUser(db *gorm.DB) { var user User db.First(&user, 1) // 查找要刪除的用戶 // 刪除用戶 result := db.Delete(&user) if result.Error != nil { panic("Failed to delete user") } fmt.Println("User deleted:", user.ID) }
5. 關(guān)系與關(guān)聯(lián)查詢
GORM 支持表之間的關(guān)系映射。比如,我們有 User
和 Post
之間的關(guān)系。一個用戶可以有多個帖子,可以使用 has many
關(guān)系。
5.1 定義關(guān)聯(lián)結(jié)構(gòu)體
type Post struct { ID uint Title string Body string UserID uint // 外鍵 User User // 關(guān)聯(lián)的 User }
5.2 關(guān)聯(lián)查詢
假設(shè)我們有 User
和 Post
兩個表,你可以使用 Preload
來加載關(guān)聯(lián)的 Post
數(shù)據(jù)。
func getUserWithPosts(db *gorm.DB) { var user User db.Preload("Posts").First(&user, 1) fmt.Println("User:", user.Name) fmt.Println("Posts:", user.Posts) }
5.3 創(chuàng)建關(guān)聯(lián)記錄
當(dāng)你插入一個帶有關(guān)聯(lián)的記錄時,可以使用 Create
方法來同時插入主表和從表數(shù)據(jù):
func createUserWithPosts(db *gorm.DB) { user := User{Name: "Alice", Age: 28, Posts: []Post{ {Title: "Post 1", Body: "This is the first post"}, {Title: "Post 2", Body: "This is the second post"}, }} db.Create(&user) fmt.Println("User and Posts created:", user) }
6. 事務(wù)
在 GORM 中,事務(wù)(Transaction) 是一個非常重要的概念,尤其是在需要確保多個數(shù)據(jù)庫操作要么全部成功,要么全部失敗的情況下。事務(wù)能夠保證操作的原子性、一致性、隔離性和持久性(即 ACID 特性)。如果在事務(wù)中的某個操作失敗,事務(wù)可以回滾,使得數(shù)據(jù)庫回到事務(wù)開始之前的狀態(tài)。
事務(wù)(Transaction)是一組數(shù)據(jù)庫操作,它們要么全部執(zhí)行,要么在發(fā)生錯誤時全部不執(zhí)行。事務(wù)在數(shù)據(jù)庫操作中提供了 原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation) 和 持久性(Durability)(合稱為ACID特性):
- 原子性(Atomicity):事務(wù)中的所有操作要么都執(zhí)行,要么都不執(zhí)行。即事務(wù)是不可分割的整體。
- 一致性(Consistency):事務(wù)執(zhí)行前后,數(shù)據(jù)庫的狀態(tài)必須是一致的,符合數(shù)據(jù)庫的完整性約束。
- 隔離性(Isolation):一個事務(wù)的執(zhí)行不會被其他事務(wù)干擾。事務(wù)的執(zhí)行是相互隔離的。
- 持久性(Durability):一旦事務(wù)提交,其對數(shù)據(jù)庫的更改是永久性的,不會丟失。
在 GORM 中,你可以通過 db.Begin()
來啟動一個事務(wù),使用 tx.Commit()
來提交事務(wù),使用 tx.Rollback()
來回滾事務(wù)。以下是 GORM 中事務(wù)的常見用法。
6.1. 開始事務(wù):db.Begin()
你可以通過 db.Begin()
啟動一個事務(wù)。這個方法會返回一個事務(wù)對象(*gorm.DB
類型),通過這個對象你可以執(zhí)行數(shù)據(jù)庫操作。
tx := db.Begin() // 開始事務(wù)
db.Begin()
會創(chuàng)建一個事務(wù)。- 返回的
tx
是事務(wù)對象,所有的數(shù)據(jù)庫操作都應(yīng)通過tx
來執(zhí)行,而不是直接使用db
。
6.2 執(zhí)行事務(wù)中的操作
在事務(wù)中,你可以進行一系列的數(shù)據(jù)庫操作。所有的操作都應(yīng)該通過事務(wù)對象 tx
來執(zhí)行,而不是直接通過 db
執(zhí)行。
示例
tx := db.Begin() // 開始事務(wù) // 執(zhí)行多個數(shù)據(jù)庫操作 if err := tx.Create(&user).Error; err != nil { tx.Rollback() // 操作失敗,回滾事務(wù) return err } if err := tx.Model(&user).Update("Age", 30).Error; err != nil { tx.Rollback() // 操作失敗,回滾事務(wù) return err }
tx.Create(&user)
會在事務(wù)中插入一條記錄。tx.Model(&user).Update("Age", 30)
會在事務(wù)中更新該記錄。
6.3 提交事務(wù):tx.Commit()
當(dāng)所有的操作都執(zhí)行成功時,可以調(diào)用 tx.Commit()
提交事務(wù),將所有的變更永久保存到數(shù)據(jù)庫。
if err := tx.Commit().Error; err != nil { tx.Rollback() // 提交失敗,回滾事務(wù) return err }
tx.Commit()
會提交事務(wù),執(zhí)行所有的操作并將它們持久化到數(shù)據(jù)庫。
6.4 回滾事務(wù):tx.Rollback()
如果在事務(wù)過程中遇到錯誤,應(yīng)該調(diào)用 tx.Rollback()
來回滾事務(wù)。這樣,所有在事務(wù)中執(zhí)行的操作都會撤銷,數(shù)據(jù)庫將恢復(fù)到事務(wù)開始前的狀態(tài)。
if err := tx.Rollback().Error; err != nil { fmt.Println("Error during rollback:", err) return err }
tx.Rollback()
會撤銷事務(wù)中的所有操作。
6.5 在事務(wù)中使用錯誤處理
通常,事務(wù)中的操作需要進行錯誤處理。只要有任何一項操作失敗,應(yīng)該調(diào)用 tx.Rollback()
進行回滾。
tx := db.Begin() // 執(zhí)行操作 1 if err := tx.Create(&user).Error; err != nil { tx.Rollback() // 錯誤發(fā)生,回滾事務(wù) return err } // 執(zhí)行操作 2 if err := tx.Model(&user).Update("Age", 30).Error; err != nil { tx.Rollback() // 錯誤發(fā)生,回滾事務(wù) return err } // 所有操作成功,提交事務(wù) if err := tx.Commit().Error; err != nil { tx.Rollback() // 提交失敗,回滾事務(wù) return err }
6.6 事務(wù)中的多表操作
在事務(wù)中,你可以操作多個表,只要使用同一個事務(wù)對象 tx
,所有的表操作都將在一個事務(wù)內(nèi)完成。
示例:多表操作
tx := db.Begin() // 插入用戶表 if err := tx.Create(&user).Error; err != nil { tx.Rollback() return err } // 更新訂單表 if err := tx.Model(&order).Update("Status", "Shipped").Error; err != nil { tx.Rollback() return err } // 提交事務(wù) if err := tx.Commit().Error; err != nil { tx.Rollback() return err }
- 這里插入了一個用戶并更新了訂單狀態(tài),所有操作都在同一個事務(wù)中進行。
6.7 事務(wù)的嵌套
GORM 不直接支持嵌套事務(wù)(即在一個事務(wù)中開啟另一個事務(wù))。但是,你可以通過手動管理事務(wù)嵌套。在嵌套事務(wù)中,只有最外層的事務(wù)會決定是否提交或回滾。
tx := db.Begin() // 外部事務(wù)操作 if err := tx.Create(&user).Error; err != nil { tx.Rollback() return err } nestedTx := tx.Begin() // 開始嵌套事務(wù) // 嵌套事務(wù)操作 if err := nestedTx.Model(&order).Update("Status", "Shipped").Error; err != nil { nestedTx.Rollback() // 嵌套事務(wù)回滾 tx.Rollback() // 外部事務(wù)回滾 return err } nestedTx.Commit() // 嵌套事務(wù)提交 tx.Commit() // 外部事務(wù)提交
- 上述代碼演示了如何在一個事務(wù)中手動開啟一個嵌套事務(wù)。嵌套事務(wù)的提交和回滾會影響最外層事務(wù)。
6.8 事務(wù)中的并發(fā)問題
在事務(wù)中使用并發(fā)操作時,必須小心并發(fā)引起的 數(shù)據(jù)競爭 和 死鎖 問題。GORM 默認使用 隔離級別 為 ReadCommitted
,你可以通過配置數(shù)據(jù)庫的事務(wù)隔離級別來避免一些并發(fā)問題。
tx := db.Begin().Set("gorm:query_option", "LOCK IN SHARE MODE") // 事務(wù)操作
此時,LOCK IN SHARE MODE
會在查詢時加鎖,避免其他事務(wù)修改同一行數(shù)據(jù),防止數(shù)據(jù)不一致。
總結(jié)
GORM 是一個功能強大且易于使用的 Go 語言 ORM 庫,能夠讓開發(fā)者以面向?qū)ο蟮姆绞脚c數(shù)據(jù)庫交互,減少了 SQL 語句的編寫和管理的復(fù)雜度。它適合需要處理數(shù)據(jù)庫的 Go 項目,特別是那些涉及大量數(shù)據(jù)操作、需要事務(wù)支持和多表關(guān)聯(lián)的應(yīng)用。
到此這篇關(guān)于GO中GORM 使用教程的文章就介紹到這了,更多相關(guān)GO GORM 使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go實現(xiàn)自動解壓縮包以及讀取docx/doc文件內(nèi)容詳解
在開發(fā)過程中,我們常常需要處理壓縮包和文檔文件。本文將介紹如何使用Go語言自動解壓縮包和讀取docx/doc文件,需要的可以參考一下2023-03-03Golang使用singleflight解決并發(fā)重復(fù)請求
高并發(fā)的場景下,經(jīng)常會出現(xiàn)并發(fā)重復(fù)請求資源的情況,singleflight是golang內(nèi)置的一個包,這個包提供了對重復(fù)函數(shù)調(diào)用的抑制功能,所以下面我們就來看看如何使用它解決并發(fā)重復(fù)請求吧2023-08-08go將request?body綁定到不同的結(jié)構(gòu)體中教程
這篇文章主要為大家介紹了go將request?body綁定到不同的結(jié)構(gòu)體中教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10Ubuntu18.04 LTS搭建GO語言開發(fā)環(huán)境過程解析
這篇文章主要介紹了Ubuntu18.04 LTS搭建GO語言開發(fā)環(huán)境過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11