Sessions et persistance

Relation proche entre les sessions et la persistance des données

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

Lazy loading permet de charger les objets au moment où l'on en a vraiment besoin. Si cet appel se fait dans le fichier de vue, on peut être confrontés à une exception LazyInitializationException, liée à la fermeture de la session et à l'impossibilité de charger les tables liées. A travers cet article on verra comment remédier à ce souci.

Sessions et persistance

Rappelons-nous ce qu'on s'est dit au sujet de la récupération des relations dans les entités. On a évoqué une possibilité de charger les entités associées uniquement quand on y accède. Cette fonctionnalité s'applique grâce au lazy loading. Quand cette récupération se fait du côté du contrôleur, il n'y a pas de problèmes : session qui gère la persistance dans Hibernate est ouverte. Cependant, les problèmes commencent au moment où on a besoin de ces objets lazy loaded seulement dans la vue. La session est alors fermée et les exceptions du type LazyInitializationException: Session has been closed peuvent apparaître sur dans les logs.

Hibernate a développé l'Open Session in View Pattern qui se base sur un intercepteur implémentant l'interface Filter. Dans cette solution la session active est liée au thread qui traite la requête. La transaction sera alors comité quand le thread termine son traitement et non pas quand la méthode fasse toutes les opérations.

Dans la documentation d'Hibernate on peut récupérer le code du filtre à écrire. Cependant, si l'on utilise Spring, on peut se contenter de rien faire. Ce framework fournit les filtres adaptés au JPA et Hibernate prêt à être définis dans le web.xml. Voici le filtre en question placé dans ce fichier de notre projet :

    <filter>
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
        <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 

Ce code débloque OpenEntityManagerInVilter pour toutes les requête. Toutes les associations chargées en mode lazy loading pourront être désormais lues directement dans la partie vue sans aucun souci. Cependant, tous les changements faits sur les données du côté des fichiers JSP ne vont pas persister dans la base.

Pourtant, un problème potentiel existe. Si l'une exception est lancée pendant la récupération des association lazy loaded dans la vue, il sera difficile de rediriger proprement la requête vers une page d'erreur adaptée à ce type de situations.

Regardons maintenant la preuve de ce qu'on avance. Pour notre test on créera une méthode testBook() dans la classe BookController. Il s'agit d'une méthode tout simple qui récupère un livre dont l'identifiant est 22. Ensuite dans la vue on récupérera le livre (${book}) et ses exemplaires (${book.bookCopies}) :

BookController {
    // ...
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public String testBook(Model layout)
    {
        layout.addAttribute("book",  bookRepository.findOne(22l));
        return "testBook";
    }
}

Et la vue :

${book}

${book.bookCopies}

Voici les différentes phases du test. Tout d'abord on va vérifier le comportement de la page avec le filtre OpenEntityManagerInViewFilter activé :

    // 1st test :
    @OneToMany(mappedBy = "book")
    public List getBookCopies() {
        return bookCopies;
    }
    // 2nd test : 
    @OneToMany(mappedBy = "book", fetch = FetchType.LAZY)
    public List getBookCopies() {
        return bookCopies;
    }

Dans les 2 cas, le code s'est exécuté normalement et on a pu voir la liste des exemplaires sur la page JSP.

A la deuxième étape on verra le comportement de l'application sans le filtre OpenEntityManagerInViewFilter :

    @OneToMany(mappedBy = "book", fetch = FetchType.LAZY)
    public List getBookCopies() {
        return bookCopies;
    }

Dans ce cas, à l'affichage de notre page de test, on verra LazyInitializationException :

afficher le code

Bartosz KONIECZNY Sessions

Une question ? Une remarque ?

*

*

Un conseil PHP

Comment ne pas afficher une partie de la page ?

En PHP, grâce aux méthodes liées aux tampons de sortie on peut librement manipuler les éléments qu'on veut afficher sur la page. La fonction ob_end_clean() permet de ne pas afficher le contenu défini avant son appel. Prenons un exemple :

echo 'test page';
ob_end_clean();
echo 'test 2 page';
Ce bout de code affichera uniquement le deuxième echo ("test 2 page"). Le premier sera ignoré et "supprimé" par la fonction ob_end_clean().