使用数据库/ sql和驱动程序包以及Tx,它似乎无法检测事务是否已提交或回滚而不尝试另一个事务并因此接收错误,然后检查错误以确定类型错误.我希望能够从Tx对象确定是否已提交.当然,我可以在使用Tx的函数中定义和设置另一个变量,但我有很多它们,每次都是2次(变量和赋值).如果需要,我还有一个延迟函数来执行回滚,它需要传递bool变量.
在提交或回滚之后将Tx变量设置为nil是否可以接受,并且GC是否会恢复任何内存,或者是否为no-no,还是有更好的替代方案?
Luk*_*uke 112
你要确保Begin(),Commit()以及Rollback()同样的功能内出现.它使事务更容易跟踪,并允许您通过使用a来确保它们正确关闭defer.
下面是一个示例,它根据是否返回错误执行提交或回滚:
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
}
Run Code Online (Sandbox Code Playgroud)
这可能会有点重复.另一种方法是使用事务处理程序包装事务:
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() // err is non-nil; don't change it
} else {
err = tx.Commit() // err is nil; if Commit returns error update err
}
}()
err = txFunc(tx)
return err
}
Run Code Online (Sandbox Code Playgroud)
使用上面的交易处理程序,我可以这样做:
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
}
return nil
})
}
Run Code Online (Sandbox Code Playgroud)
这使我的交易简洁,并确保通过正确处理交易.
在我的事务处理程序中,我recover()用来捕获恐慌以确保立即发生回滚.如果恐慌预期,我会重新抛出恐慌,让我的代码能够抓住它.在正常情况下,不应发生恐慌.应该返回错误.
如果我们没有处理恐慌,那么交易最终将被回滚.当客户端断开连接或事务被垃圾收集时,数据库将回滚非提交的事务.但是,等待事务自行解决可能会导致其他(未定义)问题.因此,最好尽快解决它.
可能不会立即明确的一件事是,defer如果捕获了返回变量,则可以更改闭包内的返回值.在事务处理程序中,当err(返回值)为nil 时提交事务.调用Commit也可以返回错误,因此我们将其返回错误设置为err = tx.Commit().我们不会这样做Rollback因为err是非零并且我们不想覆盖现有的错误.