Transactions Java JDBC
Gérez les transactions de base de données en Java JDBC avec setAutoCommit, commit, rollback et savepoints.
Une transaction regroupe plusieurs instructions SQL en une unité tout-ou-rien : soit toutes les modifications sont validées, soit aucune ne l'est. L'exemple classique est un virement bancaire — débiter un compte, créditer un autre — où appliquer l'une sans l'autre entraînerait une perte ou une création d'argent. JDBC contrôle les transactions via l'objet Connection.
Ce chapitre explique comment ouvrir une transaction en désactivant l'auto-commit, comment fonctionnent commit() et rollback(), comment les savepoints permettent d'annuler une partie d'une transaction, et comment les niveaux d'isolation font des compromis entre cohérence et concurrence.
L'auto-commit est activé par défaut
Une nouvelle connexion s'exécute en mode auto-commit : chaque instruction est validée dès qu'elle se termine. C'est acceptable pour des instructions isolées, mais incorrect pour des unités multi-instructions. Pour ouvrir une transaction, désactivez l'auto-commit :
conn.setAutoCommit(false); // begin a transaction
try {
// ... several statements ...
conn.commit(); // make all changes permanent
} catch (SQLException e) {
conn.rollback(); // undo everything since the last commit
throw e;
} finally {
conn.setAutoCommit(true); // restore default for the pool
}commit et rollback
commit() rend permanentes toutes les modifications effectuées depuis le début de la transaction et les rend visibles aux autres. rollback() les annule toutes. La règle d'or : commit en cas de succès, rollback pour toute exception. Oublier le rollback laisse la connexion bloquée avec des verrous et une transaction à moitié terminée.
Un exemple de virement
Le virement utilise deux objets PreparedStatement, exécute les deux UPDATE, et ne valide qu'une fois les deux exécutés. Si l'un d'eux lève une exception, le bloc catch annule tout afin qu'aucun argent ne soit créé ou perdu :
conn.setAutoCommit(false);
try (PreparedStatement debit = conn.prepareStatement(
"UPDATE acct SET bal = bal - ? WHERE id = ?");
PreparedStatement credit = conn.prepareStatement(
"UPDATE acct SET bal = bal + ? WHERE id = ?")) {
debit.setBigDecimal(1, amount); debit.setInt(2, fromId); debit.executeUpdate();
credit.setBigDecimal(1, amount); credit.setInt(2, toId); credit.executeUpdate();
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
}Savepoints : rollback partiel
Un Savepoint est un marqueur à l'intérieur d'une transaction vers lequel on peut revenir, annulant le travail effectué après tout en conservant le travail effectué avant. C'est utile quand une étape facultative à l'intérieur d'une unité plus grande pourrait échouer, mais que vous ne souhaitez pas abandonner toute la transaction :
Savepoint sp = conn.setSavepoint("afterDebit");
// ... risky step ...
conn.rollback(sp); // undo back to the savepoint, not the whole transactionDeux règles à retenir : revenir à un savepoint ne met pas fin à la transaction — il faut encore appeler commit() ou un rollback() complet ensuite — et vous devriez libérer les savepoints dont vous n'avez plus besoin avec conn.releaseSavepoint(sp) pour libérer les ressources de la base de données. Les savepoints n'existent que lorsque l'auto-commit est désactivé.
Niveaux d'isolation
setTransactionIsolation(...) fait des compromis entre cohérence et concurrence. Du plus faible au plus fort : READ_UNCOMMITTED (voit les lignes « sales » non validées des autres), READ_COMMITTED (la valeur par défaut courante), REPEATABLE_READ (les relectures voient les mêmes lignes), et SERIALIZABLE (les transactions se comportent comme si elles s'exécutaient l'une après l'autre). Les niveaux plus forts préviennent davantage d'anomalies mais permettent moins de parallélisme.
Les anomalies contre lesquelles chaque niveau protège, dans l'ordre :
- Lecture sale (dirty read) — lire une ligne qu'une autre transaction a modifiée mais pas encore validée. Prévenue à partir de
READ_COMMITTED. - Lecture non répétable (non-repeatable read) — relire la même ligne et obtenir une valeur différente parce qu'une autre transaction a validé une modification entre-temps. Prévenue à partir de
REPEATABLE_READ. - Lecture fantôme (phantom read) — réexécuter la même requête et obtenir des lignes supplémentaires parce qu'une autre transaction a inséré des lignes correspondantes. Prévenue uniquement par
SERIALIZABLE.
Définissez le niveau avant le début du travail, et vérifiez ce que votre base de données supporte réellement avec DatabaseMetaData.supportsTransactionIsolationLevel(...) — tous les pilotes ne respectent pas tous les niveaux.
Exemple complet : niveaux d'isolation et unité de travail
Ce programme affiche les quatre constantes de niveau d'isolation par ordre croissant de force, puis modélise un virement comme une unité atomique — mettant en attente deux mises à jour et validant les deux ou les annulant toutes les deux — reproduisant le cycle setAutoCommit/commit/rollback sans base de données active.
Ce qu'il faut retenir de l'exécution :
- Les constantes d'isolation croissent avec la force :
READ_UNCOMMITTED(1),READ_COMMITTED(2),REPEATABLE_READ(4),SERIALIZABLE(8). Vous en passez une àsetTransactionIsolationpour choisir le niveau d'anomalie de concurrence que vous tolérerez. - Le virement met en attente les deux mises à jour avant de valider. C'est l'essence d'une transaction : les deux
UPDATEforment une unité, etcommit()n'intervient qu'une fois les deux prêts. - L'étape
commit()déplace ici les deux instructions dependingverscommittedensemble — reproduisant la façon dont la base de données rend toutes les modifications durables et visibles au même instant, jamais à moitié. - Le bloc
catchvidepending— le substitut derollback(). La leçon à retenir est la discipline : toute exception à l'intérieur de la transaction doit mener à un rollback, sinon vous laissez la base de données dans un état partiellement appliqué. transfer applied: truereflète une validation réussie. Inversez la logique pour que la vérification échoue et vous verriez un rollback sans rien d'appliqué — exactement la garantie tout-ou-rien qu'une transaction est censée vous offrir.
Pratique
Sujets connexes
- JDBC Connection — là où vivent l'auto-commit, commit et rollback.
- PreparedStatement — la façon sécurisée d'exécuter les mises à jour paramétrées à l'intérieur d'une transaction.
- Batch Processing — regrouper de nombreuses instructions ; les lots et les transactions fonctionnent souvent ensemble.
- DatabaseMetaData — interroger les niveaux d'isolation pris en charge par votre pilote.