47

database/sql および driver パッケージと Tx を使用すると、トランザクションがコミットまたはロールバックされたかどうかを検出するには、別のトランザクションを試行して結果としてエラーを受け取り、エラーを調べてそのタイプを判断する必要があるようです。エラー。コミットされているかどうかを Tx オブジェクトから判断できるようにしたいと考えています。確かに、Tx を使用する関数で別の変数を定義して設定することはできますが、かなりの数の変数があり、毎回 2 倍になります (変数と代入)。必要に応じてロールバックを実行する遅延関数もあり、bool 変数を渡す必要があります。

コミットまたはロールバック後に Tx 変数を nil に設定することは許容されますか?GC はメモリを回復しますか?

4

1 に答える 1

151

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

これにより、トランザクションが簡潔になり、by トランザクションが適切に処理されるようになります。

私のトランザクション ハンドラーではrecover()、パニックをキャッチして、ロールバックがすぐに発生するようにしています。パニックが予想される場合にコードがキャッチできるように、パニックを再スローします。通常の状況では、パニックは発生しません。代わりにエラーが返されます。

パニックを処理しなければ、トランザクションは最終的にロールバックされます。コミットされていないトランザクションは、クライアントが切断されたとき、またはトランザクションがガベージ コレクションを取得したときに、データベースによってロールバックされます。ただし、トランザクションが自然に解決されるのを待つと、他の (未定義の) 問題が発生する可能性があります。そのため、できるだけ早く解決することをお勧めします。

すぐに明確にならない可能性があることの 1 つはdefer、戻り変数がキャプチャされた場合にクロージャー内で戻り値を変更できることです。トランザクション ハンドラでerr(戻り値) が nil の場合、トランザクションはコミットされます。への呼び出しCommitもエラーを返す可能性があるため、戻り値を err に設定しerr = tx.Commit()ます。is が非 nil であり、既存のエラーを上書きしたくないので、同じRollbackことはしません。err

于 2014-05-06T18:48:48.410 に答える