本文介绍了数据库/ sql Tx - 检测提交或回滚的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用数据库/ sql和驱动程序包以及Tx,似乎无法检测事务是否已提交或回退而未尝试另一个事件并因此接收到错误,然后检查错误以确定错误的类型。我希望能够从Tx对象中确定是否已提交。当然,我可以在使用Tx的函数中定义和设置另一个变量,但我有相当多的变量,并且每次都是2次(变量和赋值)。如果需要,我也有一个延迟函数来执行回滚,并且它需要通过bool变量。


在Commit或Rollback之后将Tx变量设置为nil并且GC是否可以恢复任何内存,或者这是否定的,还是有更好的选择?

解决方案

为什么你甚至需要这样做?调用 Begin()的函数也应该调用 Commit() Rollback() / code>并返回相应的错误。



例如,此代码根据是否返回错误来执行提交或回滚:

  func(s Service)DoSomething()(err error){
tx,err:= s.db.Begin()
if err!= nil {
return
}
推迟func(){
if err!= nil {
tx.Rollback()
返回
}
err = tx.Commit()
}()
if _,err = tx.Exec(...); err!= nil {
return
}
if _,err = tx.Exec(...); err!= nil {
return
}
// ...
return
}

注意我是如何检查错误以查看我是否应该提交或回滚。然而,上面的例子并没有处理恐慌。



我不喜欢在每个数据库例程上执行提交/回滚逻辑,所以我通常将它们包装在一个事务处理程序中。有些东西是这样的:

$ $ $ $ c $ func Transact(db * sql.DB,txFunc func(* sql.Tx)error) (错误){
tx,err:= db.Begin()
if err!= nil {
return
}
defer func(){
如果p:= recover(); p!= nil {
tx.Rollback()
panic(p)//在回滚后重新引发恐慌
} else if err!= nil {
tx.Rollback )
} else {
err = tx.Commit()
}
}()
err = txFunc(tx)
return err
}

这允许我这样做:

  func(s服务)DoSomething()错误{
返回Transact(s.db,func(tx * sql.Tx)错误{
if _ ,err:= tx.Exec(...); err!= nil {
return err
}
if _,err:= tx.Exec(...); err! =无{
返回错误
}
})
}

请注意,如果我的事务中有任何事情发生了恐慌,它将自动由事务处理程序处理。



在我的实际实现中,我将一个接口而不是* sql.Tx传递给防止对 Commit() Ro的不必要调用这是一个简单的代码片段,演示 defer 是如何工作的(打印4,不是5):

pre $ $ $ $ $ $ $ $ $ $ $ $ $ func test()(i int){
defer func(){
i = 4
}()
return 5
}

func main(){
println (test())
}


Using the database/sql and driver packages and Tx, it is not possible it appears to detect whether a transaction has been committed or rolled-back without attempting another and receiving an error as a result, and then examining the error to determine the type of error. I would like to be able to determine from the Tx object whether committed or not. Sure, I can define and set another variable in the function that uses Tx, but I have quite a number of them, and it is times 2 every time (variable and assignment). I also have a deferred function to do a Rollback if needed, and it needs to be passed the bool variable.

Would it be acceptable to set the Tx variable to nil after a Commit or Rollback, and will the GC recover any memory, or is that a no-no, or is there a better alternative?

解决方案

Why would you even need to do this? The function calling Begin() should also be calling Commit() or Rollback() and returning an appropriate error.

For example, this code does a commit or rollback depending on whether an error is returned:

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
}

Notice how I'm checking error to see whether or not I should commit or rollback. The above example does not, however, handle panics.

I don't like doing commit/rollback logic on every database routine, so I usually wrap them in a transaction handler. Something along the lines of this:

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()
        } else {
            err = tx.Commit()
        }
    }()
    err = txFunc(tx)
    return err
}

This allows me to do this instead:

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
        }
    })
}

Notice that if anything inside my transaction panics it's automatically handled by the transaction handler.

In my actual implementation I pass an interface instead of *sql.Tx to prevent unwanted calls to Commit() or Rollback().

Here's a simple snippet to demonstrate how defer works (prints 4, not 5):

package main

func test() (i int) {
    defer func() {
        i = 4
    }()
    return 5
}

func main() {
    println(test())
}

http://play.golang.org/p/0OinYDWFlx

这篇关于数据库/ sql Tx - 检测提交或回滚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-22 11:04