Dozer à la rescousse du lazy-loading

 Article modifié dernièrement le 21 Juil 2011 @ 3 h 14 min

Pour ceux qui ne connaissent pas mon chouchou Dozer, voici deux articles pour vous donner envie d’y goûter, ici et .

Cette fois-ci Dozer vient en aide au problème récurrent du lazy-loading (chargement à la demande), dans mon cas les problèmes se sont posés avec Hibernate qui a adopté cette stratégie par défaut depuis la version 3.

Si vous non plus vous n’êtes pas convaincu par l’utilisation du design pattern nommé Open Session in View, et que vos services métiers renvoient systématiquement des objets de type DTO par exemple, alors la suite de ce billet peut vous aider dans vos développements.

Model vers DTO

Si vos services métiers retournent d’autres objets que ceux provenant du modèle de données mappés avec Hibernate, alors la copie automatique de l’objet Model vers DTO peut se faire avec Dozer. Cependant la copie de chaque attribut de l’objet Model vers l’objet DTO utilise systématiquement les getters de celui-ci, annulant du même coup l’effet du lazy-loading car l’appel d’un getter d’un objet Model sur un attribut configuré en chargement à la demande, déclenche automatiquement une requête en base pour récupérer les informations qu’il ne possède pas encore.

Pour cela j’utilise deux mapping Dozer différents par objet Model qui utilise des associations lazy-loadées (il est bien entendu possible d’en définir plus), et dans mes services métiers j’indique quel mapping utiliser.

Mapping Dozer

Les deux mapping en question ressemblent à ceci, tout est paramétré dans le fichier de configuration XML de Dozer en utilisant l’attribut map-id de la balise mapping, ainsi que la balise field-exlucde pour jouer sur les attributs que nous ne souhaitons pas charger :

  • le mapping qui ne charge pas les associations en lazy-loading, appelé miniDeck
  • le mapping qui charge entièrement l’objet et donc les associations en lazy-loading, appelé maxiDeck

Impact

L’impact au niveau de vos services métiers est minime, il suffit juste de récupérer la « moulinette Dozer » que vous avez défini préalablement via Spring ou autre (je vous renvois pour cela au premier lien en haut du billet), et ensuite il faut appeler la méthode public void map(Object source, Object destination, String mapId) throws MappingException qui va vous permettre de transformer vos objets Model en DTO avec le mapping de votre choix.

Avec ce mécanisme mis en place, vous savez toujours de quoi sont composés les objets DTO qui sont remontés à vos clients et vous ne vous faites plus avoir avec des données non chargées au moment où vous en avez besoin, ce qui évitera par ailleurs toute Lazy-Loading Exception du style : « org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: fr.mtg.supremacy.core.model.Deck.contents, no session or session was closed ».

Trois flèches vers le bas

1- Logiciel de brouillage d’adresse IP :

Contourner la censure en surfant anonyme

2- L’article explicatif :

La différence entre un proxy et un VPN

3- Comment espionner un smartphone (app) :

L’application de référence

Commentez ici

  • Mimie 20 juillet 2011, 13 01

    Un jeune projet qui semble vouloir rivaliser avec Dozer : Orika, affaire à suivre.

  • aitBaamran 24 juillet 2012, 16 04

    c’est un bon tutoriale

  • Mathieu 14 août 2012, 23 11

    Coucou Mimie,

    j’ai une question au sujet de ce post. J’aimerai bien utiliser Dozer pour mapper mes objets entity vers des objets DTO, et je me posais justement la question par rapport au lazy loading.

    L’intérêt du lazy loading est donc de charger au moment opportun ce qui est utile, bien souvent les collections représentants les liens avec d’autres entités.
    Dans mon architecture, ces appels se font ou vont se faire dans la couche présentation soit directement dans la page Web (avec du DataBinding) soit dans le pattern ViewModel que j’utilise (une classe proche d’une classe POJO et qui fournit toutes les données via les services à la page Web).

    Bref dans ce fonctionnement là, j’ai du mal à voir comment le lazy loading va pouvoir opérer.
    Car au lieu de charger une première fois l’objet ou une liste d’objet qu’on ferait apparaître au niveau web dans une liste (listbox), et de faire un appel au getter adéquat pour charger les données manquantes que l’on souhaite ne faire apparaître qu’à la demande de l’utilisateur (dans une petite popup ajax par ex, un tooltip ou autre), nous serions obligés de loader l’objet DTO en « fullDeck ».
    Ca ressemblerait alors à du eager.

    Si au contraire je ne prends que le miniDeck, je vais devoir à un moment ou un autre refaire appel à mon service pour me fournir le fullDeck.

    J’avoue que je suis un peu perdu dans cette histoire.

    As tu un avis sur le sujet plus précis ou un exemple d’utilisation ?
    Peut être est ce aussi assez dépendant du fonctionnement du framework de présentation ?

    Merci.

  • Mimie 15 août 2012, 10 10

    Salut Mathieu, si j’ai pas compris tu as besoin de « mini » pour la construction de ta page puis de « maxi » lorsque l’utilisateur fera une action, dans ce cas tu as les choix suivants :

    – appeler ton service qui récupère « mini » au départ puis en ajax appeler ton service qui récupère « maxi » (2 méthodes différente de ton controleur : spring, struts, servlet, peu importe)
    – utiliser le pattern d’open session in view cité en haut du billet qui va te permettre d’utiliser le lazy-loading natif d’hibernate car la connexion entre ta couche de présentation et ta couche de données reste active et donc l’appel d’un « get » déclenchera la requête en base automatiquement à la demande

    Tu y vois plus clair ?

  • Mathieu 15 août 2012, 11 11

    Ben non justement :p

    Voici l’architecture en place rapidement représentée par qq classes, en partant de la couche présentation, imaginons un cas simple d’utilisateurs qui ont des droits sur des éléments listés en base :

    – J’ai donc une classe « UserViewModel », qui ressemble à une POJO et transmets à ma page les infos nécessaires pour l’affichage des données.
    – Cette classe fait appel à ma classe de service « UserService » (enfin l’interface :p). Imaginons que cette interface a une méthode « getAllUsers » qui retourne une List.
    – Cette interface de service a à son tour une interface de DAO « UserDao » qui va utiliser un entityManager JPA afin d’exécuter la recherche en base.
    – Et donc une classe entity « User » avec la mapping JPA.

    Le tout avec une implémentation Hibernate de JPA.

    J’ai donc ma liste d’utilisateur, je l’affiche et je récupère un certain nombre d’attributs via les getters (en faite c’est directe via du DataBinding) pour renseigner les colonnes de ma liste.

    Maintenant, je voudrais faire un clic sur une ligne précise pour afficher des informations complémentaires (les droits) de mon utilisateur dans une popup (ces droits sont stockés dans d’autres tables, y a des jointures… ce qui induit des Collections dans mon entity User).

    Bref charger dès le départ ces Collections serait très lourd. Mais il est possible de les charger à la demande au moment de faire appel au getter de celles-ci. Ca provoquera un chargement lazy loading hibernate. Dans mon cas, j’ai mis le PersistentContext en mode étendu dans la DAO pour pouvoir faire ces lazy loading. J’ai un mal fou à savoir si c’est un mode de fonctionnement normal. Certains semblent dire que ce n’est pas Thread Safe car l’entityManager serait un singleton à l’application.
    Mais ca ressemble fortement en terme d’utilisation à l’OpenSessionInViewFilter dont tu parle je pense….D’ailleurs, je ne connais pas la différence entre les 2 et surtout la mise en application du OpenSessionInViewFilter (j’ai vu qu’il fallait ajouter un filtre dans le web.xml qui pointerait sur une classe Spring mais bon est ce compatible avec mon framework de présentation, un framework qui gère les échanges ajax).

    Revenons à ces infos. Les charger ne suffit pas, il faudrait les mettre en forme également et c’est là que mon pb commence…
    Le fait de devoir les formater est contraignant, et je trouve cela anormal de devoir le faire dans mon UserViewModel. D’ailleurs, je dois même parfois aussi en fonction de certains valeurs, les transformer pour l’internationalisation de l’appli.

    Je vois donc 2 solutions :
    – Ajouter un attribut @Transient dans mon entity User qui serait le résultat de ce formatage. Mais je me dis que si il faut faire cela pour tout ca peut vite faire un paquet de méthodes et d’attributs qui ne sont pas représentatifs des tables de persistences.
    – Ou alors faire des objets DTO. Je pensais faire dans ma classe de service un mapping avec Dozer pour récupérer des « User » et retourner des « UserDto ». Et du coup faire la mise en forme dans cette DTO. Mais c’est là que je me dis que je perds le lazy loading car j’en reviens finalement à charger toutes les collections initialement….vu qu’appeler la DTO ne déclenchera pas le lazy loading via le getter de l’entity :p

    J’avoue être passablement paumé tout d’un coup.

    Est ce que tu vois mieux la problématique ? et les questions que je me pose sur les DTO ?
    D’ailleurs en faisant des recherches, je vois sur developpez que soit disant les objets que j’appelle Entity qui ont les annotations JPA seraient en faite déjà des DTO…..

  • Mathieu 15 août 2012, 11 11

    J’ai vu qq part une personne qui déclarait un objet DTO en étandant son objet Entity. Et du coup, il castait dans sa classe de service pour passer de l’un à l’autre.

    Peut être, serait ce la solution pour pouvoir appeler les getters qui déclenchent les lazy loading ?

Article suivant:

Article précédent:

Share This