在實際業務中,很多操作需要保證?要么全部成功,要么全部失敗,否則可能造成數據不一致。比如:
- ? 用戶轉賬(A 賬戶扣款,B 賬戶加款)
- ? 下單支付(生成訂單、扣減庫存、記錄支付)
這種場景就需要用到?事務(Transaction)。
本文將帶你使用 Go 語言 +?GORM?實現數據庫事務處理。
一、事務的基本概念
事務(Transaction)是數據庫中一組不可分割的操作單元,具有四大特性(ACID):
- ??原子性(Atomicity):事務中的所有操作要么全部成功,要么全部失敗。
- ??一致性(Consistency):事務執行前后,數據必須保持一致。
- ??隔離性(Isolation):多個事務之間相互獨立,互不干擾。
- ??持久性(Durability):事務一旦提交,結果會永久保存。
二、GORM 事務處理方式
GORM 提供了三種事務使用方式:
- 1.?手動開啟、提交、回滾
- 2.?
db.Transaction()
?包裹函數(推薦) - 3.?嵌套事務(SavePoint / RollbackTo)
三、實戰案例:用戶轉賬
我們以一個“用戶轉賬”的例子來演示事務操作。
1. 定義模型
package?mainimport?("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.DBfunc?initDB()?{dsn?:=?"root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"var?err?errordb,?err?=?gorm.Open(mysql.Open(dsn),?&gorm.Config{})if?err?!=?nil?{log.Fatal("數據庫連接失敗:",?err)}_?=?db.AutoMigrate(&User{})
}
2. 使用事務完成轉賬
//?Transfer?轉賬函數:fromID?->?toID,金額?amount
func?Transfer(fromID,?toID?uint,?amount?int)?error?{return?db.Transaction(func(tx?*gorm.DB)?error?{var?from,?to?User//?查詢轉出賬戶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}//?查詢轉入賬戶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}//?所有操作成功?->?提交事務return?nil})
}
3. 測試轉賬
func?main()?{initDB()//?初始化兩位用戶db.Create(&User{Name:?"Alice",?Balance:?100})db.Create(&User{Name:?"Bob",?Balance:?50})//?轉賬:Alice?->?Bob?30元err?:=?Transfer(1,?2,?30)if?err?!=?nil?{log.Println("轉賬失敗:",?err)}?else?{log.Println("轉賬成功")}var?users?[]Userdb.Find(&users)log.Println("當前用戶余額:",?users)
}
四、運行效果
- 1. 初始狀態:
Alice:?100
Bob:???50
- 2. 轉賬成功后:
Alice:?70
Bob:???80
- 3. 如果 Alice 余額不足,事務會回滾:
轉賬失敗:?余額不足
Alice:?100
Bob:???50
五、事務處理注意事項
- 1.?事務函數返回?
error
- ? 返回?
nil
?→ 提交事務 - ? 返回?
error
?→ 回滾事務
- ? 返回?
- 2.?不要在事務中使用全局?
db
- ? 必須用傳入的?
tx
?對象,確保操作在同一事務內。
- ? 必須用傳入的?
- 3.?事務適合小而快的操作
- ? 長時間事務會占用鎖,可能導致性能下降。
六、總結
通過本案例,我們學習了:
- ? 事務的基本概念(ACID)
- ? GORM 的事務 API(推薦?
db.Transaction()
) - ? 用事務實現?用戶轉賬?功能
事務在業務中非常常見,掌握它能讓你的數據庫操作更加安全可靠。