Go語言結(jié)合GORM實(shí)現(xiàn)數(shù)據(jù)庫(kù)事務(wù)處理的示例代碼
在實(shí)際業(yè)務(wù)中,很多操作需要保證 要么全部成功,要么全部失敗,否則可能造成數(shù)據(jù)不一致。比如:
- 用戶轉(zhuǎn)賬(A 賬戶扣款,B 賬戶加款)
- 下單支付(生成訂單、扣減庫(kù)存、記錄支付)
這種場(chǎng)景就需要用到 事務(wù)(Transaction)。
本文將帶你使用 Go 語言 + GORM 實(shí)現(xiàn)數(shù)據(jù)庫(kù)事務(wù)處理。
一、事務(wù)的基本概念
事務(wù)(Transaction)是數(shù)據(jù)庫(kù)中一組不可分割的操作單元,具有四大特性(ACID):
- 原子性(Atomicity):事務(wù)中的所有操作要么全部成功,要么全部失敗。
- 一致性(Consistency):事務(wù)執(zhí)行前后,數(shù)據(jù)必須保持一致。
- 隔離性(Isolation):多個(gè)事務(wù)之間相互獨(dú)立,互不干擾。
- 持久性(Durability):事務(wù)一旦提交,結(jié)果會(huì)永久保存。
二、GORM 事務(wù)處理方式
GORM 提供了三種事務(wù)使用方式:
- 手動(dòng)開啟、提交、回滾
- db.Transaction() 包裹函數(shù)(推薦)
- 嵌套事務(wù)(SavePoint / RollbackTo)
三、實(shí)戰(zhàn)案例:用戶轉(zhuǎn)賬
我們以一個(gè)“用戶轉(zhuǎn)賬”的例子來演示事務(wù)操作。
1. 定義模型
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100"`
Balance int // 賬戶余額
}
var db *gorm.DB
func initDB() {
dsn := "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("數(shù)據(jù)庫(kù)連接失敗:", err)
}
_ = db.AutoMigrate(&User{})
}
2. 使用事務(wù)完成轉(zhuǎn)賬
// Transfer 轉(zhuǎn)賬函數(shù):fromID -> toID,金額 amount
func Transfer(fromID, toID uint, amount int) error {
return db.Transaction(func(tx *gorm.DB) error {
var from, to User
// 查詢轉(zhuǎn)出賬戶
if err := tx.First(&from, fromID).Error; err != nil {
return err
}
if from.Balance < amount {
return fmt.Errorf("余額不足")
}
// 扣減余額
if err := tx.Model(&from).Update("balance", from.Balance-amount).Error; err != nil {
return err
}
// 查詢轉(zhuǎn)入賬戶
if err := tx.First(&to, toID).Error; err != nil {
return err
}
// 增加余額
if err := tx.Model(&to).Update("balance", to.Balance+amount).Error; err != nil {
return err
}
// 所有操作成功 -> 提交事務(wù)
return nil
})
}
3. 測(cè)試轉(zhuǎn)賬
func main() {
initDB()
// 初始化兩位用戶
db.Create(&User{Name: "Alice", Balance: 100})
db.Create(&User{Name: "Bob", Balance: 50})
// 轉(zhuǎn)賬:Alice -> Bob 30元
err := Transfer(1, 2, 30)
if err != nil {
log.Println("轉(zhuǎn)賬失敗:", err)
} else {
log.Println("轉(zhuǎn)賬成功")
}
var users []User
db.Find(&users)
log.Println("當(dāng)前用戶余額:", users)
}
四、運(yùn)行效果
初始狀態(tài):
Alice: 100
Bob: 50
轉(zhuǎn)賬成功后:
Alice: 70
Bob: 80
如果 Alice 余額不足,事務(wù)會(huì)回滾:
轉(zhuǎn)賬失敗: 余額不足
Alice: 100
Bob: 50
五、事務(wù)處理注意事項(xiàng)
事務(wù)函數(shù)返回 error
- 返回
nil→ 提交事務(wù) - 返回
error→ 回滾事務(wù)
不要在事務(wù)中使用全局 db
必須用傳入的 tx 對(duì)象,確保操作在同一事務(wù)內(nèi)。
事務(wù)適合小而快的操作
長(zhǎng)時(shí)間事務(wù)會(huì)占用鎖,可能導(dǎo)致性能下降。
六、總結(jié)
通過本案例,我們學(xué)習(xí)了:
- 事務(wù)的基本概念(ACID)
- GORM 的事務(wù) API(推薦
db.Transaction()) - 用事務(wù)實(shí)現(xiàn) 用戶轉(zhuǎn)賬 功能
事務(wù)在業(yè)務(wù)中非常常見,掌握它能讓你的數(shù)據(jù)庫(kù)操作更加安全可靠。
到此這篇關(guān)于Go語言結(jié)合GORM實(shí)現(xiàn)數(shù)據(jù)庫(kù)事務(wù)處理的示例代碼的文章就介紹到這了,更多相關(guān)Go語言數(shù)據(jù)庫(kù)事務(wù)處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go利用embed包實(shí)現(xiàn)靜態(tài)資源嵌入的方案
在很多?Go?項(xiàng)目中,程序除了核心代碼之外,往往還需要依賴一些靜態(tài)資源,傳統(tǒng)做法通常是把這些文件與程序一起打包部署,但這樣會(huì)帶來一些問題,例如部署復(fù)雜、路徑管理困難、發(fā)布時(shí)需要攜帶額外文件等,因此本文給大家介紹了Go利用embed包實(shí)現(xiàn)靜態(tài)資源嵌入的方案2026-03-03
Go語言在終端打開實(shí)現(xiàn)進(jìn)度條處理數(shù)據(jù)方法實(shí)例
這篇文章主要介紹了Go語言在終端打開實(shí)現(xiàn)進(jìn)度條處理數(shù)據(jù)方法實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
go語言開發(fā)環(huán)境安裝及第一個(gè)go程序(推薦)
這篇文章主要介紹了go語言開發(fā)環(huán)境安裝及第一個(gè)go程序,這篇通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
go語言轉(zhuǎn)換json字符串為json數(shù)據(jù)的實(shí)現(xiàn)
本文主要介紹了go語言轉(zhuǎn)換json字符串為json數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03

