Transaction Spring
Transaction Spring
et la Propagation
18 novembre 2025
1
Résumé
Ce cours présente de manière détaillée et pédagogique les mécanismes transac-
tionnels dans Spring Framework, avec un accent particulier sur les niveaux d’isola-
tion et les stratégies de propagation. L’approche adoptée part des problématiques
concrètes rencontrées dans les applications d’entreprise pour introduire progressive-
ment les solutions offertes par Spring. Ce document s’adresse aux développeurs Java
souhaitant maîtriser la gestion des transactions dans leurs applications Spring.
2
Table des matières
1 Introduction 5
1.1 Qu’est-ce qu’une transaction ? . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Pourquoi avons-nous besoin de transactions ? . . . . . . . . . . . . . . . . . 5
1.3 Transactions et threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
7 Bonnes pratiques 20
7.1 Choisir le bon niveau d’isolation . . . . . . . . . . . . . . . . . . . . . . . . 20
7.2 Choisir la bonne stratégie de propagation . . . . . . . . . . . . . . . . . . . 21
7.3 Optimiser les performances . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.4 Gérer les exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3
8 Cas d’utilisation avancés 21
8.1 Transactions distribuées . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
8.2 Transactions programmatiques avec TransactionTemplate . . . . . . . . . . 22
8.3 Tests avec transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
9 Conclusion 23
10 Exercices pratiques 23
11 Références 23
4
1 Introduction
1.1 Qu’est-ce qu’une transaction ?
Une transaction est une séquence d’opérations qui forme une unité logique de travail.
Les transactions sont régies par quatre propriétés fondamentales, souvent désignées par
l’acronyme ACID :
— Atomicité : Une transaction est traitée dans sa totalité ou pas du tout.
— Cohérence : Une transaction préserve la cohérence de la base de données.
— Isolation : Les transactions s’exécutent indépendamment les unes des autres.
— Durabilité : Une fois validée, une transaction persiste même en cas de panne.
Exemple concret
Imaginons une application bancaire où un client transfère 1000€ du compte A au
compte B. Cette opération implique deux étapes :
1. Débiter 1000€ du compte A
2. Créditer 1000€ sur le compte B
Si le système tombe en panne après la première étape mais avant la seconde, l’argent
disparaîtrait sans jamais atteindre le compte B. Les transactions permettent d’éviter
ce scénario.
1 @Service
2 public class MonService {
3
4 @Autowired
5 private AutreService autreService ;
6
5
7 @Transactional
8 public void m e t h o d e T r a n s a c t i o n n e l l e () {
9 // Cette o p r a t i o n fait partie de la transaction
10 entite . setValeur ( " nouvelle valeur " ) ;
11
12 // Ce thread s p a r n ’ h r i t e PAS de la transaction
13 new Thread (() -> {
14 // Cette o p r a t i o n n ’ est PAS dans la transaction principale
15 autreService . faireQu elqueCho se () ;
16 }) . start () ;
17 }
18 }
Problématique
Dans les applications web ou les applications avec des traitements asynchrones, la
gestion des transactions entre différents threads peut devenir complexe. Comment
garantir la cohérence des données lorsque plusieurs threads doivent participer à une
même unité logique de travail ?
Solution
Pour propager une transaction entre différents threads, vous pouvez utiliser :
— TransactionSynchronizationManager pour obtenir les ressources transaction-
nelles actuelles
— JTA (Java Transaction API) pour les transactions distribuées
— Des frameworks comme Spring Integration qui gèrent la propagation du
contexte
Problème
La transaction A modifie une donnée. La transaction B lit cette donnée modifiée.
La transaction A est annulée (rollback). La transaction B travaille maintenant avec
une donnée qui n’existe pas officiellement.
6
2.1.2 Lectures non reproductibles (Non-repeatable Reads)
Une transaction relit une donnée qu’elle a déjà lue et constate qu’elle a été modifiée
par une autre transaction.
Problème
La transaction A lit une donnée. La transaction B modifie cette donnée et valide.
La transaction A relit la même donnée et obtient une valeur différente.
Problème
La transaction A exécute une requête qui renvoie un ensemble de lignes. La tran-
saction B insère de nouvelles lignes qui correspondent aux critères de la requête de
A. Si A réexécute sa requête, elle verra des lignes "fantômes" qui n’existaient pas
lors de sa première exécution.
Problème
La méthode A démarre une transaction et appelle la méthode B. La méthode B
a également besoin d’une transaction. Devrait-elle créer sa propre transaction ou
participer à celle de A ? Que se passe-t-il si B échoue mais que A réussit ?
3.1 DEFAULT
Problématique
Comment choisir un niveau d’isolation approprié sans connaître les spécificités de
chaque base de données ou système transactionnel ?
7
Solution
Le niveau DEFAULT utilise le niveau d’isolation par défaut du système sous-jacent.
Cela permet de s’adapter automatiquement aux paramètres optimaux définis par
les administrateurs de la base de données.
Ce niveau utilise le niveau d’isolation par défaut du système sous-jacent. C’est géné-
ralement un bon choix lorsque vous n’avez pas de besoins spécifiques.
1 @Transactional ( isolation = Isolation . DEFAULT )
2 public void maMethode () {
3 // Code transactionnel
4 }
3.2 READ_UNCOMMITTED
Problématique
Comment maximiser les performances dans des scénarios où la cohérence absolue
des données n’est pas critique et où la rapidité d’accès est prioritaire ?
Solution
READ_UNCOMMITTED offre les meilleures performances en permettant la lec-
ture de données non validées. C’est utile pour les rapports en temps réel ou les
tableaux de bord où des données approximatives sont acceptables et où la rapidité
est essentielle.
C’est le niveau le plus bas d’isolation. Une transaction peut lire des données modifiées
par d’autres transactions non validées.
Attention
Ce niveau permet les lectures sales, les lectures non reproductibles et les lectures
fantômes. À utiliser avec précaution !
3.3 READ_COMMITTED
Problématique
Comment éviter de lire des données potentiellement invalides (qui pourraient être
annulées) tout en maintenant de bonnes performances ?
8
Solution
READ_COMMITTED assure que seules les données validées sont lues, éliminant
ainsi le problème des lectures sales. C’est un bon compromis entre performance et
cohérence pour la plupart des applications.
Une transaction ne peut lire que les données validées par d’autres transactions. Cela
évite les lectures sales mais permet toujours les lectures non reproductibles et les lectures
fantômes.
1 @Transactional ( isolation = Isolation . READ_COMMITTED )
2 public void maMethode () {
3 // Code transactionnel
4 }
3.4 REPEATABLE_READ
Problématique
Comment garantir qu’une transaction obtiendra toujours les mêmes résultats lors-
qu’elle relit les mêmes données, même si d’autres transactions modifient ces données
entre-temps ?
Solution
REPEATABLE_READ utilise des verrous ou des versions de données pour garantir
que les données lues par une transaction restent cohérentes tout au long de son
exécution. C’est essentiel pour les rapports financiers ou les calculs qui nécessitent
une vue cohérente des données.
Ce niveau garantit que si une transaction lit une donnée, elle obtiendra la même valeur
si elle la relit, même si d’autres transactions ont modifié cette donnée entre-temps. Cela
évite les lectures sales et les lectures non reproductibles, mais permet toujours les lectures
fantômes.
1 @Transactional ( isolation = Isolation . REPEATABLE_READ )
2 public void maMethode () {
3 // Code transactionnel avec isolation moyenne
4 }
3.5 SERIALIZABLE
Problématique
Comment garantir une cohérence totale des données dans des situations critiques
où aucune anomalie n’est acceptable, même au prix de performances réduites ?
9
Solution
SERIALIZABLE offre le plus haut niveau d’isolation en exécutant les transactions
comme si elles étaient séquentielles. C’est crucial pour les opérations financières
sensibles, les mises à jour de soldes ou toute opération où l’intégrité des données
est primordiale.
C’est le niveau le plus élevé d’isolation. Les transactions s’exécutent comme si elles
étaient sérialisées, c’est-à-dire exécutées l’une après l’autre. Cela évite tous les problèmes
de concurrence mais peut considérablement réduire les performances.
1 @Transactional ( isolation = Isolation . SERIALIZABLE )
2 public void maMethode () {
3 // Code transactionnel avec isolation maximale
4 }
Solution
REQUIRED assure qu’une transaction est toujours disponible. Si une transaction
existe déjà, la méthode s’y joint ; sinon, une nouvelle transaction est créée. C’est
idéal pour les opérations qui doivent être atomiques mais peuvent faire partie d’une
unité de travail plus large.
Si une transaction existe déjà, la méthode s’exécute dans cette transaction. Sinon, une
nouvelle transaction est créée.
10
Cas d’utilisation
C’est le comportement par défaut et convient à la plupart des cas d’utilisation.
Utilisez-le lorsque votre méthode doit s’exécuter dans une transaction et que vous
voulez participer à une transaction existante si elle existe.
4.2 SUPPORTS
Problématique
Comment créer des méthodes flexibles qui peuvent fonctionner avec ou sans tran-
saction selon le contexte d’appel ?
Solution
SUPPORTS permet à une méthode de s’adapter au contexte transactionnel exis-
tant. Elle s’exécutera dans une transaction si elle est appelée dans un contexte
transactionnel, ou sans transaction si ce n’est pas le cas. C’est utile pour les opéra-
tions de lecture qui peuvent bénéficier d’une transaction mais n’en nécessitent pas
absolument.
La méthode s’exécute dans la transaction existante si elle existe. Sinon, elle s’exécute
sans transaction.
Cas d’utilisation
Utilisez ce mode lorsque votre méthode peut fonctionner avec ou sans transaction.
Par exemple, pour des opérations de lecture qui peuvent bénéficier d’une transaction
mais n’en nécessitent pas absolument.
4.3 MANDATORY
Problématique
Comment s’assurer qu’une méthode n’est jamais appelée hors d’un contexte tran-
sactionnel, pour éviter des modifications incohérentes ?
11
Solution
MANDATORY force l’existence préalable d’une transaction. Si aucune transaction
n’est active lors de l’appel, une exception est levée. C’est un mécanisme de sécurité
pour garantir que certaines opérations critiques ne sont exécutées que dans le cadre
d’une transaction plus large gérée par l’appelant.
Cas d’utilisation
Utilisez ce mode lorsque votre méthode doit obligatoirement s’exécuter dans le
contexte d’une transaction existante. Cela permet de s’assurer que le code appelant
a déjà démarré une transaction.
4.4 REQUIRES_NEW
Problématique
Comment isoler certaines opérations pour qu’elles soient indépendantes de la tran-
saction appelante, que celle-ci réussisse ou échoue ?
Solution
REQUIRES_NEW crée systématiquement une nouvelle transaction indépendante,
suspendant temporairement la transaction existante. C’est essentiel pour les opé-
rations qui doivent être validées indépendamment, comme la journalisation d’évé-
nements ou les notifications qui doivent persister même si l’opération principale
échoue.
La méthode s’exécute toujours dans une nouvelle transaction. Si une transaction existe
déjà, elle est suspendue jusqu’à ce que la nouvelle transaction soit terminée.
Cas d’utilisation
Utilisez ce mode lorsque vous voulez que votre opération soit complètement indé-
pendante de la transaction appelante. Par exemple, pour la journalisation ou les
audits qui doivent être persistés même si la transaction principale échoue.
12
5 journal . setAction ( action ) ;
6 journal . setDate ( new Date () ) ;
7 journalRe pository . save ( journal ) ;
8 }
4.5 NOT_SUPPORTED
Problématique
Comment exécuter certaines opérations en dehors de toute transaction, même si
elles sont appelées depuis un contexte transactionnel ?
Solution
NOT_SUPPORTED suspend toute transaction existante pendant l’exécution de la
méthode. C’est utile pour les opérations qui ne nécessitent pas de transaction ou qui
pourraient être affectées négativement par une transaction (comme des requêtes de
lecture intensives ou des opérations sur des systèmes externes non transactionnels).
La méthode s’exécute toujours sans transaction. Si une transaction existe, elle est
suspendue pendant l’exécution de la méthode.
Cas d’utilisation
Utilisez ce mode pour les opérations qui ne doivent pas être transactionnelles et
qui pourraient être affectées négativement par une transaction (par exemple, des
opérations de lecture intensives).
4.6 NEVER
Problématique
Comment empêcher formellement qu’une méthode soit appelée dans un contexte
transactionnel ?
Solution
NEVER garantit qu’aucune transaction n’est active lors de l’appel de la méthode. Si
une transaction est détectée, une exception est levée. C’est un mécanisme de sécurité
pour les opérations qui ne doivent absolument pas être exécutées dans un contexte
transactionnel, par exemple pour éviter des verrous prolongés ou des interactions
avec des systèmes externes incompatibles.
13
La méthode s’exécute sans transaction. Si une transaction existe, une exception est
levée.
Cas d’utilisation
Utilisez ce mode pour les méthodes qui ne doivent jamais être appelées dans un
contexte transactionnel, par exemple pour des raisons de performance ou de com-
patibilité.
4.7 NESTED
Problématique
Comment créer des points de sauvegarde dans une transaction, permettant d’annu-
ler partiellement certaines opérations sans affecter toute la transaction ?
Solution
NESTED crée une transaction imbriquée avec un point de sauvegarde (savepoint).
Si la transaction imbriquée échoue, seules les opérations effectuées dans cette sous-
transaction sont annulées, tandis que la transaction parente peut continuer. C’est
utile pour les opérations composées de sous-tâches qui peuvent échouer individuel-
lement sans compromettre l’ensemble.
La méthode s’exécute dans une transaction imbriquée si une transaction existe déjà.
Sinon, elle se comporte comme REQUIRED.
Cas d’utilisation
Utilisez ce mode lorsque vous voulez pouvoir annuler une partie de la transaction
sans affecter la transaction parente. Notez que tous les gestionnaires de transactions
ne prennent pas en charge les transactions imbriquées.
Attention
Le support des transactions imbriquées dépend du gestionnaire de transactions sous-
jacent. Par exemple, JTA ne supporte pas les transactions imbriquées, tandis que
JDBC avec savepoints les supporte.
14
5 Gestion des exceptions et rollback
5.1 Mécanisme de rollback dans Spring
Spring offre un mécanisme sophistiqué pour gérer les exceptions et déclencher des
rollbacks automatiques lorsque nécessaire.
1 @Transactional
2 public void m e t h o d e A v e c R o l l b a c k A u t o m a t i q u e () {
3 // Si une RuntimeException est l e v e ici , Spring effectuera un
rollback
4 throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Cette exception d c l e n c h e r a un
rollback " ) ;
5
6 // Si une exception v r i f i e est l e v e ici , Spring ne fera PAS de
rollback par d f a u t
7 throw new IOException ( " Cette exception ne d c l e n c h e r a pas de
rollback " ) ;
8 }
Problématique
Comment contrôler précisément quelles exceptions doivent déclencher un rollback
et lesquelles doivent être ignorées ?
Solution
Spring permet de spécifier explicitement les types d’exceptions qui doivent déclen-
cher un rollback (rollbackFor) et ceux qui ne doivent pas en déclencher (noRoll-
backFor). Cela offre un contrôle fin sur le comportement transactionnel en cas d’ex-
ception.
1 @Transactional (
2 rollbackFor = { Exception . class } , // Toutes les exceptions
d c l e n c h e r o n t un rollback
3 noRollbackFor = { NotFound Exceptio n . class } // Sauf celle - ci
4 )
5 public void m e t h o d e A v e c R o l l b a c k P e r s o n n a l i s e () throws Exception {
6 // Le code ici
7 }
15
5.3 Rollback programmatique
Dans certains cas, vous pourriez avoir besoin de déclencher un rollback manuellement,
en fonction de conditions métier spécifiques.
Problématique
Comment déclencher un rollback sans lever d’exception, par exemple lorsqu’une
condition métier n’est pas satisfaite ?
Solution
Spring permet de marquer la transaction courante comme "rollback-only", ce qui
garantit qu’elle sera annulée à la fin, même si aucune exception n’est levée. C’est
utile pour les cas où la logique métier détecte une condition d’erreur mais ne souhaite
pas interrompre l’exécution avec une exception.
1 @Service
2 public class MonService {
3
4 @Autowired
5 private T r a n s a c t i o n A s p e c t S u p p o r t t r a n s a c t i o n A s p e c t S u p p o r t ;
6
7 @Transactional
8 public void m e t h o d e A v e c R o l l b a c k P r o g r a m m a t i q u e ( boolean condition ) {
9 // Logique m t i e r
10
11 if (! condition ) {
12 // Marquer la transaction pour rollback sans lever d ’
exception
13 T r a n s a c t i o n A s p e c t S u p p o r t . c u r r e n t T r a n s a c t i o n S t a t u s () .
setRollbackOnly () ;
14 return ; // Continuer l ’ e x c u t i o n mais la transaction sera
annul e
15 }
16
17 // Suite du code
18 }
19 }
Problématique
Comment les exceptions affectent-elles les transactions imbriquées ou les transac-
tions avec différentes stratégies de propagation ?
16
Solution
Le comportement dépend de la stratégie de propagation :
— Avec REQUIRED, une exception dans une méthode interne provoque un
rollback de toute la transaction
— Avec REQUIRES_NEW, seule la transaction interne subit un rollback, la
transaction externe continue
— Avec NESTED, seule la partie imbriquée est annulée, la transaction parente
peut continuer
1 @Service
2 public class ServiceExterne {
3
4 @Autowired
5 private ServiceInterne serviceInterne ;
6
7 @Transactional
8 public void methodeExterne () {
9 // Code avant
10
11 try {
12 serviceInterne . methodeInterne () ; // Peut lever une exception
13 } catch ( Exception e ) {
14 // Avec REQUIRES_NEW , la transaction interne est d j
annul e
15 // mais la transaction externe peut continuer
16
17 // Avec REQUIRED , cette gestion d ’ exception ne suffit pas
18 // car la transaction est d j m a r q u e pour rollback
19 }
20
21 // Code a p r s - s ’ e x c u t e r a uniquement si la m t h o d e interne
22 // utilise REQUIRES_NEW ou NESTED
23 }
24 }
25
26 @Service
27 public class ServiceInterne {
28
29 @Transactional ( propagation = Propagation . REQUIRES_NEW )
30 public void methodeInterne () {
31 // Si une exception est l e v e ici , seule cette transaction est
annul e
32 throw new RuntimeException ( " Erreur " ) ;
33 }
34 }
17
6.1.1 Configuration par annotations
C’est l’approche la plus simple et la plus courante.
1 @Configuration
2 @EnableTransactionManagement
3 public class AppConfig {
4
5 @Bean
6 public DataSource dataSource () {
7 // Configuration de la source de d o n n e s
8 }
9
10 @Bean
11 public P l a t f o r m T r a n s a c t i o n M a n a g e r tr an sa cti on Ma nag er () {
12 return new D a t a S o u r c e T r a n s a c t i o n M a n a g e r ( dataSource () ) ;
13 }
14 }
Ensuite, vous pouvez utiliser l’annotation @Transactional sur vos méthodes ou classes :
1 @Service
2 public class MonService {
3
4 @Transactional ( isolation = Isolation . READ_COMMITTED ,
5 propagation = Propagation . REQUIRED ,
6 timeout = 30 ,
7 readOnly = false ,
8 rollbackFor = Exception . class )
9 public void m a M e t h o d e T r a n s a c t i o n n e l l e () {
10 // Code m t i e r
11 }
12 }
18
21 }
22 }
8 // Getters et setters
9 }
10
11 @Repository
12 public interface CompteRepository extends JpaRepository < Compte , Long > {
13 Compte findByNumero ( String numero ) ;
14 }
15
16 @Service
17 public class TransfertService {
18
19 private final CompteRepository compteRepository ;
20 private final JournalService journalService ;
21
22 public TransfertService ( CompteRepository compteRepository ,
23 JournalService journalService ) {
24 this . compteRepository = compteRepository ;
25 this . journalService = journalService ;
26 }
27
28 @Transactional ( isolation = Isolation . REPEATABLE_READ )
29 public void transferer ( String sourceNumero , String destinationNumero
,
30 BigDecimal montant ) {
31
32 // V r i f i c a t i o n du montant
33 if ( montant . compareTo ( BigDecimal . ZERO ) <= 0) {
34 throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Le montant doit tre
positif " ) ;
35 }
36
37 // R c u p r a t i o n des comptes
38 Compte source = compteRepository . findByNumero ( sourceNumero ) ;
39 Compte destination = compteRepository . findByNumero (
dest inationNu mero ) ;
40
41 if ( source == null || destination == null ) {
42 throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Compte introuvable " ) ;
43 }
44
45 // V r i f i c a t i o n du solde
46 if ( source . getSolde () . compareTo ( montant ) < 0) {
19
47 throw new I l l e g a l S t a t e E x c e p t i o n ( " Solde insuffisant " ) ;
48 }
49
50 // R a l i s a t i o n du transfert
51 source . setSolde ( source . getSolde () . subtract ( montant ) ) ;
52 destination . setSolde ( destination . getSolde () . add ( montant ) ) ;
53
54 // Sauvegarde des modifications
55 compteRepository . save ( source ) ;
56 compteRepository . save ( destination ) ;
57
58 // Journalisation du transfert ( dans une nouvelle transaction )
59 journalService . j o u r n a l i s e r T ra n s f e r t ( sourceNumero ,
destinationNumero , montant ) ;
60 }
61 }
62
63 @Service
64 public class JournalService {
65
66 private final Journal Repositor y journal Reposito ry ;
67
68 public JournalService ( J ournalRep ository j ournalRe pository ) {
69 this . journal Reposito ry = journalR epositor y ;
70 }
71
72 @Transactional ( propagation = Propagation . REQUIRES_NEW )
73 public void j ou r n a l i s e r T r a n s f e r t ( String source , String destination ,
74 BigDecimal montant ) {
75 Journal journal = new Journal () ;
76 journal . setTypeOperation ( " TRANSFERT " ) ;
77 journal . setCompteSource ( source ) ;
78 journal . s e t C o m p t e D e s t i n a t i o n ( destination ) ;
79 journal . setMontant ( montant ) ;
80 journal . setDate ( new Date () ) ;
81
7 Bonnes pratiques
7.1 Choisir le bon niveau d’isolation
— Commencez avec le niveau par défaut de votre base de données (généralement
READ_COMMITTED).
— N’utilisez READ_UNCOMMITTED que si vous êtes absolument certain que les
lectures sales ne poseront pas de problème.
— Utilisez REPEATABLE_READ lorsque vous avez besoin de cohérence dans les
lectures multiples.
20
— Réservez SERIALIZABLE pour les opérations critiques où la cohérence est plus
importante que la performance.
21
8.2 Transactions programmatiques avec TransactionTemplate
Pour les cas où les annotations ne sont pas suffisantes :
1 @Service
2 public class MonService {
3
4 private final T r an s a ct i on T em p l at e t r an s a ct i on T em p l at e ;
5
6 public MonService ( P l a t f o r m T r a n s a c t i o n M a n a g e r tra ns ac tio nM an age r ) {
7 this . t r an s a ct i on T em p l at e = new T ra n sa c ti o n Te m pl a t e (
tr an sa cti on Ma nag er ) ;
8 this . t r an s a ct i on T em p l at e . setIso lationLe vel (
9 TransactionDefinition . ISOLATION_READ_COMMITTED );
10 this . t r an s ac t i on T em p l at e . s e t P r o p a g a t i o n B e h a v i o r (
11 T r a n s a c t i o n D e f i n i t i o n . P R O P A G A T I O N _ R E Q UI R E D ) ;
12 }
13
22
9 Conclusion
La gestion des transactions est un aspect fondamental du développement d’applica-
tions robustes. Spring offre une abstraction puissante qui simplifie considérablement cette
tâche. En comprenant les niveaux d’isolation et les stratégies de propagation, vous pouvez
concevoir des applications qui garantissent l’intégrité des données tout en maintenant de
bonnes performances.
Les points clés à retenir sont :
— Les transactions garantissent les propriétés ACID.
— Les transactions sont associées au thread d’exécution courant dans Spring.
— Les niveaux d’isolation contrôlent comment les transactions interagissent entre
elles.
— Les stratégies de propagation définissent comment les transactions se comportent
lorsqu’une méthode transactionnelle en appelle une autre.
— Spring offre un contrôle fin sur le comportement de rollback en cas d’exception.
— Choisissez toujours le niveau d’isolation et la stratégie de propagation en fonction
de vos besoins spécifiques.
10 Exercices pratiques
1. Implémentez un système de réservation où plusieurs utilisateurs peuvent réserver
le même produit. Utilisez le niveau d’isolation approprié pour éviter les problèmes
de concurrence.
2. Créez un service qui effectue plusieurs opérations, certaines devant être dans la
même transaction et d’autres dans des transactions séparées.
3. Implémentez un mécanisme de compensation pour annuler les effets d’une transac-
tion déjà validée en cas d’erreur dans une étape ultérieure.
11 Références
— Documentation officielle de Spring : [Link]
docs/current/reference/html/[Link]#transaction
— Martin Fowler, "Patterns of Enterprise Application Architecture"
— Pramod J. Sadalage et Martin Fowler, "NoSQL Distilled"
— Spring in Action, Craig Walls
23