web-dev-qa-db-fra.com

Hibernate lazy-load application design

J'ai tendance à utiliser Hibernate en combinaison avec Spring framework et ses capacités de démarcation déclarative des transactions (par exemple, @ Transactional ).

Comme nous le savons tous, hibernate essaie d'être aussi non invasif et aussi transparent que possible, mais cela prouve un un peu plus difficile lors de l'utilisation des relations lazy-loaded.


Je vois un certain nombre d'alternatives de conception avec différents niveaux de transparence.

  1. Établir des relations non chargées paresseusement (par exemple, fetchType=FetchType.EAGER)
    • Cela altère toute l'idée de chargement paresseux.
  2. Initialiser les collections à l'aide de Hibernate.initialize(proxyObj);
    • Cela implique un couplage relativement élevé avec le DAO
    • Bien que nous puissions définir une interface avec initialize, il n'est pas garanti que d'autres implémentations fournissent un équivalent.
  3. Ajoutez le comportement de transaction aux objets Model persistants eux-mêmes (en utilisant 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 en raison de cette mise en veille prolongée est une opération sur un proxy pour être avec.
    • Perte de contrôle lorsque des transactions ont réellement lieu
  4. Fournissez les deux API paresseux/non paresseux, par exemple, loadData() et loadDataWithDeps()
    • Force l'application à savoir quand utiliser quelle routine, à nouveau couplage étroit
    • Débordement de méthode, loadDataWithA(), ...., loadDataWithX()
  5. Forcer la recherche de dépendances, par exemple en ne fournissant que des opérations byId()
    • Nécessite beaucoup de routines non orientées objet, par exemple, findZzzById(zid), puis getYyyIds(zid) au lieu de z.getY()
    • Il peut être utile de récupérer chaque objet dans une collection un par un en cas de surcharge importante de traitement entre les transactions.
  6. Faites partie de l'application @Transactional au lieu de seulement 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 que cela puisse entraîner des transactions importantes
  7. Fournissez au DAO une dynamique extraction des profils , par exemple, loadData(id, fetchProfile);
    • Les applications doivent savoir quel profil utiliser quand
  8. Type de transactions AoP, par exemple, intercepter des opérations et effectuer des transactions si nécessaire
    • Nécessite une manipulation de code octet ou l'utilisation d'un proxy
    • Perte de contrôle lors des transactions
    • La magie noire, comme toujours :)

Ai-je raté une option?


Quelle est votre approche préférée lorsque vous essayez de minimiser l'impact des relations lazy-loaded Dans la conception de votre application?

(Oh, et désolé pour WoT )

86
Johan Sjöberg

Comme nous le savons tous, l'hibernation essaie d'être aussi non invasive et transparente que possible

Je dirais que l'hypothèse initiale est fausse. La persistance transaparente est un mythe, car l'application doit toujours prendre en charge le cycle de vie de l'entité et la taille du graphe d'objet 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.

26
axtavt

Je ne sais pas à quel problème (causé par la paresse) vous faites allusion, mais pour moi, la plus grande difficulté est d'éviter de perdre le contexte de la session dans mes propres caches d'application. Cas typique:

  • l'objet foo est chargé et placé dans une carte;
  • un autre thread prend cet objet de la carte et appelle foo.getBar() (quelque chose qui n'a jamais été appelé auparavant et qui est évalué paresseux);
  • boom!

Donc, pour résoudre ce problème, nous avons un certain nombre de règles:

  • envelopper les sessions de la manière la plus transparente possible (par exemple OpenSessionInViewFilter pour les applications Web);
  • avoir une API commune pour les threads/pools de threads où la liaison/dissociation de session db se fait quelque part dans la hiérarchie (enveloppé dans try/finally) afin que les sous-classes n'aient pas à y penser;
  • lors du passage d'objets entre les threads, transmettez les ID au lieu des objets eux-mêmes. Le thread récepteur peut charger un objet s'il en a besoin;
  • lors de la mise en cache d'objets, ne mettez jamais en cache des objets mais leurs identifiants. Disposez d'une méthode abstraite dans votre classe DAO ou gestionnaire pour charger l'objet à partir du cache Hibernate de 2e niveau lorsque vous connaissez l'ID. Le coût de la récupération d'objets à partir du cache Hibernate de deuxième niveau est encore beaucoup moins cher que d'aller dans DB.

Comme vous pouvez le voir, cela 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 enthousiaste. 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 pas en dire plus sont également bien pires, donc je suppose que je peux vivre avec.

7
mindas

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 de chargement différé". Si vous avez besoin de quelque chose de plus avancé pour l'optimisation des performances, je pense que la récupération des profils est la voie à suivre.

3
Augusto