Passer au contenu principal

Un petit article sur les transactions en JEE. Il ne s’agit pas de réexpliquer les mécanismes mis en oeuvre, à savoir l’AOP, mais des choses beaucoup plus terre à terre.

Les transactions dans les applications JEE se définissent sur deux niveaux, à savoir au niveau applicatif et au niveau base de données. Pour rappel les transactions doivent en théorie satisfaire les quatre principes suivants :

  • Atomicité : soit la transaction passe entièrement, soit on revient à la situation initiale. Pas d’état intermédiaire.
  • Cohérence : la transaction ne doit pas casser la cohérence des données en base.
  • Isolation : la transaction ne doit pas « voir » ce que font d’autres transactions qui se dérouleraient en même temps qu’elle.
  • Durabilité : une fois la transaction réussie, les données doivent être stockées de manière permanente en base de données.

On désigne habituellement sous le terme ACID les principes ci-dessus. Les mécanismes mis en jeu pour y parvenir sont du genre compliqués surtout au niveau de la base de données, mais on y reviendra un peu plus tard.

Les niveaux transactionnels en Java/JEE

Dans ce paragraphe nous ne faisons pas la distinctions entre les niveaux transactionnels offerts par Spring et ceux offerts par les EJB tant ils sont proches. D’ailleurs on va prendre la notation standard, à savoir celle des EJB. Dans les applications modernes les transactions sont généralement définies par annotations. La liste ci-dessous indique les différents niveaux transactionnels disponibles.

  • Required : vérifie que la méthode ainsi annotée a été lancée dans un contexte transactionnel. Si ce n’est pas le cas, crée un contexte transactionnel avant de l’invoquer. Si une exception est levée, fait un rollback sur la transaction.
  • RequiresNew : idem que pour Required, sauf qu’un nouveau contexte transactionnel est créé même si on est déjà dans un contexte transactionnel au moment d’invoquer la méthode.
  • Supported : si la méthode n’est pas invoquée dans un contexte transactionnel, n’en crée pas de novueau, sinon, reste dans le contexte transactionnel courant.
  • NotSupported : si la méthode est invoquée dans un contexte transactionnel, sort de ce contexte le temps de l’exécuter.
  • Mandatory : vérifie que la méthode est lancée dans un contexte transactionnel. Si ce n’est pas le cas, lève une exception.
  • Never : vérifie que la méthode n’est pas lancée dans un contexte transactionnel. Si c’est le cas, lève une exception.

Dans la pratique Required est utilisé pour les opérations en écriture, Supported pour les opérations en lecture. RequiresNew et NotSupported sont rarement utilisés, et on se demande pourquoi Mandatory et Never existent…

Notez toutefois que si vous utilisez des contextes transactionnels, il est de bon aloi d’annoter toutes les méthodes publiques de vos classes avec des informations de contexte, du moins pour celles qui peuvent être invoquées dans le cadre d’une transaction.

Au niveau de la base

Pour que les transactions fonctionnent, il convient aussi de configurer la base de données correctement. Cette configuration aura un impact sur l’isolation des transactions, mais aussi sur les performances. Et comme vous pouvez vous en douter, plus l’isolation est élevée, moins bonnes sont les performances. Bref il s’agit comme toujours d’une histoire de compromis…

Voici la liste des configurations disponibles :

  • Read uncommitted : aucune isolation. Chaque transaction en cours « voit » ce que fond les autres transactions, y compris ce qui n’a pas encore été committé par ces dernières. Ceci peut être très problématiques si les traitements à effectuer sont fonctions de valeurs non encore committées.
  • Read committed : le niveau de base de l’isolation. Chaque transaction en cours voit uniquement ce qui a été committé par les autres transactions. Le problème de ce niveau concerne les lectures non répétables, à savoir qu’à un temps t un champ de la base aura une première valeur, et à t+n il aura pris une autre valeur committée par une autre transaction.
  • Repeatable reads : garantit que les lectures de valeurs sont répétables. Cependant ce niveau ne protège pas contre les lectures fantômes, à savoir que lancer deux fois le même SELECT au cours du temps dans une même transaction peut renvoyer des résultats différents.
  • Serializable : le niveau d’isolation maximal, en fait le seul qui satisfasse réellement les principes ACID… et de loin le plus lent.

Dans la pratique le niveau Read committed suffit dans la plupart des cas.

En bref…

Au niveau des transactions comme dans beaucoup de choses tout est affaire de compromis, suivant les besoins fonctionnels de l’application sur laquelle vous travaillez. Veillez toutefois à ne pas mettre toutes les méthodes en Required ou RequiresNew au niveau JEE sous peine de deadlocks ou du moins d’un sérieux impact sur les performances…

Cet article vous a plu ? Vous aimerez sûrement aussi :

Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter Son LinkedIn

Laisser un commentaire