2009-11-22 1 views
10

Je suis en train de courir dans les exceptions de LazyLoading comme la plupart des gens qui essayent à distance avec un ORM. Dans la plupart des cas, le basculement vers la récupération désirée résout le problème (chargement paresseux/requêtes non atomiques/sécurité du filetage/problème n + 1 ...). Mais chercher ardemment a aussi des inconvénients si vous avez affaire à un très gros graphique d'objets.Stratégies de chargement Lazy/Eager dans les cas à distance (JPA)

Le chargement du graphe d'objet entier n'est pas nécessaire dans la plupart des cas d'utilisation. Il est mauvais de charger plus de données que nécessaire (ou de les charger à partir de la base de données et d'extraire le sous-ensemble nécessaire).

Alors, quelles sont les autres solutions pour résoudre ce genre de problème (lors de l'exécution)?
J'ai vu:

  • Injecter une dépendance d'accès aux données en objet de domaine et laissez l'objet décider soit de charger paresseux ou désireux: se sent mal! La couche de domaine doit être indépendante de tout service. L'injection de domaine est également une opération coûteuse. Le domaine devrait être ignorant de l'accès aux données et devrait être utilisé avec ou sans accès aux données.
  • Récupère tout paresseux sauf les cas d'utilisation qui nécessitent plus de données: Cela semble meilleur pour les performances mais force ainsi de nombreux clients => allers-retours serveur/base de données. L'initialisation des champs paresseux peut également souffrir de la douleur (essayé avec JPA). De cette façon, ne semble pas générique et fait l'objet des mêmes restrictions paresseux mentionnées ci-dessus.
  • Encapsuler la persistance dans la classe Lazy: Plus de complexité, pas de meilleure pratique pour l'interopération avec ORM. Couche de services de ballonnements (tellement de code "manuscrit" se sent mal).
  • Utilisez des projections complètes pour chaque cas d'utilisation: nous finirons dans SQL et perdrons l'avantage d'un ORM.
  • Une couche DTO/Virtual Proxy renforce la complexité et rend le code plus difficile à maintenir (Wormhole antipattern >> Bloat).

Je pensais beaucoup à une autre façon. Peut-être que la projection générique white./black listning est une solution. Idée (liste noire): définit une liste de classes avec les limites d'une opération de récupération. Si une propriété correspond et qu'elle est paresseuse, supprimez le proxy paresseux (CGLIB) et remplissez la valeur avec null. Sinon, simple empêche d'aller chercher (et laisse la valeur à null). Nous pouvons donc définir des limites claires dans nos DAO.

Exemple: ProductDao.findByName("Soap",Boundaries.BLACKLIST,"Category, Discount") les deux derniers paramètres peuvent également être liés dans un objet Boundaries. Idea (whitelist): Comme la liste noire, mais vous devez déclarer les propriétés qui doivent être chargées dans une liste blanche.

Que pensez-vous d'une telle solution? (Problèmes possibles, restrictions, avantages ...) Comment dois-je écrire cela dans java? Peut-être via AOP pour faire correspondre les méthodes DAO (parce que je suis capable de modifier le comportement proxy cglib là-bas)?

+0

Quel type d'architecture avez-vous? Utilisez-vous GWT, par exemple? – Kaitsu

+0

Services Web JAX-WS via une implémentation de référence (Metro) –

Répondre

6
  1. Vous pouvez vous débarrasser de toutes les collections que vous voulez et utiliser NamedQueries à la place. Nous avons utilisé cette approche dans un projet (EJB + Swing), et cela a plutôt bien fonctionné - ainsi vous déterminez les données exactes à extraire. NamedQueries sont des requêtes normales, imaginez-les comme PreparedStatement-s. L'idée n'est pas de créer/récupérer/mettre à jour/supprimer des objets uniques avec des requêtes.L'idée est que vous récupériez vos Collections avec des requêtes. Par exemple, au lieu de mapper une liste @ManyToMany, définissez une NamedQuery qui récupère cette liste. Ainsi, vous pouvez extraire les données de collecte séparément, et seulement lorsque vous en avez besoin, pas automatiquement. Utilisez un proxy personnalisé (en utilisant CGLIB) pour les objets transférés - chaque fois qu'une collection est référencée (via son getter), essayez de retreindre, et attrapez LazyInitializationException et appelez le niveau serveur pour les données demandées.

  2. Tout comme la précédente, mais de ne faire que des proxies des collections, de la manière dont Hibernate les utilise lorsque l'initialisation paresseuse est nécessaire.

  3. En outre, jetez un oeil au modèle Value List Handler - pourrait être utile.

(Vous pouvez également utiliser hibernate.max_fetch_depth (si vous utilisez Hibernate) avec une combinaison de ce qui précède, si elle est adaptée à votre cas.)

+0

Les requêtes nommées aboutissent à la duplication de code pour les opérations CRUD si elles sont appliquées à chaque classe de domaine. J'utilise des DAO Generic JPA câblés et créés au printemps. Il est également très important d'éviter la dépendance d'Hibernate. Décorateur/Proxy via AOP semble bien. Mais votre approche n'est pas découplée de la couche de données (domaine/mix de services | domaine/mélange d'accès aux données) car le getter de domaine devrait faire des appels de serveur. J'essaie d'éviter l'injection dans le modèle (logique pour les appels etc.) comme décrit dans ma question. –

+1

Eh bien, Hibernate par exemple utilise la même idée pour aller chercher des collections paresseuses quand la session est toujours là - elle fait un proxy de la collection qui contient de la logique. Ce n'est donc pas un gros problème pour le faire. Les NamedQueries ne sont pas une duplication de l'opération CURD, mais sont à la place des annotations @ * ToMany (et des éléments connexes) - un peu plus verbeux, cependant. – Bozho

+0

P.S. Pourquoi est-il important d'éviter la dépendance d'Hibernate?Si vous utilisez Hibernate comme fournisseur de persistance, vous finirez par utiliser ses annotations dans les cas où JPA n'a pas l'option requise. :) – Bozho

1

Même si cela prend un peu de travail et JAX-WS/JAXB nécessite des versions récentes de ces bibliothèques, c'est une solution très élégante: créer un marshaller qui peut tester si les objets/collections ont été initialisés.

Comme décrit ici: https://forum.hibernate.org/viewtopic.php?f=1&t=998896

1

De nos jours (2013), il est possible de garder de chargement paresseux si vous votre service à distance avec GraniteDS.

Cela devrait correctement sérialiser vos entités JPA ou Hibernate en n'initialisant pas les relations paresseuses et en gardant l'état paresseux pour le client. Si vous accédez à ces relations sur le client, il les récupère de manière transparente en arrière-plan. En outre, GraniteDS semble être capable de faire un chargement paresseux inverse, ce qui signifie que lorsque vous renvoyez des objets modifiés au serveur, il n'enverra pas d'entités non modifiées, rendant ainsi la communication serveur nécessaire très efficace. Je ne suis pas encore un expert GraniteDS, mais il semble être capable de s'intégrer aux couches de service JEE6 et Spring et travaille avec tous les fournisseurs JPA les plus importants. Naturellement, vous devez masquer l'accès distant basé sur GraniteDS derrière une interface de service pour maximiser le comportement transparent, mais cela peut être facilement fait si le client utilise également Spring (vous injectez donc le service en fonction des besoins de l'environnement) .

Questions connexes