web-dev-qa-db-fra.com

Comment résoudre l'exception LazyInitializationException avec JPA et Hibernate

Je travaille sur un projet pour un client qui souhaite utiliser une initialisation différée . Ils ont toujours "une exception d'initialisation différée" lorsqu'ils mappent des classes avec le mode de chargement différé par défaut.

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name =    "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")})
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection;

Existe-t-il un modèle standard utilisant des classes JPA pour éviter cette erreur?

Les extraits sont les bienvenus, merci beaucoup pour votre temps. 

48
rupertin

Hibernate 4.1.6 résout finalement ce problème: https://hibernate.atlassian.net/browse/HHH-7457

Vous devez définir la propriété hibernate hibernate.enable_lazy_load_no_trans = true

Voici comment le faire au printemps:

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="com.mycompany.somepackage"/>
    <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/>
    <property name="jpaDialect" ref="jpaDialect"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
        </props>
    </property>
</bean>

Voila; Maintenant, vous n'avez plus à vous soucier de l'exception LazyInitializationException lorsque vous naviguez dans votre modèle de domaine en dehors d'une session de veille prolongée (contexte de persistance dans "JPA-speak")

60
andreak

Il existe de nombreuses façons de pré-extraire des propriétés. Elles sont donc présentes une fois la session fermée: 

  1. Appelez le getter approprié. Une fois le champ extrait dans le haricot, il est là après la fermeture de la session. 
  2. Vous pouvez initialiser le champ dans une requête EJBQL, cherchez le mot clé JOIN FETCH.
  3. Activez AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS si vous utilisez une version d'Hibernate compatible.

Plusieurs problèmes peuvent survenir lorsque vous essayez ces solutions:

  1. L'invocation des getters peut être optimisée par le compilateur JIT (cela prend parfois un certain temps). 
  2. Les entités que vous essayez de JOIN FETCH peuvent être liées par le biais de plusieurs relations 'nombreuses' impliquant des listes. Dans ce cas, la requête résultante renvoie des résultats ambigus et Hibernate refusera de récupérer vos données dans une requête unique.
  3. Il y a déjà un bogue intéressant lié à AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS . Et il y en aura plus car, comme le disent les gars de l'hibernation: Remarque: cela peut se produire en dehors de la transaction et n'est pas sans danger. Utiliser avec précaution. Vous êtes principalement seul.

La meilleure solution consiste à essayer un JOIN FETCH en premier. Si cela ne fonctionne pas, essayez l'approche getter. Si le compilateur JIT dérange le processus au moment de l'exécution, affectez le résultat à un public static volatile Object.

Ou arrêtez d'utiliser Hibernate ...

16
jb.

Notez que vous ne devriez pas utiliser hibernate.enable_lazy_load_no_trans pré Hibernate 4.1.7, car il fuit les connexions. Voir https://hibernate.onjira.com/browse/HHH-7524

14
Hani

LazyInitializationException signifie que vous appelez la collection après la fermeture de la session de veille prolongée ou après la séparation de l'objet de la session. 

Vous devez reconnecter l'objet à la session de mise en veille prolongée, modifier l'emplacement où vous appelez la collection ou déplacer la limite de fermeture de la session vers une couche supérieure.

7
Daniel Alexiuc

OpenSessionInView est un modèle pour traiter ce problème. Quelques infos ici:

http://www.hibernate.org/43.html

Vous voudrez être prudent lors de la mise en œuvre de ce modèle et en comprendre les implications. Chaque fois que vous naviguez dans une association paresseuse dans la vue, une autre requête SQL sera lancée pour charger les données. Si vos cas d'utilisation sont tels que le nombre et la taille de ces requêtes SQL sont faibles, cela n'a peut-être pas d'importance. Assurez-vous au moins d'ajuster vos paramètres de journalisation afin de pouvoir identifier le type de requêtes qu'Hibernate exécute de manière "magique" en arrière-plan afin de charger les données.

Pensez également au type d'application que vous écrivez. Si vous ne traitez pas d'accès distant (pas de services Web, pas de client Web basé sur AJAX), alors OSIV peut fonctionner très bien. Cependant, si un sérialiseur à distance commence à parcourir tout le graphe d'objet, il déclenchera probablement un nombre ridicule de requêtes SQL et paralysera votre base de données et votre serveur d'applications.

5
cliff.meyers

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 nécessitent soit que la connexion à la base de données soit ouverte pour le rendu de l'interface utilisateur (Open Session in View), ou une connexion à la base de données est nécessaire pour chaque association différée extraite du contexte de persistance initial (hibernate.enable_lazy_load_no_trans).

Parfois, vous n’avez même pas besoin d’entités et une projection DTO, c’est encore mieux… .. Vous ne devez extraire des entités que lorsque vous devez les modifier. Pour les transactions en lecture seule, les projections DTO sont meilleures .

5
Vlad Mihalcea

Lorsque vous utilisez une collection et que vous souhaitez l'initialiser avec un chargement différé, utilisez cette collection avant la fermeture de la session. Si session est fermée après cela, si vous souhaitez utiliser, vous obtenez une exception lazyinitializeException car lazy est try par défaut.

4
SANTOSH Kumar

Les tutoriels Oracle Java soulignent que "Les beans entreprise prennent en charge les transactions, mécanismes qui gèrent l'accès simultané aux objets partagés". Ainsi, afin de gérer les problèmes de Lazy Fetch, je crée un bean de session Java sans état, puis j'obtiens toutes les sous-classes dont j'ai besoin avant de revenir de la méthode. Cela évite l'exception d'extraction paresseuse. Oracle a également fait référence à ce phénomène en tant que modèle J2EE principal "Façade de session". Cette tendance semble meilleure que certaines des autres pratiques mentionnées.

1
K.Nicholas

Je travaille sur un projet qui vise à résoudre les problèmes JPA courants lors du mappage d'entités vers des DTO à l'aide de ModelMapper. Ce problème a déjà été résolu sur le projet. Lien vers le projet: JPA Model Mapper

"Il est crucial pour les performances de déclarer les entités comme étant des charges paresseuses. Nous n'avons donc pas besoin d'extraire toutes les entités associées chaque fois que nous avons besoin de données." Mais cette technique soulève quelques problèmes. Le plus courant est le LazyInitializationException qui peut être assez ennuyeux parfois . La plupart du temps, nous voudrions simplement un objet null pour une entité Non chargée au lieu d'un objet qui lève une exception si on y accède ... "

Source: JPA Model Mapper

Par conséquent, dans le projet, nous traitons L'exception LazyInitializationException en définissant la valeur null pour toutes les entités non chargées. Les exemples ci-dessous montrent comment cela fonctionne.

Remappage d'une entité avec la valeur null pour toutes les entités non chargées:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);

Remappage d'une entité sur un DTO avec la valeur null pour toutes les entités non chargées:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);

Pour plus d'informations, veuillez consulter JPA Model Mapper

0