在这篇文章中,作者展示了三种golang事务的写法。第一种写法很简单,程序流程也很清晰,但是事务处理和程序流程嵌入太深,容易遗漏,造成严重问题funcDoSomething()(errerror){tx,err:=db.Begin()iferr!=nil{return}deferfunc(){ifp:=recover();p!=nil{tx.Rollback()panic(p)//re-throwpanicafterRollback}}()if_,err=tx.Exec(...);err!=nil{tx.Rollback()return}if_,err=tx.Exec(...);err!=nil{tx.Rollback()return}//...err=tx。Commit()return}第二种写法下面的写法从程序流程中提取事务处理,不容易遗漏,但是范围是整个函数,程序流程不是很清楚funcDoSomething()(errerror){tx,err:=db.Begin()iferr!=nil{return}deferfunc(){ifp:=recover();p!=nil{tx.Rollback()panic(p)//re-throwpanicafterRollback}elseiferr!=nil{tx.Rollback()}else{err=tx.Commit()}}()if_,err=tx.Exec(...);err!=nil{return}if_,err=tx.Exec(...);err!=nil{return}//...return}第三种写法写法3是对写法2的进一步封装,写法更高级一点,缺点是同上)error)(errerror){tx,err:=db.Begin()iferr!=nil{return}deferfunc(){ifp:=recover();p!=nil{tx.Rollback()panic(p)//re-throwpanicafterRollback}elseiferr!=nil{tx.Rollback()}else{err=tx.Commit()}}()err=txFunc(tx)returnerr}funcDoSomething()error{returnTransact(db,func(tx*sql.Tx)error{if_,err:=tx.Exec(...);err!=nil{returnerr}if_,err:=tx.Exec(...);err!=nil{returnerr}})}经过总结和试验自己的写法,我采用了如下写法,defertx.Rollback()使得事务回滚总是在执行tx.Commit()时执行之后,tx.Rollback()起到关闭事务的作用。当程序因错误而终止时,tx.Rollback()起到回滚事务和关闭事务的作用。正常场景funcDoSomething()(errerror){tx,_:=db.Begin()defertx.Rollback()if_,err=tx.Exec(...);err!=nil{return}if_,err=tx。Exec(...);err!=nil{return}//...err=tx.Commit()return}循环场景(一)小事务在循环中提交一次,在循环内使用这种写法时,不能使用defer,所以事务部分应该分离成一个独立的函数funcDoSomething()(errerror){tx,_:=db.Begin()defertx.Rollback()if_,err=tx.Exec(...);err!=nil{return}if_,err=tx.Exec(...);err!=nil{return}//...err=tx.Commit()return}for{iferr:=DoSomething();err!=nil{//...}}(2)批量提交大交易的场景和正常场景一样,没有区别funcDoSomething()(errerror){tx,_:=db.Begin()defertx.Rollback()for{if_,err=tx.Exec(...);err!=nil{return}if_,err=tx.Exec(...);err!=nil{return}//...}err=tx.Commit()返回}
