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 :

[DAO] Faire le lien entre les VO et les Objets Métiers


Sujet :

Hibernate Java

  1. #1
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut [DAO] Faire le lien entre les VO et les Objets Métiers
    salut
    j'utilise hibernate3 pour ma couche dao qui manipule des BO (objets métiers)
    Eg: Client create(Client c)

    par contre, ma couche de services manipule des VO (composés de qq champs de plusieurs BO) ce qui evite de remonter tous les champs inutiles des BO à la couche presentation
    Eg: ProduitCommandeClientVO listProdCmde(int nCmde)


    Mon probleme, c'est que dans ma couche de service, je suis obligé de créer des BO avec juste leur identifiant pour les passer en parametres aux methodes de dao
    et par exemple s'il existe deja ce BO avec l'id en question dans la session, ca provoque une erreur, c normal

    Je ne veux pas non plus faire une requete en bd pour remonter les info d'1 BO alors que je n'ai besoin que de son id et que je le possede (au pire , ca provoque une exception)


    Mes questions:
    - comment sont vos signatures de methode en DAO et en Services (avec BO&VO...) ?
    - est ce que je fais une erreur de conception?
    - est ce que la reponse ne se trouve pas dans les methodes attach/detach/merge? Dans ce cas est ce que je me complique trop la vie au niveau des VO?

    merci d'avance

  2. #2
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    J'ai tendance à toujours utiliser des objets métiers. Ensuite, selon les besoins particuliers, je peux être amené à utiliser un assembleur d'objets valeur dans des cas particuliers (présentation, distribution).

    Citation Envoyé par mauvais_karma
    - est ce que je fais une erreur de conception?
    Je ne peux pas répondre.

    Citation Envoyé par mauvais_karma
    est ce que je me complique trop la vie au niveau des VO?
    J'ai aussi cette impression.

  3. #3
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut
    en fait le pb c que dans le cas d'une appli web, j'effectue des opérations dasn différentes session (1 session Hibernate/ requete );

    Un exemple tres simple:

    Projet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    public class Projet {
    Long id;
    Set<Collaborateur> lstCol;
    ...
     
    public void assigneCollaborateurToProjet(Collaborateur c ){
               getLstCol().add(c);
               c.addProjetAssigne(this);
            }
    ...

    ProjetDAO:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    ...
    // CRUD methode
    ...
     
    public void assignerCollaborateur(Projet p, Collaborateur c){
            p.assigneCollaborateurToProjet(c); // le BO agit sur sa collection ET sur la ref du collaborateur...
            //update(p);
        }
    Service Metier pour Projet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     public void assignerCollaborateurToProjet(int idProjet, int idCollaborateur);

    Dans le cas ou dans une premiere session, j'ai créé un Projet, puis retourné ce projet à ma vue (sous forme de BO ou VO, peu importe)

    1) Si dans une 2eme session, je veux utiliser assignerCollaborateurToProjet, ma methode n'a aucun effet car l'objet agit sur sa collection mais cela n'est pas détecté par hibenate

    2) si c dans la meme session et que je fourni le meme objet Projet, ca marche (et pas besoin de faire de update)

    3) si je suis dans la meme session et que je fourni un new Projet(id) avec le bon id, ca passe pas car il existe deja un projet avec le meme id

    4) suivant le cas ou je suis, le fait de faire un update sur le Projet me levera un erreur ou écrasera certains champs.


    je m'embrouille mais je ne peux pas faire de methode pour chaque situation (une avec update ou non!)
    je vais etre obligé de faire du hql avec les id, alors que justement l'interet des orm c'est de manipuler directement des BO

  4. #4
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    Je comprends pas trop ce que tu dis mais je remarque une "erreur" de conception, ton DAO ne doit pas contenir d'appel métier.

    Projet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Projet {
    Long id;
    Set<Collaborateur> lstCol;
    ...
     
    public void assigneCollaborateurToProjet(Collaborateur c ){
        getLstCol().add(c);
        c.addProjetAssigne(this);
    }
    ...
    ProjetDAO :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public void update(Projet p) {
    ...
    }
    Service :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
     public void assignerCollaborateurToProjet(int idProjet, int idCollaborateur) {
        Projet p = projetDAO.findById(idProjet);//p associé à ta session hibernate si méthode dans une transaction
        Collaborateur c = collaborateurDAO.findById(idCollaborateur);//c également associé à hibernate
        p.assigneCollaborateurToProjet(c);
        //update pas nécessaire avec hibernate
    }

  5. #5
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut
    je remarque une "erreur" de conception, ton DAO ne doit pas contenir d'appel métier.
    > oui c vrai

    si je comprend bien, avec cette solution, je dois obligatoirement charger les BO
    avec de la chance (meme session) j'utilise le cache de session
    sinon je suis obligé de passer par une requete en base

    c un peu comme pour le dao.delete ou je dois d'abord charger l'objet avant de le supprimer?!

    c le seul compromis possible alors?
    c pas desastreux en terme de requetes générées par rapport à une solution en HQL/SQL pure?


    merci de ton aide

  6. #6
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut
    bon, le pb vient maintenant du fait que si je manipule mes bo dans les services métiers, je m'expose à un pb d'initialisation tardive si la session est fermée (par ex. si je touche à une collection). Alors que le pb n'esiste pas si je manipule mes bo dans la dao...

    qu'en pensez vous?


    et du coup on est obligé de travailler avec le parrter OpenSessionInView, et l'indépendance des couche n'est pls que relative

    Et puis d'un autre coté, je ne peux pas faire que detacher/reatacher des objets dans chaque methode CRUD...

  7. #7
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    Hmmm, prenons les choses dans l'ordre :

    Citation Envoyé par mauvais_karma
    si je manipule mes bo dans les services métiers, je m'expose à un pb d'initialisation tardive si la session est fermée (par ex. si je touche à une collection). Alors que le pb n'esiste pas si je manipule mes bo dans la dao...
    Comment gères tu les transactions et les sessions hibernate ? Tu n'as pas déclaré les méthodes de ton service comme transactionnelles ? As tu un exemple concret (ou simplifié) à montrer ? Ne peux tu pas demander le chargement explicite de tes collections lors du chargement de tes objets métiers ?

    Citation Envoyé par mauvais_karma
    et du coup on est obligé de travailler avec le parrter OpenSessionInView, et l'indépendance des couche n'est pls que relative
    C'est également une approche que je n'aime pas trop.

  8. #8
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut
    - a propos des sessions hibernate, j'ai tendance a utiliser le listener http de spring, et je flush() à la fin de chaque methode de dao, et effectivement, il n'y a plus de pb de chargement tardif

    mais en phase de dvlp/test, je prefere ouvrir/fermer moi meme les sessions, ce qui permet de voir les effets de bord (lazy loading en particulier)

    De plus, si les services ne sont plus exposés dans un contexte d'appli web (web services ou requete http), mais via RMI ou EJB par exemple, il faut prévoir une autre implémentation des services/dao qui gère lui meme la session


    - pour les transactions, je laisse spring les gérer au niveau service


    je mets un exemple concret en rentrant ce soir

  9. #9
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    Citation Envoyé par mauvais_karma
    De plus, si les services ne sont plus exposés dans un contexte d'appli web (web services ou requete http), mais via RMI ou EJB par exemple, il faut prévoir une autre implémentation des services/dao qui gère lui meme la session
    1- Tu peux définir une interface pour la distribution (coarse-grain) et une implémentation qui va faire appel au proxy transactionnel sur ton service et transformer le résultat sous forme de DTO.
    2- Tu dois pouvoir distribuer le proxy transactionnel si la granularité de l'interface le permet.

    J'attends de voir ton code pour en discuter plus longuement.

  10. #10
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut
    DAO de Base qui utilise les HibernateTemplate pour gérer les session et les transactions: toutes les DAO hérite de cette classe pour bénéficier d'opérations élémentaires... Chaque methode CRUD (find*, delete, update, create) sont déclarées transactionnelle, et l(utilisation des HibernateTemplate permet de s'inscrire dans la tx courante ou d'en creer une nouvelle

    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
    34
    35
    36
     
    public abstract class BaseDAOHibTemplate<T extends BaseBO, ID extends Serializable> 
                            extends HibernateDaoSupport implements BaseDAO<T, ID>{
     
        private Class<T> type;
     
        /** Creates a new instance of BaseDAOHib */
        public BaseDAOHibTemplate(Class<T> type) {
            this.type = type;
        }
     
     
     
        /*
         *  Operations C.R.U.D.
         */
     
        public T findById(ID id){
            return (T) getHibernateTemplate().get(type, id);
        }
     
     
        public T create(T bo){
            getHibernateTemplate().save(bo);
            return bo;
        }
     
        public void delete(ID id){
            T bo = findById(id);
            getHibernateTemplate().delete(bo);
        }
     
        public void update(T bo){
            getHibernateTemplate().saveOrUpdate(bo);
        }
    }

    Un service métier, dont les méthodes sont déclarées transactionnelles également:



    public class GestionProjetImpl implements GestionProjet {

    private static final Logger log = Logger.getLogger(GestionProjetImpl.class);

    private ProjetDAO projetDAO;
    private LotDAO lotDAO;
    private TacheDAO tacheDAO;
    private CollaborateurDAO collaborateurDAO;

    /** Creates a new instance of GestionProjetImpl */
    public GestionProjetImpl() {}



    public Projet creerProjet( String intitule,
    Calendar dateCreation, Calendar dateDebut, Calendar dateFin,
    Client client){
    Projet p = null;
    if (dateDebut.before(dateFin) ){
    p = new Projet(intitule, dateCreation, dateDebut, dateFin, client );
    projetDAO.create(p);
    }
    return p;
    }

    public void updateProjet(Projet p){
    projetDAO.update(p);
    }
    public void deleteProjet(int numProjet){
    projetDAO.delete(numProjet);
    }

    public Lot creerLot(String intitule, int idProjet,
    Calendar dateDebut, Calendar dateFin ){

    Projet projet = projetDAO.findById(idProjet);
    if (projet != null ){
    try{
    Lot lot = new Lot(intitule, projet, dateDebut, dateFin );
    lot.setDateCreation( new GregorianCalendar());
    projet.addTolots(lot); // a remonter dans lotDAO.create() ?
    return lotDAO.create(lot);
    }
    catch(Exception e){} // Le constructeur du BO retourne une checked exception
    }
    else{} // Le projet auquel est rattache le lot n'existe pas
    return null;
    }

    public void updateLot(Lot l){
    lotDAO.update(l);
    }

    public void deleteLot(int numLot){
    lotDAO.delete(numLot);
    }


    ...

    public void assignerCollaborateurToProjet( int idProjet, int idCollaborateur){
    Projet p = projetDAO.findById(idProjet);
    Collaborateur c = collaborateurDAO.findById(idCollaborateur);
    if (p != null && c != null ){
    p.assigneCollaborateurToProjet(c);
    projetDAO.update(p); // facultatif si session ouverte, obligatoire si session fermée, donc on le met pour l'instant!
    }
    }
    Deja, j'ai enrichi au maximum mes objets du domaine, c'est pour cela entre autre que les constructeurs des BO peuvent lever des exceptions. Pour l'instant elles sont de type checked.

    Pour moi la dao doit avoir une granularité faible, juste des methodes pour manipuler l'etat d'objets + des methodes d'extraction de données.

    Alors que dire de la granularité des services? Beaucoup plus fine. J'essaie de faire le plus possible de methodes qui prennent en parametre non pas des BO mais des valeurs qui serviront à construire les BO. Bonne idée, je commence à en douter...


    Donc avec ce bout de code, 2 cas se présente:
    - appli Web (WS, servlet, jsp, ...)
    Dans ce cas, ouverture de la session pour la durée de la requete, c'est bon, ca passe.
    Cela dit, je remarque qu'en générale, on appel qu'une seule méthode de service par requete, donc ca revient en fait à ouvrir la session hibernate au niveau des services métiers, meme si c transparent... alors qu'on voudrai garder une certaine indépendance entre les couches!

    - appli sous forme de composants/services distribués, style SSLB:
    On doit ouvrir/fermer la session dans chaque methode de dao
    et en ce qui concerne les methodes des services, elle ne doivent pas manipuler de BO ayant été instrumentalisé par hibernate, sous peine de lever les fameuse lazy initialisation error...


    Alors en réfléchissant, je me demande si la solution pour le cas ejb ne serait pas de changer la dao par une version qui detache/reattache les objets....
    ... et du coup les services restent inchangés



    bon c'etait long à lire, merci d'être arrivé jusqu'ici!
    j'attend surtout des remarques sur mon raisonnement et des retours sur vos expériences

    merci d'avance

  11. #11
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    Citation Envoyé par mauvais_karma
    Pour moi la dao doit avoir une granularité faible, juste des methodes pour manipuler l'etat d'objets + des methodes d'extraction de données.
    Ok

    Citation Envoyé par mauvais_karma
    Alors que dire de la granularité des services? Beaucoup plus fine.
    Pourquoi beaucoup plus fine ? Au contraire, le service sert souvent comme Facade (pattern du GoF) qui a pour but de coordonner un certain nombre d'actions (appel de la couche de persistance, appel de la logique des objets métiers).

    Citation Envoyé par mauvais_karma
    J'essaie de faire le plus possible de methodes qui prennent en parametre non pas des BO mais des valeurs qui serviront à construire les BO. Bonne idée, je commence à en douter...
    Je ne vois pas en quoi c'est une mauvaise idée...enfin bon, une autre solution : faire un objet requete (une sorte de pattern Command) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class GestionProjetImpl implements GestionProjet {
     
    public Projet creerProjet(CreationProjetRequest request){
    Projet p = null;
    if (request.getDateDebut.before(request.getDateFin()) ){
    p = new Projet(request.getIntitule(), request.getDateCreation(), request.getDateDebut(), request.getDateFin(), request.getClient());
    projetDAO.create(p);
    return p;
    } 
     
    }
    Tu vas me dire : "mais tu utilises un VO" et je te répondrais oui mais dans un sens seulement (entrant dans mes services) et pas dans un but de distribution de mes objets.


    Citation Envoyé par mauvais_karma
    Donc avec ce bout de code, 2 cas se présente:
    - appli Web (WS, servlet, jsp, ...)
    Dans ce cas, ouverture de la session pour la durée de la requete, c'est bon, ca passe.
    Cela dit, je remarque qu'en générale, on appel qu'une seule méthode de service par requete, donc ca revient en fait à ouvrir la session hibernate au niveau des services métiers, meme si c transparent... alors qu'on voudrai garder une certaine indépendance entre les couches!
    Hmmm, tu n'es vraiment pas clair sur ce coup là. Tu recois une requete http qui est traitée par un controleur (action Struts pour toi). L'implémentation de ton controleur fait appel à une méthode d'un service de ton domaine. Cet appel de méthode entraine l'ouverture et la fermeture d'une session avec un commit/rollback selon les règles que tu auras choisis. Cette méthode lance un traitement métier et renvoit des objets métiers que tu utilises pour former ton model, soit directement avec tes objets métiers, soit en formant un DTO s'il y a une grosse différence entre la présentation et le modèle métier.
    -> Où est le problème d'indépendance des couches ?

    Citation Envoyé par mauvais_karma
    - appli sous forme de composants/services distribués, style SSLB:
    On doit ouvrir/fermer la session dans chaque methode de dao
    et en ce qui concerne les methodes des services, elle ne doivent pas manipuler de BO ayant été instrumentalisé par hibernate, sous peine de lever les fameuse lazy initialisation error...
    Non, non et non !!! Ce n'est pas au DAO de gérer les transactions dans ce cas là. C'est toujours l'appel de méthode sur l'interface du service qui entraine l'ouverture/fermeture de la session. C'est vrai avec Spring et avec les EJB qui définissent des paramètres transactionnels sur l'interface d'EJB.

    Citation Envoyé par mauvais_karma
    Alors en réfléchissant, je me demande si la solution pour le cas ejb ne serait pas de changer la dao par une version qui detache/reattache les objets....
    ... et du coup les services restent inchangés
    Trop lourd à mon avis (ca n'engage que moi)

    Citation Envoyé par mauvais_karma
    j'attend surtout des remarques sur mon raisonnement et des retours sur vos expériences
    Tu as au moins le mérite de te poser de bonnes questions, il te faut maintenant savoir où trouver les bonnes réponses. Je crois t'avoir déjà donné mon opinion sur certains livres riches en info.

  12. #12
    Membre à l'essai
    Inscrit en
    Août 2004
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 35
    Points : 20
    Points
    20
    Par défaut
    Citation Envoyé par dlemoing
    Tu as au moins le mérite de te poser de bonnes questions, il te faut maintenant savoir où trouver les bonnes réponses. Je crois t'avoir déjà donné mon opinion sur certains livres riches en info.
    Je suis interressé par les livres que tu mentionnes, peux tu en dire plus ?
    Merci

  13. #13
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    * Patterns of Enterprise Application Architecture de Martin Fowler : lien amazon

    * Expert One-On-One J2Ee Design and Development de Rod Johnson : lien amazon

    * Expert One-on-One J2EE Development without EJB de Rod Johnson et Juergen Hoeller : lien amazon

    * Professional Java Development With The Spring Framework de Rod Johnson : lien amazon

    Si tu ne comptes pas utiliser Spring, le dernier n'est pas indispensable. Je conseille vraiment les 2 premiers. Je n'ai pas lu le 3eme, je l'ai juste parcouru mais il semblait lui aussi très intéressant.
    Il ne faut pas être faché avec l'anglais pour lire ces livres mais il ne faut pas s'inquieter, ils sont très clair et très facile à comprendre.
    Bien sur, il faut avoir également chez soi le livre du GoF, Design patterns: Elements of reusable object-oriented software.

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

Discussions similaires

  1. [Framework] faire le lien entre le DAO et la jsp
    Par etudiantinformatik dans le forum Spring
    Réponses: 1
    Dernier message: 21/03/2011, 18h11
  2. Réponses: 1
    Dernier message: 26/08/2010, 13h17
  3. Réponses: 3
    Dernier message: 02/07/2010, 15h50
  4. Réponses: 2
    Dernier message: 10/04/2010, 23h01

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