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

Hibernate Java Discussion :

Persistance et organisation du code


Sujet :

Hibernate Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    370
    Détails du profil
    Informations personnelles :
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Avril 2006
    Messages : 370
    Par défaut Persistance et organisation du code
    J'ai pour habitude, lorsque je développe un projet, de bien séparer les couches de l'application et généralement, pour la persistance, je crée une classe qui servira d'interface entre le monde metier et le monde persistance.
    Cette classe regroupe les différentes actions faisables sur la base et permet ainsi en cas de changement de persistance de conserver intacte le code metier (simple redefinition des methodes de cette classe)

    Mais la avec hibernate, je n'arrive pas a mettre en place une tel solution : en effet je me retrouve bloqué car les fonctionnalitées interessantes d'hibernate nécessitent que les objets soit persistant. Les actions successives doivent donc avoir lieu au sein de la même session.

    Donc ma question est, pour les habitués d'Hibernate comment conserver vous un code metier non pollué (ie: j'aimerai éviter les appel a HibernateUtil dans tout les coin ou j'ai besoin de persister) tout en pouvant tiré parti des fonctionnalitées d'hibernate (lazy loading ...) ? Des explications, des exemples (petit diagrammes ...)

  2. #2
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    370
    Détails du profil
    Informations personnelles :
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Avril 2006
    Messages : 370
    Par défaut
    Voici un petit exemple illustrant mon problème :

    Ce code permet de recuperer un utilisateur dans la base de donnée et de le placer dans une Collection :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public User recupererUser(int idUser){
            User util = null;
            
            /*On regarde si l'utilisateur est toujours dans la liste des user connecté*/
            for(User us : users)
            {
                if(us.getId() == idUser)
                {
                    util = us;
                    break;
                }
            }
            
            //On va rechercher dans la base de données
            if (util == null)
            {
                   //util = gestionPersist.recupererUser(idUser);
                Session s = HibernateUtil.currentSession();
                Transaction tx = s.beginTransaction();
                
                 util = (User) s.get(User.class,idUser);
                
                tx.commit();
                HibernateUtil.closeSession();
                
                    if (util == null)
                    {
                            throw new IllegalArgumentException() ;
                    }
                    users.add(util); //On le remet dans la liste des user connécté
            }
            return util;        
        }
    la ligne en commentaire illustre l'utilisation de ma classe de gestion de persistance. Or a la fin de cette methode, je clos la session donc le lazy loading sur mon objet User (qui possède une collection ne peut plus marcher ...

    Exemple dans une autre méthode de la classe de gestion des users

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public String ajouterFichier(int idUser, FileItem fichier){
     
            User us;
            try {
                us = recupererUser(idUser); //Exception a gerer
            }
            catch (IllegalArgumentException e){
                throw e;
            }
            StockageFichier stock = new StockageFichier(us.getNom()+us.getPrenom(),fichier);
            stock.stocker();
            Fichier fic = new Fichier(fichier.getName());
            Session s = HibernateUtil.currentSession();
            Transaction tx = s.beginTransaction();
            s.lock(us,LockMode.UPGRADE);//L'utilisateur est detaché de la persistance donc pb pr lazy loading il faut le réattacher manuellement
            s.refresh(us);
            us.addFicher(fic); //Une fois réattaché le lazy loading fonctionne (sinon exception)
            fic.creerLiens();
            tx.commit();
            HibernateUtil.closeSession();
            return fic.getLienPage();
        }
    Illustraton du problème avec le lazy loading : sans réattacher l'objet pas moyen d'ajouter des fichier a sa collection.
    On voit bien que le code n'est pas très élégant, que les methode de gestion d'hibernate n'ont pas a etre ici : vous me direz pourquoi ne pas faire cette action dans une methode de la persistance :
    1. J'y ai pensé, mais es-ce que une action comme user.addFichier() a t'elle bien sa place dans la persistance (perso je ne pense pas)
    2. Je vais être ennuyer à chaque fois que je vais vouloir acceder à mes éléments de la Collection de l'user (le lazy loading ne fonctionnera pas puisque l'objet est détaché ...
    Voyez vous une solution pour rendre tout cela moins fouilli structurellement parlant ? Moi je sèche ...

    Perso je pense que éliminer le lazy loading éviterai certains problèmes mais je perds alors une fonctionnalité clé d'Hibernate ce qui me dérange quand même pas mal.

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    370
    Détails du profil
    Informations personnelles :
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Avril 2006
    Messages : 370
    Par défaut
    En fait ce que je voudrais concretement c'est une explication sur l'integration d'hibernate dans une vrai application, parceque tout ce que l'on trouve sur le net c'est facile, hop de classe bidon un mapping et une classe de test ...

    Mais bon c'est pas ca qui va expliquer comment architecturer une vrai application, où gère t'on les sessions, où placé au mieux et sous quel forme les accès à la base (des classes spécifiques, dans les classes metier (mouais)) comment intégré tout ca dans un environnement web ...

    Voila j'ai a présent bien compris les notions d'hibernate comme l'état des objets, le loading ... mais je voudrais bien de l'aide pour savoir comment intégrer tout cela et tirer le meilleur parti d'hibernate.

    Donc si vous avez des diagrammes UML, des appli open source, des explications concrètes ou tout autre a me faire circuler où je pourrais voir tout cela je vous en serais grandement reconnaissant ...

  4. #4
    Membre émérite Avatar de BizuR
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    688
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 688
    Par défaut
    Si je devais répondre à ta question, je dirai qu'il y a plusieurs solutions. Encore une fois, je te renverrai vers la doc HTML Hibernate qui contient toutes les infos les concernant, je vais t'introduire ici même à la problématique :

    Il faut, dans un premier temps que tu définisses une stratégie de connexion à ta base de données. Tu as deux solutions majeures :
    1/ Une session longue. Tu gardes ta session ouverte durant tout le temps de manipulation des objets et ne la ferme qu'une fois tout terminé.
    2/ Des sessions courtes.Tu instancies une session à chaque opération en base de données. Cette stratégie implique dirons nous une plus grande complexité dans la gestion des objets, et notamment face au lazy loading; car, pour conserver un objet entre deux sessions, il faut utiliser le détachement des objets, mais le chargement tardif ne fonctionne pas sur les objets détachés ...

    En plus de cette stratégie, il existe aussi des stratégies d'accès concurrentiel :
    1/ Optimiste avec champ date ou version. Obligatoire pour l'utilisation des sessions courtes puisque les verrous basiques de DB ne durent que lors de sessions/transactions il me semble, à moins que tu ne gères toi-même tes verrous.
    2/ Pessimiste. Ici, tu poses un verrou à chaque utilisation d'un objet dans le cadre de transactions atomiques.

    Il faut enfin que tu redéfinisses la stratégie de chargement des objets. Pour cela, tu as :
    1/ Lazy loading. Chargement tardif des objets associés à ton objet manipulé. Connait des problèmes si tu utilises des sessions courtes ainsi que le problème du N+1 Select (1 requete pour acceder à l'objet et ses identifiants, N pour charger chaque objet de la liste d'objets associés).
    2/ Batch-size. On charge une partie des objets par lots, le reste étant réalisés en chargement tardif (moins couteux que le lazy loading).
    3/ Fetch group (ou nom similaire). Chargement d'une association complete.
    4/ Aucun. Tout se charge en une requete, mais si le graphe est trop complexe tu risques fortement de charger directement toute la base, et la tu te confrontes à des problèmes de performance.

    Une fois tout cela défini, tu peux réaliser ton programme en fonction de ces critères et mapper correctement tes objets avec la base.

    D'un point de vue "architecture", je considérerai l'API Hibernate comme un service et te permettant de gérer la persistance de tes objets. Autrement dit, dans chacune de tes classes métiers, tu ne toucheras jamais au code Hibernate, et lors de tes traitements, tu feras appel à ce service de persistance pour qu'il s'occupe lui même de tout le code Hibernate.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    //exemple, pour sauvegarder, tu ne fais pas : 
    monobjet.save();
    //mais plutot
    manager.save(monobjet);
    // où manager est de type IManager (une interface) 
    //et instancié comme un HibernateManager (qui implémente l'interface IManager)
    La gestion des sessions se fera de deux manieres différentes :
    sessions courtes : ton manager peut traiter unitairement les sessions à l'interieur de chacune de ses fonctions.
    sessions longues : tu dois alors peut etre externaliser cette gestion via un objet Session que tu créeras. Puis lors de l'instanciation du manager, tu lui passe la session en paramètres, etc...

    Enfin voila, je pense que chaque strategie possède sa propre architecture et il n'existe pas une seule et unique solution, voici donc juste une proposition . (Perso, c'est aussi celle que j'utilise)

    Pour les choix strategiques, c'est à toi de regarder du coté des besoins de ton application, des traitements que tu réalises, etc. pour bien déterminer la mieux adaptée à ce que tu recherches.

  5. #5
    Membre expérimenté
    Inscrit en
    Mai 2005
    Messages
    217
    Détails du profil
    Informations personnelles :
    Âge : 43

    Informations forums :
    Inscription : Mai 2005
    Messages : 217
    Par défaut
    Perso, pour une webApp, j'ai opté pour le lazyloading et donc les sessions courtes ...

    Pour le reste, il faut veiller egalement à la struture de la base car meme si la normalisation est une bonne chose en terme de redondance et pour assurer un SI coherent, elle generer neanmoins une multiplication des requetes ... donc il faut trouver le bon compromis.

    Sinon comme l'a dit bizu, l'utilisation d'un manager (avec interface) est sans aucun doute, la solution architecturale la plus propre car elle est la moins intrusive dans le code metier (respect de la separation couche persistance <-> couche metier)

  6. #6
    Membre émérite Avatar de BizuR
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    688
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 688
    Par défaut
    Citation Envoyé par _beber85
    Perso, pour une webApp, j'ai opté pour le lazyloading et donc les sessions courtes ...
    La lazy loading est dangereux lorsque l'on opte pour les sessions courtes.

    Menfin, cela mis à part, la solution est tout de même viable mais comment gère tu justement cet accès aux objets chargés tardivement ? Tu crées une session à chaque traitement du style (enfin requete HTTP si l'on en croit le second document justement) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    manager.startSession();
    manager.attach(monobjet);
    //traitements
    manager.detach(monobjet);
    manager.closeSession();
    ?

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    8
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2007
    Messages : 8
    Par défaut fonctionnement du merge
    Bonjour
    suite à quelques problèmes j'ai pu comprendre (en partie) le fct du merge
    il est utile lorsqu'on gère les version d'un objet
    il faut noter que la fonction merge retourne l'objet mergé avec celui de la session (celui passé en parametre n'est pas modifié)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    MyClass monObjet2 = session.get(MyClass, monObject.getId())
    if(monObjet2 != null)//l'objet est persistent dans la session
    {
      //le merge ne generera pas d'HibernateException
      try
      {
         monObjet2 = session.merge(monObjet)
      }
      catch (StaleObjectStateException ex)
      {
        //monObjet est dans une version antérieure à celle contenue 
        //dans la   version
        //à vous de régair en fonction
        //car monObjet2.getVersion() > monObjet.getVersion()
      }
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Session] Organisation du code et les Sessions
    Par sir_gcc dans le forum Autres composants
    Réponses: 3
    Dernier message: 03/01/2007, 21h49
  2. Organisation du code source
    Par _kal_ dans le forum C
    Réponses: 18
    Dernier message: 04/08/2006, 14h15
  3. organisation du code.
    Par poporiding dans le forum C++
    Réponses: 36
    Dernier message: 13/07/2006, 10h15
  4. organisation du code.
    Par poporiding dans le forum C++
    Réponses: 3
    Dernier message: 28/06/2006, 17h10
  5. Réponses: 4
    Dernier message: 19/09/2005, 17h56

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