Golang transaction 事務使用的正確姿勢

本文中作者展示了?golang?事務的三種寫法。
第一種寫法
這種寫法非常樸實,程序流程也非常明確,但是事務處理與程序流程嵌入太深,容易遺漏,造成嚴重的問題
func?DoSomething()?(err?error)?{
????tx,?err?:=?db.Begin()
????if?err?!=?nil?{
????????return
????}
????defer?func()?{
????????if?p?:=?recover();?p?!=?nil?{
????????????tx.Rollback()
????????????panic(p)??//?re-throw?panic?after?Rollback
????????}
????}()
????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????tx.Rollback()
????????return
????}
????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????tx.Rollback()
????????return
????}
????//?...
????err?=?tx.Commit()
????return
}
第二種寫法
下面這種寫法把事務處理從程序流程抽離了出來,不容易遺漏,但是作用域是整個函數(shù),程序流程不是很清晰
func?DoSomething()?(err?error)?{
????tx,?err?:=?db.Begin()
????if?err?!=?nil?{
????????return
????}
????defer?func()?{
????????if?p?:=?recover();?p?!=?nil?{
????????????tx.Rollback()
????????????panic(p)?//?re-throw?panic?after?Rollback
????????}?else?if?err?!=?nil?{
????????????tx.Rollback()
????????}?else?{
????????????err?=?tx.Commit()
????????}
????}()
????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????return
????}
????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????return
????}
????//?...
????return
}
第三種寫法
寫法三是對寫法二的進一步封裝,寫法高級一點,缺點同上
func?Transact(db?*sql.DB,?txFunc?func(*sql.Tx)?error)?(err?error)?{
????tx,?err?:=?db.Begin()
????if?err?!=?nil?{
????????return
????}
????defer?func()?{
????????if?p?:=?recover();?p?!=?nil?{
????????????tx.Rollback()
????????????panic(p)?//?re-throw?panic?after?Rollback
????????}?else?if?err?!=?nil?{
????????????tx.Rollback()
????????}?else?{
????????????err?=?tx.Commit()
????????}
????}()
????err?=?txFunc(tx)
????return?err
}
func?DoSomething()?error?{
????return?Transact(db,?func?(tx?*sql.Tx)?error?{
????????if?_,?err?:=?tx.Exec(...);?err?!=?nil?{
????????????return?err
????????}
????????if?_,?err?:=?tx.Exec(...);?err?!=?nil?{
????????????return?err
????????}
????})
}
我的寫法
經過總結和實驗,我采用了下面這種寫法,defer tx.Rollback() 使得事務回滾始終得到執(zhí)行。當 tx.Commit() 執(zhí)行后,tx.Rollback() 起到關閉事務的作用, 當程序因為某個錯誤中止,tx.Rollback() 起到回滾事務,同事關閉事務的作用。
普通場景
func?DoSomething()?(err?error)?{
??tx,?_?:=?db.Begin()
??defer?tx.Rollback()
??if?_,?err?=?tx.Exec(...);?err?!=?nil?{
??????return
??}
??if?_,?err?=?tx.Exec(...);?err?!=?nil?{
??????return
??}
??//?...
??err?=?tx.Commit()
??return
}
循環(huán)場景
(1) 小事務 每次循環(huán)提交一次 在循環(huán)內部使用這種寫法的時候,defer 不能使用,所以要把事務部分抽離到獨立的函數(shù)當中
func?DoSomething()?(err?error)?{
????tx,?_?:=?db.Begin()
????defer?tx.Rollback()
????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????return
????}
????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????return
????}
????//?...
????err?=?tx.Commit()
????return
}
for?{
????if?err?:=?DoSomething();?err?!=?nil{
?????????//?...
????}
}
(2) 大事務 批量提交 大事務的場景和普通場景是一樣的,沒有任何區(qū)別
func?DoSomething()?(err?error)?{
????tx,?_?:=?db.Begin()
????defer?tx.Rollback()
????for{
????????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????????return
????????}
????????if?_,?err?=?tx.Exec(...);?err?!=?nil?{
????????????return
????????}
????????//?...
????}
????err?=?tx.Commit()
????return
}轉自:http://k8i.cn/Ntg8T
文章轉載:CSDN
(版權歸原作者所有,侵刪)

點擊下方“閱讀原文”查看更多
評論
圖片
表情
