Транзакции Raw sql с подготовленными инструкциями golang

у меня возникли проблемы с поиском некоторых примеров, которые делают три из следующих вещей:

1) разрешить необработанные транзакции sql в golang.

2) использовать подготовленные операторы.

3) откат при сбое запросов.

Я хотел бы сделать что-то подобное, но с готовыми заявлениями.

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

            -- Insert record into first table.

            INSERT INTO table_1 (
                    thing_1,
                    whatever)
            VALUES(,);

            -- Inert record into second table.

            INSERT INTO table_2 (
                    thing_2,
                    whatever)
            VALUES(,);

            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? Я не могу найти пример.

изменить нет примеров здесь.

2 ответов


Yes Go имеет отличную реализацию sql сделки. Мы начинаем транзакцию с db.Начать и мы можем закончить его с tx.Commit если все пойдет хорошо или с tx.Откат в случае ошибки.

введите TX struct { }

Tx-это незавершенная транзакция базы данных.

транзакция должна завершиться вызовом для фиксации или отката.

после вызова Фиксация или откат, все операции транзакции завершаются с ошибкой ErrTxDone.

операторы, подготовленные для транзакции путем вызова методов Prepare или Stmt транзакции, закрываются вызовом Commit или Rollback.

также обратите внимание, что мы готовим запросы с переменной транзакции tx.Готовиться.(..)

ваша функция может выглядеть так:

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(,);`)
        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(, );`)
        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()
}

у меня есть полный пример здесь


Я придумал возможное решение для отката при любом сбое без каких-либо существенных недостатков. Я довольно, хотя 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
}