使用数据库/ sql和驱动程序包和Tx,似乎无法检测事务是否已提交或回滚,而不尝试另一个事件并因此而收到错误,然后检查错误以确定错误.我想能够从Tx对象确定是否进行.当然,我可以在使用Tx的函数中定义和设置另一个变量,但是我有相当多的它们,每次都是次数(变量和赋值).如果需要,我还有一个延迟函数来执行Rollback,并且需要传递bool变量.
在提交或回滚之后将Tx变量设置为n可以接受,并且GC将恢复任何内存,或者是否否,还是有更好的选择?
在提交或回滚之后将Tx变量设置为n可以接受,并且GC将恢复任何内存,或者是否否,还是有更好的选择?
解决方法
为什么你甚至需要这样做?调用Begin()的函数也应该调用Commit()或Rollback()并返回一个适当的错误.
func (s Service) DoSomething() (err error) { tx,err := s.db.Begin() if err != nil { return } defer func() { if err != nil { tx.Rollback() return } 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 { switch p := p.(type) { case error: err = p default: err = fmt.Errorf("%s",p) } } if err != nil { tx.Rollback() return } err = tx.Commit() }() err = txFunc(tx) return err }
这样我就可以做到这一点:
func (s Service) DoSomething() error { return Transact(s.db,func (tx *sql.Tx) error { if _,err := tx.Exec(...); err != nil { return err } if _,err := tx.Exec(...); err != nil { return err } }) }
请注意,如果我的事务中的任何事情都是混乱,它将被事务处理程序自动处理.
在我的实际实现中,我传递一个接口而不是* sql.Tx,以防止对Commit()或Rollback()的不必要的调用.
这是一个简单的代码段,用于演示延迟工作方式(打印4,而不是5):
package main func test() (i int) { defer func() { i = 4 }() return 5 } func main() { println(test()) }