wake-up-neo.com

So lösen Sie die LazyInitializationException bei Verwendung von JPA und Hibernate

Ich arbeite an einem Projekt für einen Kunden, der die verzögerte Initialisierung verwenden möchte .. __ Sie erhalten immer eine "faule Initialisierungsausnahme", wenn Klassen mit dem standardmäßigen Lazy-Loading-Modus zugeordnet werden.

@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;

Gibt es ein Standardmuster mit JPA-Klassen, um diesen Fehler zu vermeiden?

Ausschnitte sind willkommen, vielen Dank für Ihre Zeit. 

48
rupertin

Hibernate 4.1.6 löst dieses Problem schließlich: https://hibernate.atlassian.net/browse/HHH-7457

Sie müssen die hibernate-Eigenschaft hibernate.enable_lazy_load_no_trans = true setzen

So geht es im Frühling:

<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; Jetzt müssen Sie sich nicht um LazyInitializationException kümmern, wenn Sie Ihr Domänenmodell außerhalb einer Hibernate-Sitzung navigieren (Persistenz-Kontext in "JPA-speak")

60
andreak

Es gibt viele Möglichkeiten, Eigenschaften vorabzurufen. Sie sind also nach dem Schließen der Sitzung verfügbar: 

  1. Rufen Sie den entsprechenden Getter an. Nachdem das Feld in Bean abgerufen wurde, ist es dort, nachdem die Sitzung geschlossen wurde. 
  2. Sie können das Feld in der EJBQL-Abfrage initialisieren. Suchen Sie nach dem Schlüssel JOIN FETCH.
  3. Aktivieren Sie AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, wenn Sie eine Hibernate-Version verwenden, die dies unterstützt.

Beim Versuch dieser Lösungen können mehrere Probleme auftreten:

  1. Der Aufruf der Getter kann vom JIT-Compiler entfernt werden (manchmal dauert es eine Weile). 
  2. Die Entitäten, für die Sie JOIN FETCH versuchen, können über mehrere 'viele' Beziehungen mit List verbunden sein. In diesem Fall liefert die resultierende Abfrage mehrdeutige Ergebnisse und Hibernate lehnt den Abruf Ihrer Daten in einer einzigen Abfrage ab.
  3. Es gibt bereits einen interessanten Fehler, der mit AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS zusammenhängt. Und es wird mehr geben, weil die Jungs im Winterschlaf sagen: Hinweis: Dies kann außerhalb der Transaktion passieren und ist nicht sicher. Mit Vorsicht verwenden. Du bist meistens alleine.

Der beste Weg ist, zuerst einen JOIN FETCH auszuprobieren. Wenn das nicht funktioniert, versuchen Sie den Getter-Ansatz. Wenn dies zur Laufzeit vom JIT-Compiler durcheinander gebracht wird, weisen Sie das Ergebnis einem public static volatile Object zu.

Oder aufhören, Hibernate zu verwenden ...

16
jb.

Beachten Sie, dass Sie hibernate.enable_lazy_load_no_trans nicht vor Hibernate 4.1.7 verwenden sollten, da dadurch Verbindungen auslaufen. Siehe https://hibernate.onjira.com/browse/HHH-7524

14
Hani

LazyInitializationException bedeutet, dass Sie die Auflistung aufrufen, nachdem die Ruhezustandsitzung geschlossen wurde oder nachdem das Objekt von der Sitzung getrennt wurde. 

Sie müssen entweder das Objekt erneut anhängen, um die Sitzung in den Ruhezustand zu versetzen, den Ort ändern, an dem Sie die Sammlung aufrufen, oder die Begrenzung, an der die Sitzung geschlossen wird, auf eine höhere Ebene verschieben.

7
Daniel Alexiuc

OpenSessionInView ist ein Muster, um dieses Problem zu lösen. Einige Infos hier:

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

Sie sollten beim Implementieren dieses Musters vorsichtig sein und die Auswirkungen verstehen. Jedes Mal, wenn Sie in einer Ansicht durch eine langsame Zuordnung navigieren, wird eine andere SQL-Abfrage ausgelöst, um die Daten zu laden. Wenn Ihre Anwendungsfälle so sind, dass die Anzahl und Größe dieser SQL-Abfragen klein ist, ist dies möglicherweise ohne Bedeutung. Stellen Sie sicher, dass Sie mindestens Ihre Protokolleinstellungen anpassen, um zu sehen, welche Art von Abfragen Hibernate "magisch" im Hintergrund ausführt, damit Sie die Daten laden können.

Berücksichtigen Sie auch die Art der Anwendung, die Sie schreiben. Wenn Sie sich nicht mit Remoting beschäftigen (keine Webservices, kein AJAX-basierter Webclient), funktioniert OSIV möglicherweise sehr gut. Wenn jedoch ein Remoting-Serializer das gesamte Objektdiagramm durchläuft, wird dies wahrscheinlich eine lächerliche Anzahl von SQL-Abfragen auslösen und die Datenbank und den App-Server beschädigen.

5
cliff.meyers

Der beste Weg, die LazyInitializationException zu lösen ist die Verwendung der JOIN FETCH-Direktive in Entitätsabfragen. 

EAGER loading ist schlecht für die Leistung. Es gibt auch Anti-Patterns wie:

Die sollten Sie niemals verwenden, da sie entweder eine Datenbankverbindung für das Rendern der Benutzeroberfläche (Open Session in View) benötigen oder eine Datenbankverbindung für jede Lazy-Assoziation, die außerhalb des ursprünglichen Persistence Context (hibernate.enable_lazy_load_no_trans) abgerufen wird.

Manchmal benötigen Sie keine Entitäten, und eine DTO-Projektion ist sogar noch besser. Sie sollten Entitäten nur dann abrufen, wenn Sie sie ändern müssen. Für schreibgeschützte Transaktionen sind DTO-Projektionen besser .

5
Vlad Mihalcea

Wenn Sie eine Sammlung verwenden und diese mit dem langsamen Laden initialisieren möchten, verwenden Sie diese Sammlung vor dem Schließen der Sitzung. Wenn die Sitzung danach geschlossen wird, wenn Sie sie verwenden möchten, erhalten Sie lazyinitializeException, da Lazy standardmäßig try ist.

4
SANTOSH Kumar

Die Oracle Java-Lernprogramme weisen darauf hin, dass "Enterprise-Beans Transaktionen unterstützen, also die Mechanismen, die den gleichzeitigen Zugriff auf gemeinsam genutzte Objekte verwalten." Um die Lazy Fetch-Probleme zu behandeln, erstelle ich ein Stateless Java Session Bean und bekomme dann alle erforderlichen Unterklassen, bevor ich von der Methode zurückkehre. Dies vermeidet die Lazy-Fetch-Ausnahme. Oracle hat dies auch als "J2EE-Pattern" für die "Session-Fassade" bezeichnet. Dieses Muster scheint besser zu sein als einige der anderen genannten Praktiken.

1
K.Nicholas

Ich arbeite an einem Projekt, das auf die Lösung allgemeiner JPA-Probleme abzielt, wenn Entitäten mithilfe von ModelMapper DTOs zugeordnet werden. Dieses Problem wurde im Projekt bereits gelöst. Projektlink: JPA Model Mapper

"Es ist entscheidend für die Leistung, Entitäten als" Lazy Load "zu deklarieren. Wir müssen also nicht jedes Mal alle zugehörigen Entitäten abrufen, wenn wir Daten benötigen. Diese Technik führt jedoch zu einigen Problemen. Die häufigste ist die LazyInitializationException, die manchmal ziemlich nervig sein kann ..__ Meistens möchten wir nur ein Null-Objekt für eine nicht geladene -Entität anstelle eines Objekts, das beim Zugriff eine Ausnahme auslöst ... "

Quelle: JPA Model Mapper

Daher behandeln wir im Projekt die LazyInitializationException, indem wir für alle nicht geladenen Entitäten null setzen. Die folgenden Beispiele zeigen, wie es funktioniert.

Entitätseinstellung für alle nicht geladenen Entitäten auf null setzen:

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);

Eine Entität für alle nicht geladenen Entitäten auf eine DTO-Einstellung null setzen:

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);

Weitere Informationen finden Sie unter JPA Model Mapper