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

JPA Java Discussion :

DAO pour JPA: les meilleures pratiques?


Sujet :

JPA Java

  1. #1
    Membre régulier
    DAO pour JPA: les meilleures pratiques?
    Bonjour
    Je suis à la recherche de “bonne pratique” pour implémenter des DAO avec JPA.
    (J’utilise Spring et Hibernate)
    Il y a bien des bonnes idées dans la doc de référence de spring, mais je trouve quand même l’API de JPA « pauvre »
    Je suis pour l’instant en test avec des DAO générique de google :
    http://code.google.com/p/generic-dao/
    L’API est vraiment mieux, c’est puissant, mais je trouve dommage que les entity doivent implémenter une interface spécifique.

    Quel sont vos bonne pratique ?

  2. #2
    Expert éminent
    Bonjour.
    Veux tu expliquer s'il te plaît ce que tu veux dire par API pauvre ? DAns son contexte (la persistance des données), je ne vois pas comment l'API de JPA est pauvre, excepté le manque de la recherche par Criteria, comme celle offerte par Hibernate par exemple, et ça, c'est prévue pour JPA 2.0.

    Sinon, de point de vue pratique, je n'utilise jamais les classes Templates ou Support de Spring: je préfère garder mes DAOs JPA indépendants de Spring qui se contente juste de leur injecter un EntityManager. Le reste (listing, ajout, màj et suppression), je le fais avec l'API nue de JPA.

    Autre chose: dans 99% des cas, mes DAOs JPA sont des coques vides: tout le boulot est réalisé par un DAO générique et abstrait concocté et développé par mes soins qui fait tout le sale boulot. En général, dans mes DAOs concret, soit je me contente d'hériter de ce DAO abstrait, soit j'y ajoute quelques recherches via des requêtes nommés et le tour est joué.
    Merci les génériques et l'API de reflection de Java

    Dans l'attente de plus d'explications, @+ !

  3. #3
    Membre actif
    Pareil pour moi: http://jroller.com/greenhorn/entry/uberdao_for_jpa
    Même si entre temps il s'est pas mal enrichi...
    Mais je suis toujours perplexe quant à l'utilité de DAO (que je préfère d'ailleur appeler Repository dans un contexte d'ORM). J'ai également une API qui vérifie si un persist ou merge est nécessaire. Mais le dernier retournant l'entitée managée cela ne facilite rien...
    De plus, comme j'utilise Hibernate, dès que j'ai besoin des "Filter"s je me retrouve à injecter le PersistenceContext dans mes Façades. Ou dès que je dois intéragir avec le cache (L1 ou L2), je l'injecte dans mes Services...
    Mais pour ce qui est "fetch plan" la couche prend tout son sens!
    Suis toujours mitigé
    Alex

  4. #4
    Expert éminent
    Bonjour.
    @Alexander: J'ai vu l'exemple dans ton blog et c'est exactement ça ! enfin presque, y'a bien sur quelques ptites differences mais le principe est là !
    Ah, jolie la vérification du nom de la table via reflection + annotation Entity ! Moi, j'utilisais bêtement le nom de la classe :-).

    Citation Envoyé par a.snaps Voir le message

    Mais je suis toujours perplexe quant à l'utilité de DAO (que je préfère d'ailleur appeler Repository dans un contexte d'ORM).
    Euh ... Comment veux tu communiquer avec ta DB autrement ?

    Citation Envoyé par a.snaps Voir le message

    J'ai également une API qui vérifie si un persist ou merge est nécessaire. Mais le dernier retournant l'entitée managée cela ne facilite rien...
    Ha ! Je connais trop bien ça ! J'en ai vu des vertes et des pas mures ! La solution est Spring + Extended Entity Manager.

    Citation Envoyé par a.snaps Voir le message

    De plus, comme j'utilise Hibernate, dès que j'ai besoin des "Filter"s je me retrouve à injecter le PersistenceContext dans mes Façades.
    Là je comprends plus rien ... J'ai moi même eu besoin de l'API Criteria d'Hibernate dans un contexte JPA et je l'ai implémenté dans le DAO (logique, non ? bon, mais môche quand même ...)


    Citation Envoyé par a.snaps Voir le message

    Ou dès que je dois intéragir avec le cache (L1 ou L2), je l'injecte dans mes Services...
    Tu as d'autres applications qui écrivent dans ta DB ?

    Citation Envoyé par a.snaps Voir le message

    Mais pour ce qui est "fetch plan" la couche prend tout son sens!
    huh ? tu parles du fetch mode (lazy/eager) ?


    Citation Envoyé par a.snaps Voir le message

    Suis toujours mitigé
    Ah oui !

    @+

  5. #5
    Membre actif
    Citation Envoyé par djo.mos Voir le message
    Bonjour.
    @Alexander: J'ai vu l'exemple dans ton blog et c'est exactement ça ! enfin presque, y'a bien sur quelques ptites differences mais le principe est là !
    Ah, jolie la vérification du nom de la table via reflection + annotation Entity ! Moi, j'utilisais bêtement le nom de la classe :-).


    Euh ... Comment veux tu communiquer avec ta DB autrement ?
    En utilisant l'entitymanager directement et des namedquery'es
    Citation Envoyé par djo.mos Voir le message


    Ha ! Je connais trop bien ça ! J'en ai vu des vertes et des pas mures ! La solution est Spring + Extended Entity Manager.
    Vais y jeter un oeil...
    Citation Envoyé par djo.mos Voir le message


    Là je comprends plus rien ... J'ai moi même eu besoin de l'API Criteria d'Hibernate dans un contexte JPA et je l'ai implémenté dans le DAO (logique, non ? bon, mais môche quand même ...)

    Je parle des filters: http://www.hibernate.org/hib_docs/re...l/filters.html
    Citation Envoyé par djo.mos Voir le message


    Tu as d'autres applications qui écrivent dans ta DB ?

    Oui, mais ce n'est pas pour ça. Plus pour la gestion de mémoire et autre lors de batch....
    Citation Envoyé par djo.mos Voir le message


    huh ? tu parles du fetch mode (lazy/eager) ?

    Oui, mais des méthodes genres:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    CommandeDAO.getCommandeParId(Long): Commande
    CommandeDAO.getCommandeEtLigneParId(Long): Commande


    Ou la seconde prends les lignes de commande associées en une seule query DB

    Citation Envoyé par djo.mos Voir le message


    Ah oui !

    @+

  6. #6
    Membre actif
    une petite discussion interessante a lire:

    http://www.fabiokung.com/2007/11/12/...-repositories/

    C'est sur le concepte de Repository/DAO. Je suis tombe dessus, quand je cherchais egalement des infos sur JPA/Dao/Repository.

  7. #7
    Membre régulier
    Grand débat...

    Peut etre qu'il faut admettre finallement que la couche DAO existe mais elle est générique (complètement): la classe proncipale de votre DAO s'appelle l'entityManager qui couvre tous vos besoins: lecture, sauvegarde, modificaiton (dirty checking), suppression, et recherche.

    L'usage de l'entity manager dans la couche service (ou métier si vous préférez), ne me gène pas ou en tout cas pas plus que l'usage d'une méthode ProductDAO.getProduct(id); d'autant plus qu'il s'agit bien d'une norme, contrairement à l'objet session d'hibernate qui fait exactement la meme chose mais induisait une dépendance vers un framework non normalisé.

    la question est donc de savoir s'il est préférable d'ecrire:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public List<Client> rechercheClientParVille(String ville) {
      return ClientDAO.listeClientParVille(ville);
    }


    ou

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
     
    public List<Client> rechercheClientParVille(String ville) {
      return entityManager.createQuery("From Client c where c.ville=:ville").setParamater("ville",ville).list()



    pour moi, la deuxième solution est vraiment envisageable d'autant qu'ejbql est un langage de requetage objet indépendant du mondèle de stockage

    Voila mon avis :-)
    Mais c'est un sujet polémique !
    Le DAO ne disparait pas, JPA vous en fournie un totalement générique ! pourquoi ne pas l'utiliser.



    Citation Envoyé par cisco Voir le message
    Bonjour
    Je suis à la recherche de “bonne pratique” pour implémenter des DAO avec JPA.
    (J’utilise Spring et Hibernate)
    Il y a bien des bonnes idées dans la doc de référence de spring, mais je trouve quand même l’API de JPA « pauvre »
    Je suis pour l’instant en test avec des DAO générique de google :
    http://code.google.com/p/generic-dao/
    L’API est vraiment mieux, c’est puissant, mais je trouve dommage que les entity doivent implémenter une interface spécifique.

    Quel sont vos bonne pratique ?

  8. #8
    Expert éminent
    Bonjour.
    En y allant à ta façon, c'est vrai su'on utilise JPA-QL qui est Objet et indépendant de la base de données. Toutefois, tu lies ta couche Services irrémediablement à un API de persistance spécifique (JPA) et tu ne pourras pas les réutiliser dans un autre projet avec JDBC/Spring JDBC/Toplink/Hibernate/JDO/etc. comme couche de persistance d'où la nécessité de l'abstraction offerte par la couche DAO.

  9. #9
    Membre régulier
    Citation Envoyé par djo.mos Voir le message
    Bonjour.
    tu ne pourras pas les réutiliser dans un autre projet avec JDBC/Spring JDBC/Toplink/Hibernate/JDO/etc. comme couche de persistance d'où la nécessité de l'abstraction offerte par la couche DAO.
    oui, c'est juste. Si vous avez besoin de porter vos applications en utilisant d'autres technos, alors le dao est nécessaire mais il va se résumer à Peau de chagrin... reste simplement pour vous à savoir si cela correspond à votre besoin... A force de mettre des couches des indirections etout etout dans tous les sens on perd complètement les développeurs :-) mais ce que dis djo.mos est très juste.

  10. #10
    Membre actif
    Sur mon projet actuel, on pourrait concretement supprimer cette couche DAO. Ca part du constat que dans notre cas, il n'y a pas de changement du support de persistence, la seule grande revolution de ce cote la, ca a ete une montee de version du sgbd.

  11. #11
    Membre régulier
    Sans remettre en cause le bien fondé du sempiternel découpage Presentation/Service/dao (avec les variantes qu'il comporte), il ne faut pas non plus tomber dans l'excès qui est souvent a l'origine de complexité resultant sur l'effet inverse souhaité: projet inmaintenable car architecture pas assez pragmatique (en partant du principe qu'il est livré dans les délais).

    Un argument supplementaire à ne pas systématiquement utiliser de DAO spécifique (et donc utiliser l'entity manager directement dans la couche service): Une utilisation simplifié des transaction au niveau de la couche de service (car c'est leur place), plutot que dans la couche de DAO: on peut alors presque mettre Spring à la poubelle (pour la gestion des transactions tout du moins).

    Il faut bien être conscient que peu de développeur Java ont suffisament de recul pour utiliser à bon escient struts ou jsf, ou meme jsp et servlet directement avec spring+jpa ou hibernate convanblement.... Les audits que j'ai eu l'occasion de faire sur ce type de projet étaient souvent catastrophique...

    D'ailleurs pour ceux qui font du spring + Jpa, je ne peux que vous inviter à regarder la spec ejb3 dans son intégralité: la démarcation des transactions est le job des ejb session et la version 3 de ces composants est au moins aussi simple (voire plus) que ce que propose spring pour répondre au même problème !

    mon rêve: après la "fusion" entre ejb entité et hibernate, une fusion "ejb session"/Spring




    Citation Envoyé par ndp Voir le message
    Sur mon projet actuel, on pourrait concretement supprimer cette couche DAO. Ca part du constat que dans notre cas, il n'y a pas de changement du support de persistence, la seule grande revolution de ce cote la, ca a ete une montee de version du sgbd.

  12. #12
    Rédacteur

    Citation Envoyé par djo.mos Voir le message
    Bonjour.
    En y allant à ta façon, c'est vrai su'on utilise JPA-QL qui est Objet et indépendant de la base de données. Toutefois, tu lies ta couche Services irrémediablement à un API de persistance spécifique (JPA) et tu ne pourras pas les réutiliser dans un autre projet avec JDBC/Spring JDBC/Toplink/Hibernate/JDO/etc. comme couche de persistance d'où la nécessité de l'abstraction offerte par la couche DAO.
    Un truc doit m'échapper ...

    Dans le cas où on utilise une couche de DAO, certes, on pourra réutiliser la couche Service. Mais pour utiliser une autre API de persistence (JDO, ...), il faudra disposer également d'une implémentation des DAO pour cette autre API, n'est ce pas ?

    Or, dans l'exemple proposé par Denis où l'on se passe des DAO, n'est t'il pas plus simple de constater qu'il suffit de faire une autre implémentation du service pour la nouvelle API ?

    1° CAS (avec DAO) :

    Extrait de "ServiceImpl extends Service":

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    public List<Client> rechercheClientParVille(String ville) {
      return ClientDAO.listeClientParVille(ville);
    }


    Le service est certes indépendant de l'implémentation de la persistance mais il faut bien une implémentation de ClientDAO pour JPA, JDO, ... ?
    Et ceci, pour chaque DAO !


    2° CAS (sans DAO) :

    Extrait de "JPAService implements Service" :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    public List<Client> rechercheClientParVille(String ville) {
      return entityManager.createQuery("From Client c where c.ville=:ville").setParamater("ville",ville).list()


    Il ne faut plus que fournir une autre implémentation du Service (JDOService) ?

    Et s'il y a du code commun à JDOService et JPAService, il suffit qu'il étendent un AbstractService ...

    Tout le monde savait que c'était impossible. Il est venu un imbécile qui ne le savait pas et qui l'a fait. Marcel PAGNOL
    On ne savait pas que c'était impossible, alors on l'a fait. John Fitzgerald KENNEDY.
    L'inexpérience est ce qui permet à la jeunesse d'accomplir ce que la vieillesse sait impossible. Paul (Tristant) BERNARD
    La meilleure façon de prédire l'avenir, c'est de l'inventer.

  13. #13
    Expert éminent
    Oki, je voies mieux maitenant, merci

    Pour répondre à ta question: c'est pas aussi simple que ça: la couche service n'est pas une simple abstraction de la couche DAO, mais des traitements métiers qui peuvent être assez complexes et longs.
    De plus, la couche DAO est beaucoup plus simple à réaliser que la couche Service.

    Une société peut par exemple développer leur propre gestionnaire de sécurité, et ils veulement le réutiliser pour chaque projet.

    Ils vont pas s'amuser à modifer le code de ce gestionnaire à chaque fois pour épulcher les références à JPA pour pouvoir utiliser la chose dans un projet à base de JDBC (java 1.4 par exemple oblige).

    etc.

    Pour ce qui est de la dépendance, c'est là qu'entre en jeu la DI avec Spring par exemple.
    Je développe mes services pour ne dépendre que de l'interface des DAOs, sans la moindre hard-reference vers une implémentation particulière. L'implémentation en question serait configuré à l'extérieur du prog (dans un XML par exemple) et injecté au runtime par le contenur (Spring).

    Il me suffit de changer le XML en question et je passe du mode développement/test utilisant des DAOs à base de Db4o par exemple pour passer à une config JPA/MySQL, etc.

  14. #14
    Rédacteur

    Je comprends ce que tu veux dire dans le cas d'un "service" qui fait plus qu'une simple façade.

    Ce qui m'a toujours géné dans les DAO (par entité), c'est où mettre le code de persistance qui opère sur plusieurs de ces entités ?

    Exemple avec JDBC :

    Tu fais des JDBC_DAO pour plusieurs entités : A, B, C.
    Mais si tu as une transaction qui travaille à la fois sur A, B et C, où va tu mettre le code de ta transaction JDBC ?
    Pas dans le service puisque il est censé être indépendant de la couche de persistance, n'est ce pas ?

    Tout le monde savait que c'était impossible. Il est venu un imbécile qui ne le savait pas et qui l'a fait. Marcel PAGNOL
    On ne savait pas que c'était impossible, alors on l'a fait. John Fitzgerald KENNEDY.
    L'inexpérience est ce qui permet à la jeunesse d'accomplir ce que la vieillesse sait impossible. Paul (Tristant) BERNARD
    La meilleure façon de prédire l'avenir, c'est de l'inventer.

  15. #15
    Expert éminent
    Si, c'est bien dans la couche Service que ça va.
    Et personne n'a dit que la couche Service est indépendante de la couche persistance: c'est rend tout simplement impossible le développement d'une application: d'où vont les services récupérer les objets sur lesquels ils travaillent ? et ou vont ils stocker leurs résultats ?

    Par contre, la couche service doit être indépendante de l'implémentation de la couche de persistance.

    Comme énoncé plus hat, les transactions doivent être implémentés au niveau de la couche Service et pas DAO.
    Ainsi, si tu opères sur plusieurs objets persistents dans la couche service, tu est sûr que ces traitements seront atomiques.

    N.B.: Si j'utilise beaucoup le mot "doit", c'est pour exprimer mon point de vu personnel, et non pas pour dicter des lois tombés du ciel

  16. #16
    Membre chevronné
    Je plussois sur la responsabilité transactionnelle de la couche service. La notion d'atomicité lors d'une opération métier est essentielle.

    En revanche, la notion de DAO me chagrine de lourdeur... Je rêve d'un truc à la groovy, qui a pris le parti de faire porter les fonctions d'accès directement sur les classes du domaine métier et ses instances. Je trouve ça remarquablement limpide. Certes c'est un coup facile à faire sur un langage dynamiquement typé, beaucoup moins sur un langage statique...

  17. #17
    Rédacteur

    Citation Envoyé par djo.mos Voir le message
    Si, c'est bien dans la couche Service que ça va.
    Et personne n'a dit que la couche Service est indépendante de la couche persistance: c'est rend tout simplement impossible le développement d'une application: d'où vont les services récupérer les objets sur lesquels ils travaillent ? et ou vont ils stocker leurs résultats ?

    Par contre, la couche service doit être indépendante de l'implémentation de la couche de persistance.

    Comme énoncé plus hat, les transactions doivent être implémentés au niveau de la couche Service et pas DAO.
    Ainsi, si tu opères sur plusieurs objets persistents dans la couche service, tu est sûr que ces traitements seront atomiques.
    Excuse moi si j'ai fait un raccourci de langage en disant "indépendant de la couche de persitance", je pensai bien entendu "indépendante de l'implémentation de la couche de persistance" car comme tu le dis : comment ferais t'on sinon.

    Je suis également d'accord pour gérer les transactions aux niveau de la couche de service pour les traitements atomiques.

    Et malgré cela, je reviens sur mon interrogation initiale avec mon exemple en JDBC ? Si mes souvenirs sont bons, faire une transaction en JDBC revient à faire du code propre à JDBC, donc à avoir une implémentation de la persistance au niveau de la couche service, ce que justement on dit vouloir éviter !

    La seule échappatoire possible que je vois est de faire une interface de service et des implémentations différentes comme évoquées par mes messages précédents. Je retombe alors à m'interroger sur l'utilité des DAO une fois de plus ...

    Je te rassure, j'ai bien compris tes arguments pour les DAO.

    J'essaye juste de clarifier mes pensées :

    1/ Si on fait un service et des DAO (n implémentations / couche persistance), ça à les avantages que tu cites mais ça ne résout pas mon problème exemple avec JDBC.

    2/ Si on fait des services (n implémentations / couche persistance) et pas de DAO, ça résou mon problème exemple avec JDBC mais cause les difficultés citées pour ton gestionnaire de sécurité.

    3/ Si on fait des services (n implémentations / couche persistance) et des DAO (n implémentations / couche persistance), ça résout les problèmes en 1 et 2 mais ça alourdit l'architecture.

    Où je me trompe dans mon raisonnement ?

    Tout le monde savait que c'était impossible. Il est venu un imbécile qui ne le savait pas et qui l'a fait. Marcel PAGNOL
    On ne savait pas que c'était impossible, alors on l'a fait. John Fitzgerald KENNEDY.
    L'inexpérience est ce qui permet à la jeunesse d'accomplir ce que la vieillesse sait impossible. Paul (Tristant) BERNARD
    La meilleure façon de prédire l'avenir, c'est de l'inventer.

  18. #18
    Expert éminent
    Oui, le point que tu cites est très pertinent

    Donc, la réponse (magique) à ça est (encore) ce (cher) Spring

    Rassures toi: tout les problèmes que tu cites ont été rencontrés plus tôt par d'autres programmeurs, et on y a remedidé au fil de temps.

    Donc, comme on utilise déjà la DI (depedancy Injection) pour ne pas lier la ouche service à l'implémentation de la couche persistance, il y'a aussi l'AOP (Aspect Oriented Programming) qui permet de résoudre le problème de tansactions que tu cites.

    Il s'agit ici d'externaliser la gestion des transaction, mais aussi de la rendre déclarative.

    En gros, tu indiques au conteneur IoC/DI (Spring) que telle méthode de telle classe est transactionnele.Après, tu peux aller plus loin en indiquant que deux méthodes doivent s'exécuter dans la meêm transaction, etc.

    Le conteneur s'occupe de l'ouverture d'une transaction, de son commit, et si quelque chose tourne mal, du rollback.
    Tout ça d'une manière déclarative sans la moindre ligne de code spécifique à un framework de persistance donné.

    Si tu veux un exemple pratique, tu peux regarder dans ce billet par exemple ou encore celui-ci (C'est pas exactement dans le cadre du sujet, mais ça s'en approche).

  19. #19
    Membre régulier
    Je nuance ta phrase: la réponse magique peut aussi être ejb session + JPA.
    l'injection de dépendance est supportée par les EJB3, et la démarcation déclarative des transaction aussi.. à cela tu peux rajouter le contrôle des habilitation....


    Citation Envoyé par djo.mos Voir le message
    Oui, le point que tu cites est très pertinent

    Donc, la réponse (magique) à ça est (encore) ce (cher) Spring


  20. #20
    Membre chevronné
    En outre il est possible par simple re-configuration de modifier le comportement de la transaction comme le type de propagation, l'isolation, le timeout...

    Donc beaucoup de souplesse.