PDO: transactions

Spesso capita di dover eseguire più query contemporaneamente, facendo in modo che ognuna di esse vada a buon fine oppure non ne vada eseguita nessuna, preferibilmente riportando il DB allo stato di partenza.

PDO offre uno strumento importante da questo punto vista, la transazione (PDO transaction). Si tratta di una operazione in più parti che sfrutta metodi propri per recuperare le informazioni, far partire le modifiche o annullarle tutte, in caso si presenti un qualsiasi tipo di eccezione, non solo quelle riferite a PDO.

Per eseguire correttamente una transazione, la prima cosa da fare è sicuramente impostare PDO::ATTR_ERRMODE su PDO::ERRMODE_EXCEPTION

$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
];

// oppure

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Per creare la transazione abbiamo bisogno di utilizzare tre metodi in particolare:

  • beginTransaction() per farla partire
  • commit() per inviarla
  • rollback() permette di procedere con il ripristino dello stato iniziale, annullando tutte le query eseguite dopo la chiamata al metodo

La cattura delle eccezioni è fondamentale per le transazioni. Se una query fallisce, qualsiasi sia il motivo del fallimento, l’esecuzione verrà interrotta e dalla cattura dell’eccezione si passa al ripristino dello stato iniziale, in un costrutto try…catch

try {
    $pdo->beginTransaction();
    // tentativo query 1: inserimento in tabella
    $sql = ("INSERT INTO users (name) VALUES (?)");
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$name]);

    // tentativo query 2: aggiornamento dati
    $sql = ("UPDATE customer_emails SET email = (?)");
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$email]);

    // se arriviamo fin qui senza errori allora può partire il commit
    // il commit è uno solo per il blocco di query
    $pdo->commit();

} catch (Exception $e){
     $pdo->rollback();
     throw $e;
}

Se viene lanciata una eccezione, significa che almeno una delle transazioni nel blocco tra beginTransaction() e commit() non è andata a buon fine.

Possiamo anche utilizzare il ciclo foreach() per eseguire un blocco di transazioni, a patto che le transazioni siano dello stesso tipo:

try {
    $pdo->beginTransaction();
    $stmt = $pdo->prepare("INSERT INTO users (name) VALUES (?)");
    foreach (['Joe','Ben'] as $name)
    {
        $stmt->execute([$name]);
    }
    $pdo->commit();
}catch (Exception $e){
    $pdo->rollback();
    throw $e;
}

In questo tipico esempio di transazione possiamo notare alcuni aspetti fondamentali:

  • Si usa il costrutto try…catch
  • Si inizia la transazione con il metodo beginTransaction()
  • Si prepara la query SQL con il solito metodo prepare()
  • Si esegue la query (o meglio ancora, le query) con execute()
  • Si invia la transazione con il metodo commit()
  • Si catturano le eccezioni (di qualsiasi tipo siano, non solo quelle PDO con PDOException)
  • Si predispone il ripristino con il metodo rollback()
  • Si lancia l’eccezione in seguito al rollback, per farsi notificare il problema, come al solito

Le transazioni vengono in genere implementate “salvando” il blocco di modifiche da applicare tutto in una volta; questo ha il piacevole effetto collaterale di migliorare drasticamente l’efficienza di quegli aggiornamenti. In altre parole, le transazioni possono rendere gli script più veloci e potenzialmente più robusti (è comunque necessario utilizzarli correttamente per trarne vantaggio)

Sfortunatamente, non tutti i database supportano le transazioni, quindi PDO deve essere eseguito in modalità “auto-commit” quando si apre la connessione per la prima volta. Modalità di commit automatico significa che ogni query eseguita ha una propria transazione implicita, se il database lo supporta, o nessuna transazione se il database non supporta le transazioni.

Se il driver del Database non supporta le transazioni, in seguito all’esecuzione del metodo beginTransaction() verrà lanciata una eccezione di tipo PDOException (indipendentemente dalle impostazioni di gestione degli errori: questa è sempre una condizione di errore grave).

Bisogna quindi assicurarsi che il table engine supporti le transazioni (per MySQL deve essere impostato su InnoDB e non su MyISAM).

Un pensiero su “PDO: transactions

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.