2013-04-24 15 views
21

データベース/ SQLとドライバパッケージとTxを使用すると、トランザクションがコミットまたはロールバックされていないかどうかを検出することはできません。エラーの種類を判断するためにエラーを調べます。私はTxオブジェクトからコミットされているかどうかを判断できるようにしたいと思います。確かに、Txを使用する関数で別の変数を定義して設定することはできますが、かなりの数があり、毎回2回(変数と代入)です。私はまた、必要に応じてロールバックを行う遅延関数を持っており、bool変数を渡す必要があります。

コミットまたはロールバック後にTx変数をnilに設定しても問題はありません.GCはメモリを回復しますか、それともno-noですか、それとも良い方法がありますか?データベース/ SQL Tx検出コミットまたはロールバック

+1

私は問題を理解していればわかりません。コミットまたはロールバックのどちらかでトランザクションを終了する必要があります。そのため、あなたは何をしたのか分かりますが、余分な変数でこれを覚えたくありませんか? Txとboolを独自のRememberingTxでラップすると、行数が少し減ります。 GCの質問について:あなたが無しにしてもいなくても問題はありません。メモリが残っていなければ、メモリは再利用されます。そうです:はい、あなたは 'var tx * Tx;スナップ; if cond {tx.Commit; tx = nil} else {tx.Rollback};スナップ;もしtx == nil {コミットされた} else {ロールバックされた} 'しかし、それは醜い感じです。 – Volker

+0

これはまあまあですが、Txがnilでない場合にロールバックを行う遅延関数があります。いったんトランザクションがコミットされると、Txは使用できなくなるので、それをnilに設定する予定です。それはかなりではありませんが、ロールバックを試みて、エラーメッセージをテストすることはどちらかというとかなり簡単ではありません。問題はAFAIKがトランザクションがTxから "完了"されているかどうかをテストする方法がないことです。私はなぜそれがそのように、おそらくパフォーマンスが行われたのか分かりません。 –

答えて

73

なぜこれを行う必要がありますか? Begin()を呼び出す関数は、Commit()またはRollback()も呼び出して、適切なエラーを返す必要があります。例えば

、このコードは、エラーが返されるかどうかに応じてコミットまたはロールバックん:私は、私がコミットまたはロールバックするかどうかを確認するためにerrorをチェックしていますか

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() 
     } else { 
      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 
     } 
    }) 
} 

注意してください。

私の実際の実装では、Commit()またはRollback()への不要な呼び出しを防ぐために、* sql.Txの代わりにインターフェイスを渡します。

はここで実証するための簡単なスニペットですかdefer作品(版画4、ない5):

package main 

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

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

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

+0

すてきな答え!私はあなたの2番目のdoSomething()実装の最後に向かって "return nil"を見逃したと思います。 – splinter123

+0

ルーク、いつ、どのようにエラーが評価されるのですか?ドキュメントごとに、 "err"はdefer呼び出しで最初に宣言されたときにその値を取得する必要があります。だから、これは実際には私のために少し混乱しています。なぜなら、 "エラー"の値は、遅延で使われているように変わるようです。 – mirage

+0

遅延はvia:=(コロン等価)の前に宣言されます。 anon funcがそれをキャプチャします。 Deferは、値が返される直前に呼び出されます。それはそれを設定することができます。パニックが発生すると、パニックが回復し、エラーに変わり、その後返されます。何らかのエラーが発生した場合、ロールバックが発生します。最後に、エラーがない場合にはコミットが発生し、エラーの場合はエラー(現在はnil)が戻り値を確定します。 – Luke

関連する問題