Published by

Il y a 11 ans -

Temps de lecture 6 minutes

Transactions – Les concepts et modèles

En même temps indispensables et source de problèmes, les transactions restent trop souvent un des concepts les moins bien maîtrisés par les développeurs JEE.

Le but est ici d’en rappeler les principaux concepts et de présenter les modèles de gestion des transactions.

Quelques Concepts

ACID

Le plus souvent, les transactions sont présentées via l’acronyme ACID. Celui-ci liste les propriétés souhaitées d’une transaction:

  • Atomicité: toutes les opérations impliquées dans la transaction sont soit toutes validées soit toutes annulées.
  • Cohérence: la ressource concernée par la transaction ne pourra être laissée dans un état incohérent, c’est-à-dire à moitié consommée ou à moitié mise à jour.
  • Isolation: réfère le degré de séparation entre les transactions concurrentes (accédant à la même donnée). Plus l’isolation est grande, plus la cohérence augmente et la concurrence diminue.
  • Durabilité: une fois la transaction validée, les changements sont permanents et survivront à un crash du système sous-jacent.

Concernant l’isolation, dans le cadre des bases de données, il est possible de la configurer (Connection.html#setTransactionIsolation(int)). Il est ainsi possible d’être plus ou moins isolé des autres transactions.

JTA

Java Transaction API est une des APIs constituant JEE. Elle définit les interfaces permettant de gérer les transactions distribuées ; ici distribuées s’entend par réparties sur plusieurs gestionnaires de ressources transactionnelles (Exemple: 2 bases de données ou une base de données et un serveur de messages).
Bien souvent l’implémentation de JTA est fournie par le serveur d’application utilisé. Cependant pour les applications stand-alone ou s’exécutant dans un conteneur de servlets, il est tout à fait possible de s’appuyer sur JTA. Il faudra dans ce cas inclure un gestionnaire de transactions tiers, tel que celui de JBoss ou celui de JOnAS.

JTA est une petite API (au sens nombre de classes). En voici les principales:

  • UserTransaction: permet par programmation d’interagir avec une transaction (méthodes beging(), commit(), rollback(), getStatus()).
  • TransactionManager: fonctionnellement proche de UserTransaction avec en plus la possibilité de mettre en pause et de redémarrer une transaction.
  • Status: indique le statut de la transaction.

Les modèles

… ou comment déclarer ses transactions.

Les transactions locales

Les transactions locales désignent les transactions gérées directement par le gestionnaire de ressources sous-jacent. Le plus souvent, le développeur manipule des connections vers ce gestionnaire plutôt que des transactions.

Exemple avec une base de données:

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/datasource");
Connection connection = ds.getConnection();
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
String sql = "UPDATE name INTO user ...";
try {
    statement.executeUpdate(sql);
    connection.commit();
} catch (SQLException e) {
    connection.rollback();
    throw e;
} finally {
    statement.close();
    connection.close();
}

On notera que la propriété autoCommit de la connection est mise à false afin de garder la main sur le commit de celle-ci (et éventuellement exécuter plusieurs Statement avec la même transaction).

En utilisant les transactions locales, le développeur gère explicitement tout le cycle de vie des transactions, de leur création à leur fermeture.
Une limitation de ce modèle est que le développeur ne dispose d’aucun moyen pour participer à une transaction qui impliquerait plusieurs gestionnaires de ressources (Exemple: plusieurs base de données, ou une base de données et un serveur de messages).

Les transactions globales

Face à la limitation que l’on vient de mentionner, nous avons à notre disposition les transactions globales. Elles servent donc à gérer les transactions distribuées à l’aide de JTA, décrit plus haut.

Que l’on utilise Spring ou les EJB, il existe deux modèles pour utiliser les transactions globales: le modèle par programmation (« Programmatic Transaction Model ») et le modèle déclaratif (« Declarative Transaction Model »).

Le modèle par programmation

Dans le monde EJB, connu aussi sous le nom de « Bean Managed Transaction ».
Ici le développeur s’appuie directement sur les APIs de JTA pour manipuler les transactions.

Exemple avec un EJB:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class UserServiceImpl {
   UserDAO userDAO;

   public void update(User user) throws Exception {
      InitialContext context = new InitialContext();
      UserTransaction transaction = (UserTransaction)context.lookup("UserTransaction");
      try {
         transaction.begin();
	 userDAO.update(user);
         transaction.commit();
      } catch (Exception up) {
         transaction.rollback();
         throw up;
      }
   }
}

@TransactionManagement(TransactionManagementType.BEAN) est là pour indiquer que nous gérons nous-même les transactions ; si omis le gestionnaire de transaction suppose qu’on utilise un modèle déclaratif.
L’obtention d’une nouvelle transaction se fait ici par un lookup JNDI et le nom JNDI utilisé est spécifique à chaque serveur d’application.

Le modèle déclaratif

Dans le monde EJB, connu aussi sous le nom de « Container Managed Transaction ».
Le développeur ne s’occupe plus des transactions, tout (ou presque) est à la charge du gestionnaire de transaction. On indique seulement via des annotations les démarcations transactionnelles de sa fonction.

Exemple avec un EJB:

@Stateless
public class UserServiceImpl {
   UserDAO UserDAO;
   @Resource SessionContext context;

   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void update(User user) throws Exception {
      try {
	 userDAO.update(user);
      } catch (Exception up) {
         context.setRollbackOnly();
         throw up;
      }
   }
}

Le plus important ici, en plus de l’annotation TransactionAttribute, est la gestion du rollback. La levée d’une exception applicative ne déclenchera pas de rollback. Pour cela, nous avons deux solutions:

  • lever une exception système (exception qui étend RuntimeException)
  • indiquer explicitement le rollback de la transaction via l’appel context.setRollbackOnly().

Pour finir, XA et Two-Phase Commit

Le but de ce paragraphe n’est pas une présentation de XA, mais juste quelques mots qui peuvent vous aider lorsque vous les rencontrerez.
Lors d’une transaction globale, XA est utilisé par l’implémentation de l’api JTA pour faire communiquer le gestionnaire de transaction avec plusieurs gestionnaires de ressources ; le cas le plus courant étant une base de données avec un serveur de messages. On ne veut pas que le message soit envoyé si la mise à jour en base a échoué.
Pour cela XA s’appuie sur un protocole faisant participer tous les gestionnaires de ressources en question: le Two-Phase Commit. Comme indiqué dans le nom, ce protocole se déroule en 2 étapes. Premièrement, le gestionnaire de transaction demande à tous les gestionnaires de ressources s’ils sont prêts. Si tous répondent oui, alors le gestionnaire de transaction leur indique de committer.
Et c’est là qu’interviennent les fameuses exceptions heuristiques ({{HeuristicRollbackException}}, HeuristicCommitException et HeuristicMixedException) dont on ne sait souvent pas quoi en faire. Sans rentrer dans les détails, elles peuvent être levée par, par exemple, un timeout sur un des gestionnaires de ressources ou une décision non unanime lors de la deuxième phase.

Références

Published by

Commentaire

4 réponses pour " Transactions – Les concepts et modèles "

  1. Published by , Il y a 11 ans

    Article ‘rafraichissant’ sur les transactions.

    J’en profites pour ouvrir une question ouverte. Avez-vous déjà rencontré une implémentation de XA intéressante autre que celles des serveurs d’applications courants (ex : pour un utilisation dans un batch standalone avec deux sources de données JDBC, ou même des resources hétérogènes jms…).

    Plus précisément je souhaite utiliser un TransactionManager XA avec SpringBatch. Je suis tombé sur ce lien. http://www.javaworld.com/javaworld/jw-04-2007/jw-04-xa.html, je tâcherais de vous faire un retour.

    Auriez-vous des retours d’expérience ? Je ne pense pas être le premier à en avoir besoin.

  2. Published by , Il y a 11 ans

    Bonjour,

    Pour nos batch avec SpringBatch nous utilisons Atomikos (http://www.atomikos.com). La solution marche plutôt bien et nous n’avons à priori pas de problème particulier avec un environnement composé de plusieurs base de données différentes et de JMS.

  3. Published by , Il y a 11 ans

    Pour EJB3, nous pouvons ajouter l’annotation @ApplicationException(rollback = true) à la définition de l’exception pour automatiser le rollback à la levée de cette dernière.

  4. Published by , Il y a 1 mois

    Bonjour,
    Je reviens sur cet ancien post avec une question particulière sur laquelle vous pourriez avoir un avis.
    Est il possible d’utiliser l’api UserTransaction dans une application standalone (batch) qui invoque des EJB Session Stateless configurés pour laisser le conteneur gérer les transactions?
    Merci pour votre avis.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.