Java Application Servers
Introduction to Java Component-based Enterprise Applications
Transactions
- Transaction: Unité de travail atomique
- Propriétés ACID:
- Atomicité: Tout ou rien
- Cohérence: État valide avant et après
- Isolation: Indépendance des transactions
- Durabilité: Changements persistants
Isolation des Transactions
Problèmes d’Isolation
Dirty Read: Lecture d’une donnée non validée (transaction non committée)
Non-Repeatable Read: Deux lectures successives donnent des résultats différents
Phantom Read: Nouvelles lignes apparaissent entre deux lectures
Lost Update: Écrasement d’une mise à jour par une autre transaction
Niveaux d’Isolation
READ_UNCOMMITTED - Plus bas niveau - Permet tous les problèmes - Performance maximale
READ_COMMITTED - Empêche dirty reads - Niveau par défaut - Bon compromis
REPEATABLE_READ - Empêche non-repeatable reads - Verrouillage lecture - Impact modéré
SERIALIZABLE - Isolation maximale - Empêche phantom reads - Performance réduite
Transactions Locales vs Distribuées
Transactions Locales
- S’exécutent sur une seule ressource
- Gérées par un seul gestionnaire de transactions
- Plus simples à implanter
- Exemple: Transaction sur une seule base de données
Transactions Distribuées
- Impliquent plusieurs ressources
- Nécessitent un protocole 2-Phase Commit
- Plus complexes mais plus flexibles
- Exemple: Transaction entre une DB ou plusieurs DB, une file de messages, …
Gestion des Transactions en Java
- Local Transactions:
- Transactions sur une seule ressource
- JDBC, JPA, JMS, …
- Java Transaction API (JTA):
- API standard pour la gestion des transactions
- Interface
UserTransaction - Interface
TransactionManager
- Historiquement avec les Enterprise JavaBeans (EJB)
- Aujourd’hui avec CDI
EJB et CDI : Évolution des Composants Jakarta EE
Du EJB au CDI
- Historique EJB
- Framework historique (1998)
- Configuration par XML puis plus tard par annotations
- Couplage fort
- Tests unitaires difficiles
- Évolution CDI (2009+)
- Configuration par annotations
- Découplage facilité
- Tests simplifiés
Remplacement progressif
- Services EJB
- Transactions (@TransactionAttribute)
- Sécurité (@RolesAllowed)
- Concurrence (@Lock)
- Messaging (MDB)
- Apports CDI
- Contextes flexibles
- Intercepteurs modulaires
- Events/Observers
- Qualifiers
Migration EJB vers CDI
| EJB | CDI | Commentaires |
|---|---|---|
| @Stateless | @ApplicationScoped | Sans état |
| @Stateful | @SessionScoped | Avec état |
| @Singleton | @ApplicationScoped + @Singleton | Instance unique |
| @EJB | @Inject | Injection |
| @TransactionAttribute | @Transactional | Transactions |
| @Schedule | @Scheduled | Timers |
CDI et EJB peuvent coexister dans une application Jakarta EE
Les Transactions avec CDI
Deux Approches de Gestion Transactionnelle
Gestion Programmatique - Contrôle explicite par injection de UserTransaction - Style impératif - Granularité fine - Flexibilité maximale
Gestion Déclarative - Annotations @Transactional - Style déclaratif - Configuration simple - Gestion automatique
Transactions Gérées par le Bean (BMT)
- Contrôle programmatique explicite
- Démarrage et fin de transaction manuels où l’on veut
- Utilisation de l’interface UserTransaction par injection
- Plus flexible mais plus de responsabilité
public class MonBeanBMT {
@Resource
UserTransaction utx;
public void maMethode() {
try {
utx.begin();
// Code métier
utx.commit();
} catch (Exception e) {
try {
utx.rollback();
} catch (SystemException se) {
// Gestion de l'erreur
}
}
}
}@Transactional Overview
- Fournit une gestion déclarative des transactions
- Classe ou méthode annotées
- Alternative moderne aux annotations EJB
@Transactional(
//Type de transaction
//Les même que pour les EJB
value = TxType.REQUIRED, //Défaut
// Déclenche un rollback sur ces exceptions
rollbackOn = { SQLException.class },
// Ne déclenche pas de rollback sur ces exceptions
dontRollbackOn = { CustomException.class }
// Par défaut, toutes les exceptions déclenchent un rollback
)
public class CompteService {
public void transfer(long from, long to, BigDecimal amount) {
// Logique de transfert
}
}Transactions Gérées par Conteneur (CMT)
- Gestion automatique par le conteneur Jakarta EE
- Configuration déclarative via annotations ou XML
- La transaction :
- Commence au début d’une méthode publique
- Peut être propagée à d’autres méthodes
- Se termine à la fin de la méthode qui a commencé la transaction
@Transactional Classe ou méthode
Niveau Classe
- S’applique à toutes les méthodes publiques
- Configuration par défaut pour la classe
- Peut être surchargée au niveau méthode
- Héritage automatique par sous-classes
- Configuration cohérente pour le service
Priorités d’Application
- Annotation au niveau méthode
- Annotation au niveau classe
- Annotation sur interfaces
- Configuration par défaut
Niveau Méthode
- Granularité fine du contrôle
- Surcharge des paramètres de classe
- Plus grande flexibilité
- Meilleure lisibilité du code
Considérations
- Éviter les mélanges excessifs
- Privilégier la cohérence classe
- Documenter les surcharges
Attributs de @Transactional
Attributs de Base
- value (TxType)
- Définit le type de propagation
- Détermine comportement avec transactions existantes
- Par défaut: REQUIRED
- timeout
- Durée maximale en secondes
- 0 = pas de timeout
- Dépend du conteneur
Gestion des Exceptions
- rollbackOn
- Liste des exceptions déclenchant rollback
- Surcharge le comportement par défaut
- Prioritaire sur dontRollbackOn
- dontRollbackOn
- Exceptions ne déclenchant pas de rollback
- Utile pour exceptions métier non critiques
- Secondaire par rapport à rollbackOn
Types de Transactions
- REQUIRED (défaut)
- Utilise transaction existante
- Crée nouvelle si aucune existe
- Usage: Opérations CRUD standard
- REQUIRES_NEW
- Crée toujours nouvelle transaction
- Suspend transaction existante
- Usage: Opérations indépendantes
- SUPPORTS
- Utilise transaction si existe
- Continue sans transaction sinon
- Usage: Opérations lecture seule
- NOT_SUPPORTED
- Suspend transaction existante
- S’exécute hors transaction
- Usage: Opérations non transactionnelles
- MANDATORY
- Exige transaction existante
- Exception si pas de transaction
- Usage: Opérations critiques
- NEVER
- Erreur si transaction existe
- S’exécute hors transaction
- Usage: Services utilitaires
:::
Exemple de Transaction Gérée par Conteneur
@RequestScoped
public class CompteBean {
@PersistenceContext
private EntityManager em;
@Transaction(TxType.REQUIRED)
public void transferer(Long sourceId, Long destId, BigDecimal montant) {
Compte source = em.find(Compte.class, sourceId);
Compte dest = em.find(Compte.class, destId);
if (source.getSolde().compareTo(montant) < 0) {
throw new SoldeInsuffisantException();
}
source.setSolde(source.getSolde().subtract(montant));
dest.setSolde(dest.getSolde().add(montant));
em.merge(source);
em.merge(dest);
}
@Transaction(TxType.REQUIRES_NEW)
public void journaliser(String operation) {
Journal journal = new Journal();
journal.setOperation(operation);
journal.setDate(LocalDateTime.now());
em.persist(journal);
}
}Implantation de @Transactional par CDI (interceptor)
- Rôle de l’Intercepteur
- Intercepte appels méthodes annotées
- Gère cycle de vie transactionnel
- Wrapper transparent pour client
- Applique AOP (Aspect Oriented Programming)
- Génération du Proxy
- Génération dynamique au démarrage
- Même interface que classe cible
- Injection transparente via CDI
- Chaînage possible d’intercepteurs
Qu’annoter avec @Transactional?
- Opérations d’Écriture
- Méthodes modifiant des données
@Transactional(REQUIRED)
- Opérations de Lecture
- Méthodes consultation simple
- Sans effet de bord
- Performance privilégiée
@Transactional(SUPPORTS)
@TransactionScoped
- Définit une portée liée au cycle de vie d’une transaction
- Bean créé au début de la transaction
- Détruit à la fin de la transaction
@TransactionScoped
@Named
public class TransactionBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<Operation> operations = new ArrayList<>();
public void addOperation(Operation op) {
operations.add(op);
}
// Le bean et ses données persistent pendant toute la transaction
}Note: @TransactionScoped nécessite que les beans implantent Serializable
Bonnes Pratiques 1. Gestion des Timeouts
- Définir des timeouts appropriés par type d’opération
- Monitorer les transactions longues
- Prévoir mécanisme de reprise sur timeout
- Éviter les timeouts trop courts ou trop longs
Bonnes Pratiques 2. Granularité des Transactions
- Transaction Courte
- Durée minimale
- Moins de conflits
- Meilleure scalabilité
- Préférer pour opérations CRUD simples
- Transaction Longue
- À éviter si possible
- Risque deadlocks
- Impact performance
- Uniquement si cohérence forte nécessaire
Bonnes Pratiques 3. Gestion des Erreurs
- Différencier exceptions métier/technique
- Définir stratégie rollback claire
- Logger avant rollback
- Implémenter compensation si nécessaire
Bonnes Pratiques 4. Optimisation Performance
- Éviter transactions imbriquées
- Minimiser le nombre de ressources
- Utiliser read-only quand possible
- Choix judicieux isolation level
Bonnes Pratiques 5. Monitoring et Debug
- Activer logs transactionnels
- Surveiller métriques clés:
- Durée moyenne
- Taux rollback
- Nombre deadlocks
- Transactions actives
- JConsole, VisualVM, Application Server Console