使用 database/sql 和驱动程序包和 Tx,如果不尝试另一个事务并因此收到错误,然后检查错误以确定事务的类型,就不可能检测到事务是否已提交或回滚。错误。我希望能够从 Tx 对象中确定是否已提交。当然,我可以在使用 Tx 的函数中定义和设置另一个变量,但我有很多变量,每次都是 2 次(变量和赋值)。如果需要,我还有一个延迟函数来执行回滚,并且需要将它传递给 bool 变量。
在 Commit 或 Rollback 之后将 Tx 变量设置为 nil 是否可以接受,并且 GC 是否会恢复任何内存,或者这是一个禁忌,还是有更好的选择?
问问题
38598 次
1 回答
151
您要确保Begin()
、Commit()
和Rollback()
出现在同一个函数中。它使交易更易于跟踪,并允许您使用defer
.
这是一个示例,它根据是否返回错误执行 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 {
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
}
使用上面的事务处理程序,我可以这样做:
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
})
}
这使我的交易保持简洁,并确保交易得到妥善处理。
在我的事务处理程序中,我recover()
用来捕获恐慌以确保立即发生回滚。如果预期会出现恐慌,我会重新引发恐慌以允许我的代码捕获它。在正常情况下不应该发生恐慌。应该返回错误。
如果我们不处理恐慌,事务最终将被回滚。当客户端断开连接或事务被垃圾收集时,数据库会回滚未提交的事务。但是,等待事务自行解决可能会导致其他(未定义)问题。所以最好尽快解决。
可能无法立即清楚的一件事是,defer
如果返回变量被捕获,则可以更改闭包内的返回值。在事务处理程序中,当err
(返回值)为 nil 时,事务被提交。调用Commit
也可以返回错误,因此我们将其返回设置为 err with err = tx.Commit()
。我们不这样做,Rollback
因为err
它是非零的,我们不想覆盖现有的错误。
于 2014-05-06T18:48:48.410 回答