Aller au contenu principal

Les transactions

Les transactions avec MariaDB pour sécuriser les modifications de données.

Notions théoriques

Une transaction est un ensemble d'instructions SQL qui s'exécutent comme une seule unité logique.

astuce

Une transaction garantit que les opérations soient toutes exécutées, ou aucune si une erreur survient.

Les transactions sont essentielles pour assurer la cohérence, l'intégrité et la fiabilité des données dans une base de données relationnelle.

Propriétés ACID

Une transaction respecte les propriétés ACID :

  • Atomicité : tout ou rien. Si une partie échoue, tout est annulé.
  • Cohérence : les données restent valides selon les règles de la base.
  • Isolation : les transactions ne se perturbent pas entre elles.
  • Durabilité : une fois validées, les modifications sont enregistrées même en cas de panne.

Démarrer, valider et annuler une transaction

  • Pour démarrer une transaction :

    START TRANSACTION;
  • Pour valider (confirmer) les modifications :

    COMMIT;
  • Pour annuler (revenir en arrière) :

    ROLLBACK;

Exemple d'utilisation

Imaginons une base de données de banque. Lors d'un virement, il faut :

  1. Retirer de l'argent du compte A
  2. Ajouter cet argent au compte B

Si une des deux opérations échoue, il faut annuler la transaction.

Transactions implicites vs explicites

  • En mode implicite, chaque requête est une transaction.
  • En mode explicite, il faut utiliser START TRANSACTION, COMMIT, ROLLBACK.

Utilisation avec PHP (PDO)

PDO permet de gérer les transactions en PHP :

$pdo->beginTransaction(); // Démarre la transaction
$pdo->exec("SQL 1"); // Première requête
$pdo->exec("SQL 2"); // Deuxième requête
$pdo->commit(); // Valide tout

// En cas d'erreur :
$pdo->rollBack(); // Annule tout

Bonnes pratiques

  • Toujours utiliser try/catch pour gérer les erreurs.
  • Valider (COMMIT) uniquement si toutes les opérations ont réussi.
  • Annuler (ROLLBACK) dès qu'une erreur est détectée.

Exemple pratique

Il est possible de simuler une opération de virement entre deux comptes bancaires à l'aide d'une transaction SQL.

Structure de données à utiliser

CREATE TABLE comptes (
id INT PRIMARY KEY,
nom VARCHAR(50),
solde DECIMAL(10,2)
);

INSERT INTO comptes VALUES
(1, 'Alice', 1000.00),
(2, 'Bob', 500.00);

Exemple de transaction SQL

START TRANSACTION;

UPDATE comptes SET solde = solde - 200 WHERE id = 1;
UPDATE comptes SET solde = solde + 200 WHERE id = 2;

COMMIT;

Exemple en PHP avec PDO

try {
$pdo = new PDO("mysql:host=localhost;dbname=banque", "root", "");

$pdo->beginTransaction();

$pdo->exec("UPDATE comptes SET solde = solde - 200 WHERE id = 1");
$pdo->exec("UPDATE comptes SET solde = solde + 200 WHERE id = 2");

$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "Erreur : " . $e->getMessage();
}

Test de mémorisation/compréhension


Quelle commande démarre une transaction en SQL ?


Quelle commande valide une transaction ?


Quelle commande annule une transaction ?


Que signifie le A dans ACID ?


Que se passe-t-il si une erreur survient dans une transaction ?


Quelle méthode PHP démarre une transaction avec PDO ?


Que fait la méthode `$pdo->rollBack()` ?


Quand faut-il utiliser COMMIT ?


Que garantit l'isolation dans une transaction ?


Quel est le rôle principal d'une transaction ?



TP pour réfléchir et résoudre des problèmes

Ce TP vous propose de mettre en œuvre des transactions SQL avec PHP (PDO) sur la base de données bibliotheque.

Vous manipulerez des opérations interdépendantes : si l'une échoue, l'ensemble devra être annulé. L'objectif est d'illustrer les propriétés ACID.

astuce

N'oubliez pas, la pratique est la clé pour maîtriser une nouvelle compétence.

Étape 1 : Télécharger et importer la base de données

Télécharger le fichier SQL suivant et l'importer dans MariaDB :

👉 Télécharger le fichier 'cdi.sql' - Télécharger le MCD

DROP DATABASE IF EXISTS bibliotheque;
CREATE DATABASE bibliotheque;
USE bibliotheque;
-- Depuis votre outil (phpMyAdmin, DBeaver ou ligne de commande), importer le fichier cdi.sql

Étape 2 : Préparer le fichier transactions.php

Créer un fichier transactions.php qui contiendra les différentes transactions.


Bonne pratique - Toujours activer PDO::ERRMODE_EXCEPTION

Sans setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION), les erreurs SQL passent silencieusement. Avec cette option, PDO lève une exception à chaque erreur SQL, ce qui permet au bloc catch d'intercepter l'erreur et d'appeler rollBack(). Sans exception, la transaction continuerait après une erreur — résultat : données partiellement modifiées.


Étape 3 : Transaction 1 - Enregistrer un nouvel emprunt et mettre à jour le nombre de retards

Contexte : Lorsqu'un nouvel emprunt est effectué, on souhaite également incrémenter le nombre de retards si l'utilisateur a déjà rendu un livre en retard.

À faire :

  1. Ajouter un emprunt pour l'emprunteur 1 du livre 2.
  2. Si cet emprunteur a déjà eu un retard (nbretards > 0), incrémenter ce compteur.

Ces deux opérations doivent être dans la même transaction.


Bonne pratique - Pattern try/catch pour toute transaction PDO

Le pattern try { beginTransaction → exec → commit } catch { rollBack } est obligatoire. Si exec() lève une exception (contrainte violée, clé étrangère invalide, etc.), le bloc catch appelle rollBack() et annule toutes les opérations depuis beginTransaction(). Sans catch, une erreur laisserait la base dans un état incohérent.


Étape 4 : Transaction 2 - Modifier un auteur et mettre à jour tous ses livres

Contexte : Corriger le nom d'un auteur et mettre à jour tous les livres associés pour ajouter un mot-clé dans leur titre.

À faire :

  1. Modifier le nom de l'auteur "Zola" en "Émile Zola".
  2. Ajouter " (Zola)" à la fin du titre de tous ses livres.

Ces deux opérations doivent être atomiques.


Étape 5 : Transaction 3 - Mettre à jour un éditeur et tous ses livres

Contexte : L'éditeur "Flammarion" change de nom et augmente les prix de ses livres.

À faire :

  1. Modifier le nom de l'éditeur "Flammarion" en "Éditions Flammarion".
  2. Augmenter de 10 % le prix de tous les livres de cet éditeur.

Étape 6 : Transaction 4 - Simuler une erreur et vérifier le rollback

Contexte : Tester la robustesse des transactions. Provoquer volontairement une erreur après une première requête.

À faire :

  1. Ajouter un nouvel auteur.
  2. Ajouter un livre lié à cet auteur avec un champ erroné (clé étrangère idediteur = 999 invalide).

Objectif : Vérifier que le nouvel auteur n'a pas été ajouté si le livre n'a pas pu être inséré.


Bonne pratique - Tester le rollback avant la mise en production

Avant de déployer une transaction critique, provoquez volontairement une erreur pour vérifier que le rollback fonctionne. Si SELECT * FROM auteurs WHERE nom = 'Test' retourne une ligne, le rollback ne s'est pas exécuté — vérifiez PDO::ATTR_ERRMODE. Un rollback qui ne s'exécute pas laisse la base dans un état incohérent.


Étape 7 : Transaction 5 - Retour d'un livre et mise à jour du stock

Contexte : Lorsqu'un livre est rendu, on met à jour la date de retour dans emprunts, et on décrémente un champ nbexemplaires_empruntes dans une table livres_stock.

À faire :

  1. Créer une table livres_stock avec deux colonnes : idlivre, nbexemplaires_empruntes.
  2. Insérer quelques données.
  3. Créer une transaction qui met à jour la date de retour et le stock.
// Création de la table (à faire une fois)
$pdo->exec("
CREATE TABLE IF NOT EXISTS livres_stock (
idlivre INT PRIMARY KEY,
nbexemplaires_empruntes INT DEFAULT 0
)
");

// Insertion de données test
$pdo->exec("
INSERT IGNORE INTO livres_stock (idlivre, nbexemplaires_empruntes)
SELECT idlivre, 1 FROM livres WHERE idlivre IN (1, 2)
");

Étape 8 : Nettoyage et vérification

Vérifiez que toutes les transactions ont bien été exécutées ou annulées selon les cas.

-- Vérifier que l'auteur "Test" n'existe pas
SELECT * FROM auteurs WHERE nom = 'Test';

-- Vérifier les nouveaux titres de Zola
SELECT titre FROM livres WHERE idauteur = 1;

-- Vérifier les prix des livres de Flammarion
SELECT titre, prix FROM livres WHERE idediteur = 2;

-- Vérifier les emprunts récents
SELECT * FROM emprunts ORDER BY idemprunt DESC;
Une solution