web-dev-qa-db-fra.com

Transactions SQL brutes avec des déclarations préparées par Golang

J'ai du mal à trouver des exemples qui font trois des choses suivantes:

1) Autoriser les transactions SQL brutes dans Golang.

2) Utilisez des déclarations préparées.

3) Restauration des échecs de requête.

Je voudrais faire quelque chose comme ça, mais avec des déclarations préparées.

    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)

Lorsque j'exécute cela, j'obtiens cette erreur: pq: cannot insert multiple commands into a prepared statement

Ce qui donne? Les transactions conformes à ACID sont-elles même possibles dans Golang? Je ne trouve pas d'exemple.

MODIFIER aucun exemple ici .

9
1N5818

Yes Go a une excellente implémentation de sql transactions . Nous commençons la transaction avec db.Begin et nous pouvons la terminer avec tx.Commit si tout va bien ou avec tx.Rollback en cas de Erreur.

type Tx struct {}

Tx est une transaction de base de données en cours.

Une transaction doit se terminer par un appel à la validation ou à la restauration.

Après un appel à Commit ou Rollback, toutes les opérations sur la transaction échouent avec ErrTxDone.

Les instructions préparées pour une transaction en appelant les méthodes Prepare ou Stmt de la transaction sont fermées par l'appel à Commit ou Rollback.

Notez également que nous préparons des requêtes avec la variable de transaction tx.Prepare (...)

Votre fonction peut ressembler à ceci:

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

J'ai un exemple complet ici

14
Yandry Pozo

J'ai trouvé une solution possible pour revenir en arrière sur toute défaillance sans aucun inconvénient significatif. Je suis assez nouveau à Golang cependant, je peux me tromper.

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