使用database/sql和driver包和Tx,似乎无法检测事务是否已提交或者回滚,而不需要再尝试一次并作为结果收到错误,然后检查错误以确定错误类型。我想能够从Tx对象中确定是否已提交。当然,我可以在使用Tx的函数中定义和设置另一个变量,但我有相当多的这样的函数,每次都是两倍(变量和分配)。我还有一个延迟函数来执行回滚,如果需要,则需要将bool变量传递给它。将Tx变量设置为nil是否可接受,在提交或回滚后GC会恢复任何内存,还是这样做是不好的,或者是否有更好的替代方案?
Begin()
、Commit()
和Rollback()
出现在同一个函数中。这样可以更容易地跟踪事务,并通过使用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
}
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 = tx.Commit()
将其返回值设置为err。我们不对Rollback
执行相同操作,因为err
是非空的,我们不想覆盖现有错误。return
设置了err
变量。请参见https://play.golang.org/p/66wWTJl0pH。 - Lukeerr
)。Rollback不会覆盖导致其发生的现有错误。 - Luke
var tx *Tx; snip; if cond { tx.Commit; tx=nil } else { tx.Rollback}; snip; if tx==nil { was commited } else { was rollbacked}
,但感觉很丑陋。 - Volker