Le Persistence Context étendu est une fonctionnalité particulièrement intéressante proposée par Hibernate, mais qui est malheureusement peu connue, voire mal comprise.
Le but de ce billet est donc d’en expliquer simplement le fonctionnement, en espérant que cela pousse plus de monde à l’utiliser.
A quoi sert cette chose au nom barbare?Un Persistence Context étendu est utile dès lors qu’un utilisateur doit faire un certain nombre d’actions, et que vous souhaitez pouvoir sauvegarder l’ensemble des données ajoutées/modifiées/supprimées au sein d’une seule transaction.
C’est typiquement le cas lorsqu’un utilisateur rempli un « wizard »: cela peut être un panier dans une boutique en ligne, ou un QCM. C’est également très fréquemment le cas dans le cadre d’applications métier: un utilisateur va renseigner plusieurs écrans, et l’ensemble doit être sauvegardé en base de données.
Pourquoi vouloir gérer cet ensemble d’actions au sein d’une même transaction?
- C’est la seule chose correcte au niveau métier: votre client veut des données correctes en base, pas des « wizards » à moitié remplis, qui ne lui servent à rien
- C’est plus performant au niveau technique: une seule transaction pour tout insérer, c’est plus performant qu’une transaction à chaque changement d’écran
Comment peut-on réaliser cela? 2 méthodes classiques
- La méthode « Struts »: vous stockez ces données en Session, dans un objet spécifique. Lors de la confirmation finale par l’utilisateur, vous transformez alors cet objet « stockage de données » en objets Hibernate, et vous sauvegardez le tout. Cela fonctionne bien mais cela a deux défauts: il faut coder cet objet « stockage de données », et cet objet est géré en session (on ne peut faire qu’un traitement à la fois, ce qui conduit à des sites comme voyages-scnf.com où vous ne pouvez réserver qu’un seul billet à la fois).
- La méthode « GWT »: vous n’avez en fait qu’une seule page Web, avec des <div/> qui s’affichent et se cachent pour chaque écran, la confirmation finale envoyant l’ensemble des informations au serveur. Cette méthode est plus élégante, mais requiert d’avoir un framework JavaScript avancé (ainsi que d’avoir un navigateur Internet récent, ce qui n’est pas toujours le cas en entreprise). Pour information, c’est la méthode que j’utilise sur http://responcia.net.
La méthode du Persistence Context étenduLe Persistence Context étendu propose une troisième voie, qui est sans doute plus efficace dans le cadre d’une application métier: il s’agit d’un PersistenceContext qui survit sur plusieurs requêtes HTTP. Pour parler « Hibernate »: votre Session et son cache de 1er niveau survivent sur plusieurs requêtes, et ne sont flushés que lors de l’écran final.
Ce pattern vient en fait de JBoss Seam, et se retrouve dans les EJB 3 :
Utilisation simple avec Spring WebflowCe pattern a été implémenté dans Spring Webflow, lequel propose ainsi de lier un Persistence Context à un flow. Ceci corrige les deux problèmes que nous avons vu précédemment dans la méthode « Struts »:
- L’utilisateur final peut ouvrir plusieurs flows en parallèle, chacun ayant son propre PersistenceContext.
- Vous utilisez directement vos objets Hibernate, sans avoir à coder d’objet de « stockage de données », ce qui est un gain de productivité non négligeable.
Cette utilisation est décrite dans la documentation de Spring Webflow de manière assez succinte, et plus détaillée sur le site d’IBM:
On y voit que tout ce que vous avez besoin est de déclarer votre PersistenceContext dans le scope « flow », et de le commiter en fin de flow (un état « end-state »): c’est donc une solution qui est particulièrement élégante.
Utilisation avancée avec Spring WebflowComme toute solution, l’utilisation du PersistenceContext étendu avec Spring Webflow a quelques limites, mais aussi quelques astuces qu’il est préférable de connaitre avant de se lancer dans cette solution.
Tout d’abord, tout ce que fait Spring Webflow c’est repousser le moment du commit à la fin du flow:
- Rien ne vous empêche de commiter en plein milieu du flow. Cela peut être vu comme un problème, en réalité c’est plutôt une fontionnalité intéressante: il vous suffit d’appeler une méthode transactionnelle pour commiter, sans nécessairement passer par un « end-state ». Vous pouvez donc faire des commits intermédaires, si nécessaire.
- Si plusieurs utilisateurs travaillent sur les mêmes données, vous avez un risque plus élevé d’accès concurrent sur les données, car vous faites de « l’optimistic locking » sur plusieurs écrans.
Enfin, à l’heure actuelle, le Persistence Context n’est pas étendu dans les sous-flows. Cela devrait être corrigé dans la prochaine version de Spring Webflow: ils pourront alors utiliser les attributs habituels de propagation des transactions (REQUIRED, REQUIRES_NEW, etc), et donc mieux s’intégrer au flow principal: attention cependant, cette solution ne sera pas parfaite car un sous-flow ne sera alors pas une vraie transaction imbriquée, et annuler un sous-flow restera toujours problématique (la solution la plus simple lors de l’annulation étant de recharger en base de données les données modifiées dans le sous-flow).
ConclusionLe PersistenceContext étendu est un pattern utile pour qui veut gérer des enchainements d’écrans, en particulier si on l’utilise avec Spring Webflow.
Cette solution est particulièrement bien adaptée pour des applications métier « riches », et plus spécifiquement avec la technologie JSF. Si vous utilisez Spring Webflow pour gérer votre navigation JSF (
voir la documentation de Spring Faces), vous pouvez ainsi utiliser directement vos objets métier dans vos composants JSF, avec l’assurance que les modifications de l’utilisateur (via un envoi de formulaire ou un événement Ajax) seront prises en compte à la fin d’un flow.