IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Développement Web en Java Discussion :

La gestion de la session de persistence dans une application web


Sujet :

Développement Web en Java

Vue hybride

djo.mos La gestion de la session de... 24/05/2007, 03h38
OButterlin Et bien personnellement je... 24/05/2007, 09h16
grishka je pense que c'est plus un... 24/05/2007, 11h09
azerr Bonjour, merci pour ce post... 24/05/2007, 11h10
OButterlin J'aime bien l'utilisation du... 25/05/2007, 11h09
azerr Bonjour Obretin J'aime... 25/05/2007, 12h01
Shad_idees Bonjour à tous, Je suis... 18/02/2009, 14h35
Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Par défaut La gestion de la session de persistence dans une application web
    Bonjour.
    Suite à d'âpres combats et nuits blanches, et de solutions trafiquotées, je lance cette discussion pour vous demander vos avis et vos retours d'expériences sur le sujet.
    Il s'agit de l'utilisation d'Hibernate (ou un autre framework de persistence) dans le contexte d'une application web.
    Le problème se résume au mot "Lazy Fetching Exception", je ne sais pas pour vous, mais en ce qui me concerne, ça m'a pourri la vie au départ (et même quelques fois maintenant). Ce problème provient de deux causes (en général) :
    • lors du rendu des views (après les appels aux DAOs et la fin des transactions du framework de persistence.
    • lors des prochaines requêtes (suite à la fermeture et à la réouverture de la session du framework de persistence), lors de l'accès aux objets détachés.
    A travers plusieurs projets, j'ai utilisé plusieures des solutions suivantes :
    • mettre toutes les associations au mode EAGER : Je sais, c'est moche et stupide, mais ça résout tout ! Assurez vous cependant, je le fais plus, c'etait à l'époque de mes premiers projets Struts-Hibernate.
    • Rafraichir systématiquement les DTOs avant de leur accéder. j' ajoute une méthode refresh(id) dans mes DAOs qui retrouve mon bean via son id et le sort ainsi de l'état detached. Solution très lourde à mettre en oeuvre, rallonge le code, augmente le traffic de/vers la BD. Ceci résout le second problème.
    • Déléger l'ouverture et fermeture des transactions à une couche supérieure, comme à un filtre par exemple. pas joli comme manip !
    • etc..
    • Ouvrir une session de persistence par utilisateur et la stocker dans sa session (web, coté serveur) : J'adore cette solution ! plus de refresh, plus de lazy fetch exceptions, etc. MAIS, pour l'utilisation de la RAM et d'espace session, bonjour les dégats ! C'est la solution que j'utilises actuellement.
    Bref, je souhaitais vous demander votre avis sur la question.

    P.S. : je parle d'un environnement web dans un Servlet Container uniquement, comme Tomcat par exemple. Je précise ceci pour pas que l'on me propose d'utiliser les managed sessions des EJB3 et compagnie.

    En attendnt vos réponses, merci d'avance.

  2. #2
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Et bien personnellement je développe une application WEB avec un serveur TOMCAT (pour les tests) avec une couche de persistence hibernate et je n'ai jamais été confronté à un problème lié au lazy loading...
    Plutôt à des problèmes de mise à jour avec un message du type "il y a déjà un objet en session avec le même id" (lié à des contrôles avec "oublie" d'evict)
    Bref, j'utilise :
    - une couche ActionForm
    - une couche DispatchAction
    - une couche service qui permet de centraliser les opérations DB (métier)
    - une couche POJO (hibernate)
    - une couche présentation JSP (avec Struts-Layout)

    Pour ne pas être confronté au problème de lazy loading, mes contrôleurs s'arrangent pour faire des accès aux sous-objets dans la mesure où ils sont utilisés par la couche de présentation. Ainsi, même si quelqu'un (par la suite) modifiait le paramétrage d'hibernate, ça continuera de fonctionner.
    C'est d'ailleurs un point critique de cette techno, tu places judicieusement un lazy="false", tu traites les données dans la page, et un jour, quelqu'un se dit "je vais optimiser tout ça", et là,

    J'ajoute que mes contrôleurs ont un point d'entrée commun qui s'occupe (entre autre) de récupérer une Session (hibernate) et de la libérer. La couche Service ne s'occupe jamais de ça, elle utilise un context qu'on lui passe (pour la gestion des transactions multi-services, ça aide... )

    C'est sûr, ma façon de faire rajoute du code dans les contrôleurs (très peu en fait) et ajoute un lien fort entre la page et le contrôleur, mais au moins, ça colle.

    A+
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre chevronné
    Avatar de grishka
    Inscrit en
    Janvier 2003
    Messages
    285
    Détails du profil
    Informations forums :
    Inscription : Janvier 2003
    Messages : 285
    Par défaut
    je pense que c'est plus un problème de conception qu'un problème technique

    j'ai été très "extremiste" et j'ai laissé le mode lazy par défaut pour toutes les associations dans la définition du mapping. La couche d'accès aux données est censée fournir tous les objets nécessaires à la présentation. Donc j'ai utilisé systématiquement des requêtes HQL et des jointures rapportées (fetch join). La portée de la session Hibernate est normalement restreinte à la couche Dao. Par contre il est possible de démarquer les transactions applicatives au niveau de la couche service ou action (donc il est possible de rapporter plus d'objets que ce que retourne la couche dao tant que l'on reste dans cette démarcation, ce qui a mon sens est mal ).
    autre problème : l'objet détaché issu de la présentation ne contient pas un l'objet associé lors de l'enregistrement des modifs (ou lors d'un contrôle) : généralement un saveOrUpdate suffit pour resynchroniser l'objet avec la base.

    pour le problème "il y a déjà un objet en session avec le même id", j'ai utilisé session.merge() de mémoire

  4. #4
    Membre Expert
    Avatar de azerr
    Homme Profil pro
    Ingénieur Etude JEE/Eclipse RCP
    Inscrit en
    Avril 2006
    Messages
    942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Etude JEE/Eclipse RCP
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2006
    Messages : 942
    Par défaut
    Bonjour,
    merci pour ce post qui est un sujet très intéressant et qui paraît au début simple à mettre en place. Je n'ai pas la science infuse, mais après de multiple lecture et d'essai que j'ai pu faire l'architecture que j'utilise est la suivante:

    • une couche DAO : qui permet de gerer les acces aux donnees. Cette couche retourne les Bean persistent d'Hibernate sans effectuer un detachemenet de l'objet.
    • une couche Service : qui permet de gérer le métier de l'application.
      Les services font appels aux DAO. Ce sont eux qui gèrent les transactions, ouverture/fermeture des connections. Les ActionStruts qui font appel à ces services utilisent des interfaces services et non leur implémentation. Ce mecanisme permet d'etre tres evolutif. En effet si le jour ou vous decidez de changer le mode d'acces aux donnees (ex passer d'une base de donnees SGBD a une base XML, ou a un appel a un Web Service), les Action Struts ne seront pas impactes. Seul les impelementatons des Services devront etre developes. Avec Spring, l'implementation du service est defini dans son fichier de config qui permet dans ce cas de jouer le role de factory. Pour modifier l'implementation du service il suffit juste de modifier le fichier spring.


    Cette technique permet de faire des Mock Object (classe bouchon). Souvent un developpeur a besoin d'un service qui n'est pas encore developpe pour l'appeler dans son Action Struts. Il suffit de definir l'interface service. Le developpeur implemente ce service qui retourne des donnees coder en dur (sans acceder à la base) et definit dans le fichier de config Spring d'utiliser cette implemntation. Il ne pollue pas le projet avec son implementation du service.

    Une couche service definit la logique de l'application, mais aussi celle de l'entreprise. En effet ces service pourrait etre appele dans une autre application (comme une application Swing). Donc la règle que je me suis fixé et de n'utiliser aucun contexte particulier (HTTP request,Http session) dans les services. De plus ca n'est pas a la couche qui appele ces services de gerer mes transactions,...

    Sur le site Hibernate, il existe plusieurs facon de gerer la session Hibernate. ce que j'ai retenu :

    • Open In View : qui signifie que la session est ouverte dans la couche Vue presentation (pour pouvoir utiliser le mode lazy). Ceci necessite de developper un filter qui ouvre une session au debut du filtre et qui la ferme a la fin (une fois que la JSP a fini d'etre parse). Cette solution peut paraitre au debut tres seduisante, car on peut appeler en mode lazy des lists dans les JSP.
      Mais je ne l'aime pas car :

      1. le mode lazy je ne l'utilise plus, car meme si il parait tres seduisant, il peut plomber les performances tres rapidement (un item d'une liste appele une liste qui appele un autre liste,...). Et meme si on se dit "OK je connais ce probleme, je vais faire attention, je vais l'utiliser juste pour une petite liste", une application evolue et ses donnees aussi et on peut rapidement plomber les performances. Apres evolution, cette petite liste peut faire reference a d'autre liste et c'est la ou ca devient un cauchemard.
      2. la gestion d'erreur est impossible a effectuer. En effet uen requete plante, la page ne pourra pas s'afficher, alors que souvent on souhaite afficher un message d'avertissement ou d'erreur avec le formulaire de la page.
      3. On cree un fort couplage avec Hibernate. Le jour on l'on souhaite change de mode d'accès, il faut revoir toute l'architecture de l'application.

    • ThreadLocal : (c'est celle que je prefere) la session hibernate est mise dans une ThreadLocale. Si vous ne connaissez pas cette objet, je vous conseille de l'etudier. Il n'est vraiment pas complique, et il permet de reglmer beaucoup de problemes. Une ThreadLocal est une Thread qui s'attache a la Thread courante. Par exemple, ce que je fait souvent, c'est de mettre dans une ThredLocal mon objet User (qui est aussi stocke dans la session) a l'aide de mon RequestProcessor de Struts. L'interet de ce mecanisme est de pouvoir recuperer mon objet User sans avoir le contexte Session.

      Vous pouver recuperer le User dans un getter d'une classe JAVA qui n'a pas le contexte Session.

      Pour Hibernate c'est pareil. On stocke la session hibernate dans une ThreadLocal (au lieu de la mettre dans une Session utilisateur). Il y a de nombreux exemples sur le WEB de classe utilitaire Hibernate Session.


    La couche service recupere les Bean des DAO (implemente en Hibernate).
    Les DAO n'ont aucun metier dans leur code! Elle font juste les acces aux donnees. Ce sont les services qui gere le metier. Les services retournent les DTO et pas les Bean. Cette couche joue le role d'assemblage des DTO a partir de plusieurs Bean.

    Ca c'est un sujet polémique, car souvent on me dit "pourquoi ne pas utiliser les Bean directement?". Si on veut utiliser les Bean directement ceci signifie que :

    1. il faut absolument detacher l'objet de la session Hibernate lorsque le service retourne le Bean a la couche Action.
    2. On cree un fort couplage avec la base de donnees. Meme si Hibernate est tres puissant pour creer un graphe d'objet. Le jour ou la structure de la base de donnees change, la structure des Bean change aussi et du coup les Action Struts sont aussi impactes. Si le Service assemble les DTO a partir des Beans, seul cette couche sera impactée.


    Avec cette architecture en service, l'ouverture de la session s'effectue au debut de l'appel de la methode du service et en fin de l'appel. C'est sur ce sujet que Spring (et je pense que EJB3 fait la meme chose) intervient et dont je ne plus m'en passer.

    En effet souvent, on creer un service qui :
    * creer un utilisateur createUser
    * creer l'association roles/utilisateur createRoles.

    Et puis on souhaite creer un service qui fait appel aux 2, mais dans la même transaction. Si on code l'ouverture/fermeture des sessions au debut et fin de méthode, on ne peut gerer ce cas. Pour faire ceci on doit changer des methodes createUser, createRoles en leur passant la session en parametres.
    Il ya donc deux methodes pour chacun des services :

    * createUser(Session session) => qui cree un utilisateur mais qui ne commit pas la transaction.
    * createUser() => qui fait appel a la methode createUser(Session session) et qui commite la session.

    En suite on peut faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    createUserRoles() {
    Session session = ...
    createUser(session)
    createRoles (session)
    // commit de la session
    }
    Mais ca peut devenir horrible si on doit faire ca pour d'autres services.
    Avec Spring, on ne se soucie pas de l'ouverture/fermeture de la connection.
    En effet Spring est base sur le de l'AOP (Programmation par Aspect) qui permet de definir des regles sur les appeles des methodes.

    Pour simplifier on definit la regles au debut de l'appel des methodes create*
    on ouvre une session en transation et a la fin de la methode ou commit ou rollback.

    Je ne vais pas m'etendre sur le sujet, mais j'ai tente d'expliquer tout ca sur http://gestcv.sourceforge.net/fr/arc...re/spring.html

    Etant donne que ce post concerne l'architecture d'une application J2EE, je me permets aussi de vous parler rapidement d'Akrogen http://akrogen.sourceforge.net/fr/ qui est un plugin Eclipse de generation de code qui permet de decrire ses Wizards page en XML/XUL & Javascript.

    L'inetret d'Akrogen est de pouvoir creer des templates et des Wizard Eclispe
    pour sa propre architecture. Car souvent les generateurs de code ont des Wizards Eclipse code en JAVA mais qui nbe sont pas parametrables. Avec Akrogen vous ecrivez votre Wizard page en XML/XUL et Javascript (por gerer la logique du Wizard page).

    J'ai commence a creer un catalogue de template pour Struts 1.x (c'est tes basique pour l'instant) http://akrogen.sourceforge.net/fr/st.../struts1x.html
    Si vous ete sinteresses pour m'aider a concevoir d'autres catalogues (pour Hibernate, struts 2.x,...) n'hesitez pas à me contacter.

    Voila j'espere que j'ai ete clair dans mes explications, et n'hesitez surtout pas a me critiquer.

    Angelo

  5. #5
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    J'aime bien l'utilisation du filtre pour la centralisation de la libération...
    J'utilise également ThreadLocal mais il faut bien reconnaitre qu'il y a des limites vite atteintes (surtout avec struts) vu que chaque contrôleur tourne dans son propre thread.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Membre Expert
    Avatar de azerr
    Homme Profil pro
    Ingénieur Etude JEE/Eclipse RCP
    Inscrit en
    Avril 2006
    Messages
    942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Etude JEE/Eclipse RCP
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2006
    Messages : 942
    Par défaut
    Bonjour Obretin

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    J'aime bien l'utilisation du filtre pour la centralisation de la libération...
    Comme je disais dans mon post, en faisant ca tu creer un fort couplage Hibernate dans ton Application Struts.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    J'utilise également ThreadLocal mais il faut bien reconnaitre qu'il y a des limites vite atteintes (surtout avec struts) vu que chaque contrôleur tourne dans son propre thread.
    Je suis desole mais je ne comprends pas trop ce que tu veux dire. Dans mon projet gestcv http://gestcv.sourceforge.net/fr/architecture.html, je mets mon objet User stocké en session dans une ThreadLocal dans la methode process du RequestProcessor. Le RequestProcessor etant le premier appele avant les Actions, je suis sur que mon objet user est mis a jour correctement dans la ThreadLocal.

    Chaque appel d'une url sur le serveur creer une tache ou recupere une tache
    du pooling de tache. Le Requestprocessor met a jour cet objet user en premier et les actiosn peuvent ensuite bénéficier de l'objet user mis dans la ThreadLocal.

    mais peut etre que je n'ai pas bien compris ton probleme?

    Angelo

  7. #7
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Tu entends quoi par couplage fort dans ce cas de figure ?
    (je n'utilise ce point que pour libérer les ressources)

    Pour l'autre point, à vrai dire, je ne connais pas d'avance si j'aurai besoin d'une liaison avec la DB ou pas et donc, je demande une session à la première demande. Le problème, c'est qu'avec le ThreadLocal, on est ici lié au thread du contrôleur et donc, dans un même "request", on peut avoir plus d'une session (Heureusement, la structure de l'application fait que ça ne gène pas, mais c'est tout de même bête de fermer et reouvrir une session dans le même request)

    Dans tous les cas, je vais jeter un oeil sur le RequestProcessor, ça parait bon...

    A+
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 6
    Par défaut
    Bonjour à tous,

    Je suis confronté à un probleme de lazy loading. j'ai un découpage tres simple : objet métier, DAO, JSP/Servlet. Mes objets sont chargés dans mes DAO, et mes JSP affichent leur contenu

    J'ai lu attentivement les échanges de ce posts, mais je dois avouer que les solutions proposées par chacun me paraisse compliquées par rapport à mon souci et mes compétences... et je me dis qu'il y a peut etre une solution plus simple ?

    Je vous explique... Pour isoler le probleme, on va dire que j'ai trois classes
    - Cheval avec comme propriétés : Long id,String sexe, String nom, int age, Etable etableRatachee, List listeJockeys.
    - Etable: Long id, int numeroEmplacement
    - Jockey : Long id, String nom, String prenom...

    Un cheval est rataché à une seule étable. Il peut etre monté par une liste de Jockeys.

    Lorsque je veux faire un monCheval.getEtable(), j'ai une exception de lazy initialisation...

    Question 1 : J'ai cru comprendre qu'il était possible d'initialiser une collection de la sorte : Hibernate.initialize(nomDeLaCollection).
    Donc je présume que si je fais un :

    Hibernate.initialize(monCheval.getListeJockeys());

    dans la session de mon DAO, que je ferme ma session dans mon DAO, lorsque j'accederais à monCheval.getListeJockeys(), je n'aurais pas de probleme de lazyinitialisationexception. Est ce bien cela ?

    Je n'ai pas essayé pour la collection de ListeJockeys, car pour le moment je n'en suis pas la car je suis bloqué dans la seconde question...

    Question 2 : La où je suis coincé pour le moment, c'est sur
    monCheval.getEtableRatachee();

    J'ai une lazyinitialisationexception... Visiblement ca ne charge pas l objet etableRattachee si je fais un
    Hibernate.initialize(monCheval.getEtableRattachee());

    (Dans mon mapping je suis par défaut en lazy=true).
    Y a t il un moyen "simple" de charger cet objet dans mon DAO tant que ma session est ouverte, afin de pouvoir l'utiliser dans ma couche de présentation ?

    Question 3 : dans mon dao qui me permettra de récupérer de mes chevaux, et leur graphes associés, j'aurais un code similaire à celui la :

    List lChevaux = (List) s.createQuery("from Cheval where sexe='f'").list();

    Donc j'aurais une liste de cheval en retour. Mais la aussi j'aurais le probleme des etablesRatachees non chargees. Comment faire pour forcer leur chargement ?

    Quelqu'un peut il m'aider ?

Discussions similaires

  1. [MySQL] Gestion des données d'un utilisateur dans une application
    Par Boujoute dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 05/06/2012, 14h48
  2. Problème de gestion de suppression dans une application web dynamic data
    Par mo5andes dans le forum Développement Web avec .NET
    Réponses: 4
    Dernier message: 21/06/2010, 10h35
  3. XML/XSL et gestion des fichiers dans une application Web
    Par fatenatwork dans le forum XML/XSL et SOAP
    Réponses: 1
    Dernier message: 01/02/2008, 14h09
  4. Persistence dans une application desktop
    Par jproto dans le forum NetBeans
    Réponses: 4
    Dernier message: 04/07/2006, 14h01
  5. Réponses: 2
    Dernier message: 24/01/2006, 09h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo