J'ai tendance à utiliser Hibernate en combinaison avec le framework Spring et ses capacités de démarcation de transaction déclarative (par exemple, @Transactional ).
Comme nous le savons tous, la mise en veille prolongée essaie d'être aussi non invasive et transparente que possible, mais cela s'avère un peu plus difficile lors de l'utilisation de lazy-loaded
relations.
Je vois un certain nombre d'alternatives de conception avec différents niveaux de transparence.
- Faites des relations non paresseuses (par exemple,
fetchType=FetchType.EAGER)
- Cela vioalite toute l'idée du chargement paresseux.
- Initialiser les collections en utilisant
Hibernate.initialize(proxyObj);
- Cela implique un couplage relativement élevé au DAO
- Bien que nous puissions définir une interface avec
initialize
, les autres implémentations ne sont pas garanties de fournir un équivalent.
- Ajouter un comportement de transaction aux
Model
objets persistants eux-mêmes (à l'aide d' un proxy dynamique ou@Transactional
)- Je n'ai pas essayé l'approche proxy dynamique, même si je n'ai jamais semblé faire fonctionner @Transactional sur les objets persistants eux-mêmes. Probablement à cause de cette mise en veille prolongée est l'opération sur un proxy avec lequel bein.
- Perte de contrôle lorsque des transactions ont effectivement lieu
- Fournir à la fois une API paresseuse / non paresseuse, par exemple,
loadData()
etloadDataWithDeps()
- Force l'application à savoir quand utiliser quelle routine, encore une fois couplage serré
- Débordement de méthode,,
loadDataWithA()
....,loadDataWithX()
- Forcer la recherche de dépendances, par exemple en fournissant uniquement des
byId()
opérations- Nécessite beaucoup de routines non orientées objet, par exemple
findZzzById(zid)
, et ensuitegetYyyIds(zid)
au lieu dez.getY()
- Il peut être utile de récupérer chaque objet d'une collection un par un s'il y a une surcharge de traitement importante entre les transactions.
- Nécessite beaucoup de routines non orientées objet, par exemple
- Faire partie de l' application @Transactional au lieu de seulement le DAO
- Considérations possibles sur les transactions imbriquées
- Nécessite des routines adaptées à la gestion des transactions (par exemple, suffisamment petites)
- Petit impact programmatique, bien qu'il puisse entraîner de grosses transactions
- Fournir au DAO des profils de récupération dynamiques , par exemple
loadData(id, fetchProfile);
- Les applications doivent savoir quel profil utiliser quand
- Type de transactions AoP, par exemple, intercepter des opérations et effectuer des transactions si nécessaire
- Nécessite une manipulation de code d'octet ou l'utilisation d'un proxy
- Perte de contrôle lors des transactions
- Magie noire, comme toujours :)
Ai-je manqué une option?
Quelle est votre approche préférée lorsque vous essayez de minimiser l'impact des lazy-loaded
relations dans la conception de votre application?
(Oh, et désolé pour WoT )
java
hibernate
spring
lazy-loading
application-design
Johan Sjöberg
la source
la source
Réponses:
Je dirais que l'hypothèse initiale est fausse. La persistance transaparente est un mythe, car l'application doit toujours prendre soin du cycle de vie de l'entité et de la taille du graphe d'objets en cours de chargement.
Notez qu'Hibernate ne peut pas lire les pensées, donc si vous savez que vous avez besoin d'un ensemble particulier de dépendances pour une opération particulière, vous devez exprimer vos intentions d'Hibernate d'une manière ou d'une autre.
De ce point de vue, les solutions qui expriment explicitement ces intentions (à savoir, 2, 4 et 7) semblent raisonnables et ne souffrent pas du manque de transparence.
la source
Je ne sais pas à quel problème (causé par la paresse) vous faites allusion, mais pour moi, le plus gros problème est d'éviter de perdre le contexte de session dans mes propres caches d'application. Cas typique:
foo
est chargé et placé dans une carte;foo.getBar()
(quelque chose qui n'a jamais été appelé auparavant et qui est évalué paresseusement);Donc, pour résoudre ce problème, nous avons un certain nombre de règles:
OpenSessionInViewFilter
pour les applications Web);try/finally
) afin que les sous-classes n'aient pas à y penser;Ceci, comme vous pouvez le voir, est en effet loin d'être non invasif et transparent . Mais le coût est toujours supportable, à comparer avec le prix que je devrais payer pour un chargement impatient. Le problème avec ce dernier est que cela conduit parfois à l'effet papillon lors du chargement d'un seul objet référencé, sans parler d'une collection d'entités. La consommation de mémoire, l'utilisation du processeur et la latence pour ne citer que le moins sont également bien pires, donc je suppose que je peux vivre avec.
la source
transparency
est de forcer l'application à se soucier du chargement des objets paresseux. Si tout était récupéré avec empressement, l'application pourrait ne pas savoir si les objets sont conservés dans une base de données ou non, carFoo.getBar()
cela réussira toujours. >when passing objects between threads, pass IDs
, oui, cela correspondrait au n ° 5.Un modèle très courant consiste à utiliser OpenEntityManagerInViewFilter si vous créez une application Web.
Si vous créez un service, j'ouvrirais le TX sur la méthode publique du service, plutôt que sur les DAO, car très souvent une méthode nécessite d'obtenir ou de mettre à jour plusieurs entités.
Cela résoudra toute "exception Lazy Load". Si vous avez besoin de quelque chose de plus avancé pour le réglage des performances, je pense que la recherche de profils est la voie à suivre.
la source
OSIV
est toujours un anti-modèle et conduit à des problèmes très graves tels que l'incapacité de gérer gracieusement les exceptions ou la dégradation des performances. Pour résumer: IMHO OSIV est une solution facile à vivre, mais bonne uniquement pour un jouet-projets.