Transactions avec Spring et JPA

1. Vue d'ensemble

Ce didacticiel abordera la bonne façon de configurer Spring Transactions , comment utiliser l' annotation @Transactional et les pièges courants.

Pour une discussion plus approfondie sur la configuration de la persistance principale, consultez le didacticiel Spring with JPA.

Fondamentalement, il existe deux façons distinctes de configurer les transactions - les annotations et l'AOP - chacune avec ses propres avantages. Nous allons discuter de la configuration d'annotation la plus courante ici.

2. Configurer les transactions

Spring 3.1 introduit l' annotation @EnableTransactionManagement que nous pouvons utiliser dans une classe @Configuration et activer la prise en charge transactionnelle:

@Configuration @EnableTransactionManagement public class PersistenceJPAConfig{ @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){ //... } @Bean public PlatformTransactionManager transactionManager(){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory( entityManagerFactoryBean().getObject() ); return transactionManager; } }

Cependant, si nous utilisons un projet Spring Boot et que nous avons des dépendances spring-data- * ou spring-tx sur le chemin de classe, la gestion des transactions sera activée par défaut .

3. Configurer les transactions avec XML

Avant 3.1 ou si Java n'est pas une option, voici la configuration XML, utilisant la gestion des annotations et le support de l'espace de noms:

4. L' annotation @Transactional

Avec les transactions configurées, nous pouvons maintenant annoter un bean avec @Transactional soit au niveau de la classe, soit au niveau de la méthode:

@Service @Transactional public class FooService { //... }

L'annotation prend également en charge d' autres configurations :

  • le type de propagation de la transaction
  • le niveau d'isolement de la transaction
  • a Timeout pour l'opération encapsulée par la transaction
  • un indicateur readOnly - un indice pour le fournisseur de persistance que la transaction doit être en lecture seule
  • les règles de Rollback pour la transaction

Notez que - par défaut, la restauration se produit uniquement pour les exceptions non cochées à l'exécution. L'exception vérifiée ne déclenche pas une annulation de la transaction. Nous pouvons bien sûr configurer ce comportement avec les paramètres d'annotation rollbackFor et noRollbackFor .

5. Pièges potentiels

5.1. Transactions et procurations

À un niveau élevé, Spring crée des proxys pour toutes les classes annotées avec @Transactional - soit sur la classe ou sur l'une des méthodes. Le proxy permet au framework d'injecter une logique transactionnelle avant et après la méthode en cours d'exécution - principalement pour démarrer et valider la transaction.

Ce qu'il est important de garder à l'esprit, c'est que si le bean transactionnel implémente une interface, le proxy sera par défaut un proxy dynamique Java. Cela signifie que seuls les appels de méthode externes qui entrent via le proxy seront interceptés. Les appels d'auto-invocation ne démarreront aucune transaction, même si la méthode a l' annotation @Transactional .

Une autre mise en garde concernant l'utilisation de proxies est que seules les méthodes publiques doivent être annotées avec @Transactional. Les méthodes de toute autre visibilité ignoreront simplement l'annotation en silence car elles ne sont pas mandatées.

Cet article traite plus en détail des pièges du proxy.

5.2. Modification du niveau d'isolement

Nous pouvons également modifier le niveau d'isolement des transactions:

@Transactional(isolation = Isolation.SERIALIZABLE)

Notez que cela a en fait été introduit au printemps 4.1; si nous exécutons l'exemple ci-dessus avant Spring 4.1, il en résultera:

org.springframework.transaction.InvalidIsolationLevelException : JPA standard ne prend pas en charge les niveaux d'isolation personnalisés - utilisez un JpaDialect spécial pour votre implémentation JPA

5.3. Transactions en lecture seule

L' indicateur readOnly génère généralement de la confusion, en particulier lorsque vous travaillez avec JPA; depuis le Javadoc:

Cela sert simplement d'indication pour le sous-système de transaction réel; cela n'entraînera pas nécessairement l' échec des tentatives d'accès en écriture. Un gestionnaire de transactions qui ne peut pas interpréter l'indication en lecture seule ne lèvera pas d'exception lorsqu'on lui demandera une transaction en lecture seule.

Le fait est que nous ne pouvons pas être sûrs qu'une insertion ou une mise à jour ne se produira pas lorsque l' indicateur readOnly est défini. Ce comportement dépend du fournisseur, alors que JPA est indépendant du fournisseur.

Il est également important de comprendre que l' indicateur readOnly n'est pertinent que dans une transaction. Si une opération se produit en dehors d'un contexte transactionnel, l'indicateur est simplement ignoré. Un exemple simple de cela appellerait une méthode annotée avec:

@Transactional( propagation = Propagation.SUPPORTS,readOnly = true )

à partir d'un contexte non transactionnel - une transaction ne sera pas créée et l' indicateur readOnly sera ignoré.

5.4. Journalisation des transactions

Une méthode utile pour comprendre les problèmes liés aux transactions consiste à affiner la journalisation dans les packages transactionnels. Le package pertinent dans Spring est « org.springframework.transaction», qui doit être configuré avec un niveau de journalisation TRACE.

6. Conclusion

Nous avons couvert la configuration de base de la sémantique transactionnelle en utilisant à la fois Java et XML, comment utiliser @Transactional et les meilleures pratiques d'une stratégie transactionnelle.

Comme toujours, le code présenté dans cet article est disponible à l'adresse over sur Github.