使用Golang预处理语句的原始SQL事务

von4xj4u  于 2023-09-28  发布在  Go
关注(0)|答案(2)|浏览(172)

我很难找到一些例子,做以下三件事:
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中甚至可能吗?我找不到一个例子。
编辑无示例here

mcvgt66p

mcvgt66p1#

是的,Go有一个很好的SQL事务实现。我们用db.Begin开始事务,如果一切顺利,我们可以用tx.Commit结束它,或者在出错的情况下用tx.Rollback结束它。
类型Tx结构{ }
Tx是正在进行的数据库事务。
事务必须以调用Commit或Rollback结束。
在调用Commit或Rollback之后,事务上的所有操作都会失败,并显示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($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()
}
  • 我有一个完整的例子here *
v9tzhpje

v9tzhpje2#

我想出了一个可能的解决方案,在没有任何重大缺点的情况下,在任何失败时进行回滚。我对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
}

相关问题