web-dev-qa-db-fra.com

Résoudre le problème Hibernate Lazy-Init avec hibernate.enable_lazy_load_no_trans

J'ai souffert de la tristement célèbre exception d'hibernation

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Maintenant, la communauté encourage

<property name="hibernate.enable_lazy_load_no_trans" value="true"/>

dire que cela résout le problème mais TILISEZ-LE AVEC PRÉCAUTION.

Qu'est-ce qu'ils veulent dire par l'utiliser avec prudence? Qu'est-ce que cette propriété fait réellement?

S'il vous plaît donnez-moi des idées. Merci d'avance.

51
Sachin Verma

Le problème avec cette approche est que vous pouvez avoir l’effet N + 1.

Imaginez que vous avez l'entité suivante:

public class Person{
    @OneToMany // default to lazy
    private List<Order> orderList;
}

Si vous avez un rapport qui renvoie 10K personnes et si vous exécutez le code person.getOrderList() dans le rapport, le JPA/Hibernate exécutera 10K requêtes. C'est l'effet N + 1, vous n'aurez aucun contrôle sur toutes les requêtes qui seront exécutées.

Imaginez maintenant que l'ordre est comme ci-dessous:

public class Order{
    @OneToMany // default to lazy
    private List<EmailSent> emailSentList;
}

Imaginez maintenant que vous avez une itération avec person.getOrderList() et que pour chaque Order order Vous ferez une order.getEmailSentList(). Pouvez-vous voir le problème maintenant?

Pour LazyInitializationException, vous pouvez avoir quelques solutions:

  • Utilisez l'approche OpenInSessionInView. Vous devrez créer un filtre Web qui ouvrira et clôturera la transaction. Le problème avec est l'effet N + 1.
  • Utilisez la configuration hibernate.enable_lazy_load_no_trans, c’est-à-dire un hibernate et vous ne pourrez pas porter votre projet vers un autre fournisseur JPA si nécessaire. Vous pouvez également avoir l'effet N + 1.
  • Utilisez la fonctionnalité EJB nommée PersistenceContext Extended. Avec cela, vous gardez le contexte ouvert de plusieurs transactions. Les problèmes sont les suivants: Un effet N + 1 peut se produire, utilisez beaucoup de mémoire serveur (les entités resteront gérées)
  • Utilisez le FETCH dans la requête. Avec cette approche, vous pourriez faire un JPQL/HQL comme: select p from Person p join fetch p.orderList. Avec cette requête, votre liste sera chargée à partir de la base de données et n'aura pas l'effet N + 1. Le problème est que vous devrez écrire un JPQL pour chaque cas.

Si vous avez toujours un problème, vérifiez ces liens:

41
uaiHebert

Cela va à l'encontre de la manière dont nous pouvons tirer parti de l'application par Hibernate de la sémantique de lecture répétable avec le concept de session. Lorsqu'un objet est chargé pour la première fois et que l'objet est référencé à nouveau au cours de la vie de la session, le même objet est renvoyé par IRRESPECTIVE pour indiquer si cet objet a été modifié dans la base de données. C'est la sémantique de lecture répétable fournie automatiquement par hibernate.

Avec ce paramètre, aucune session ne fournit cette garantie. Par conséquent, si vous accédez maintenant à ces données, vous obtiendrez la dernière version de ces données.

Cela pourrait être bien. Mais considérons le cas où cet objet est conservé à un endroit pendant longtemps et que les données ont considérablement changé, de sorte que les données extraites paresseusement sont bien différentes des données déjà chargées lorsque la session était active. C'est ce qui doit vous préoccuper.

En termes simples, vous pouvez utiliser ce paramètre en toute sécurité si votre programme n'est pas affecté par: Quelle est l'état de conservation des données déjà extraites en cours de session, aux données qui seront extraites paresseusement en cours de session

Mais si cela (votre programme est exposé à des problèmes de synchronisation, quand il pourrait fonctionner correctement une fois et échouer une autre fois) est un problème, alors récupérez toutes les données nécessaires en cours de session.

10
codedabbler

Le meilleur moyen de résoudre l'exception LazyInitializationException consiste à utiliser la directive JOIN FETCH dans vos requêtes d'entité.

EAGER loading est mauvais pour la performance. En outre, il existe des anti-modèles tels que:

Ce que vous ne devriez jamais utiliser car ils exigent soit que la connexion à la base de données soit ouverte pour le rendu de l'interface utilisateur (Open Session in View), soit une connexion à la base de données est nécessaire pour chaque association lente qui est extraite en dehors du contexte de persistance initial (hibernate.enable_lazy_load_no_trans).

Parfois, vous n’avez même pas besoin d’entités et ne projection DTO est encore meilleure .

4
Vlad Mihalcea

Probablement parce qu’il existe de meilleures solutions, comme @ Transactionnelle , dans laquelle les sessions d’ouverture et de clôture suivent un schéma très courant consistant à "ouvrir une session, puis envelopper le tout dans un essai-rattrapage-finalement; la session." Cette annotation se situe généralement au niveau de la demande pour les applications et services Web.

Ou si vous avez besoin d'un contrôle plus granulaire, vous pouvez ouvrir des sessions manuellement à l'aide d'une SessionFactory.

Et comme d'autres l'ont mentionné, le chargement paresseux est une chose dont vous devez être au courant. Ce n'est pas une solution miracle, mais cela peut être très utile. Généralement, si vos applications sont conçues pour avoir de nombreuses petites requêtes, alors c'est ok.

Le chargement désagréable peut aussi être très mauvais. Par exemple, lorsque votre modèle d'objet a beaucoup de relations plusieurs à plusieurs mais que vos demandes n'utilisent pas les données à plus d'un niveau.

Ou vous pouvez simplement oublier le tout pour le moment. Utilisez le chargement paresseux jusqu'à ce que cela devienne un problème. Et si c'était le cas, vous auriez de toute façon mieux réussi avec Mybatis.

2
James Watkins