web-dev-qa-db-ja.com

golang準備済みステートメントを使用した未加工SQLトランザクション

次の3つを実行する例を見つけるのに苦労しています。

1)golangで生のSQLトランザクションを許可します。

2)準備されたステートメントを使用します。

3)クエリ失敗時のロールバック。

私はこのようなことをしたいのですが、準備されたステートメントを使用します。

    stmt, stmt_err := db.Prepare(`
            BEGIN TRANSACTION;

            -- Insert record into first table.

            INSERT INTO table_1 (
                    thing_1,
                    whatever)
            VALUES($1,$2);

            -- Inert record into second table.

            INSERT INTO table_2 (
                    thing_2,
                    whatever)
            VALUES($3,$4);

            END TRANSACTION;
            `)
    if stmt_err != nil {
            return stmt_err
    }   
    res, res_err := stmt.Exec(
            thing_1,
            whatever,
            thing_2,
            whatever)

これを実行すると、次のエラーが発生します:pq: cannot insert multiple commands into a prepared statement

何ができますか? ACID準拠のトランザクションはgolangでも可能ですか?例が見つかりません。

例を編集しない ここ

9
1N5818

はいGoはsql transactions の優れた実装を備えています。 db.Begin でトランザクションを開始し、すべてがうまくいけば tx.Commit で、または tx.Rollback でトランザクションを終了できます。エラー。

タイプTx構造体{}

Txは進行中のデータベーストランザクションです。

トランザクションは、コミットまたはロールバックの呼び出しで終了する必要があります。

CommitまたはRollbackを呼び出した後、トランザクションのすべての操作がErrTxDoneで失敗します。

トランザクションのPrepareまたはStmtメソッドを呼び出してトランザクション用に準備されたステートメントは、CommitまたはRollbackの呼び出しによって閉じられます。

また、トランザクション変数tx.Prepare(...)を使用してクエリを準備することにも注意してください。

関数は次のようになります。

func doubleInsert(db *sql.DB) error {

    tx, err := db.Begin()
    if err != nil {
        return err
    }

    {
        stmt, err := tx.Prepare(`INSERT INTO table_1 (thing_1, whatever)
                     VALUES($1,$2);`)
        if err != nil {
            tx.Rollback()
            return err
        }
        defer stmt.Close()

        if _, err := stmt.Exec(thing_1, whatever); err != nil {
            tx.Rollback() // return an error too, we may want to wrap them
            return err
        }
    }

    {
        stmt, err := tx.Prepare(`INSERT INTO table_2 (thing_2, whatever)
                     VALUES($1, $2);`)
        if err != nil {
            tx.Rollback()
            return err
        }
        defer stmt.Close()

        if _, err := stmt.Exec(thing_2, whatever); err != nil {
            tx.Rollback() // return an error too, we may want to wrap them
            return err
        }
    }

    return tx.Commit()
}

完全な例があります ここ

14
Yandry Pozo

重大な欠点なしに、失敗した場合にロールバックするための可能な解決策を思いつきました。私はGolangにかなり新しいですが、私は間違っている可能性があります。

func CloseTransaction(tx *sql.Tx, commit *bool) {
  if *commit {
    log.Println("Commit sql transaction")
    if err := tx.Commit(); err != nil {
      log.Panic(err)
    }
  } else {
    log.Println("Rollback sql transcation")
    if err := tx.Rollback(); err != nil {
      log.Panic(err)
    }
  }
}

func MultipleSqlQuriesWithTx(db *sql.DB, .. /* some parameter(s) */)  (.. .. /* some named return parameter(s) */, err error) {
  tx, err := db.Begin()
  if err != nil {
    return
  }
  commitTx := false
  defer CloseTransaction(tx, &commitTx)

  // First sql query
  stmt, err := tx.Prepare(..) // some raw sql
  if err != nil {
    return
  }
  defer stmt.Close()

  res, err := stmt.Exec(..) // some var args
  if err != nil {
    return
  }

  // Second sql query
  stmt, err := tx.Prepare(..) // some raw sql
  if err != nil {
    return
  }
  defer stmt.Close()

  res, err := stmt.Exec(..) // some var args
  if err != nil {
    return
  }

  /*
    more tx sql statements and queries here
  */

  // success, commit and return result
  commitTx = true
  return
}
0
BARJ