2016-08-15 3 views
1

Ich weiß, dass dies immer wieder diskutiert wurde (ich habe mehrere eigene Implementierungen in dieser Angelegenheit), vielleicht ist das ein Teil des Grundes, den ich stelle: es gibt keine definitive Antwort (nach Stunden von Suche) und ich fühle, das ist Fang 22.JPA Hibernate Lazy Laden von Sammlungen

Meine Einstellung ist Spring Boot 1.3.6, die mit Hibernate 4.3.11 "verheiratet" ist. Ich verwende Spring Data JPA.

Ich habe eine Entität, die mehrere Sammlungen von Untereinheiten enthält, @OneToMany und @ManyToMany. Das ist das Vorbild - ich liebe oder hasse es nicht.

Dann habe ich eine normale CRUD UI mit einer Tabelle der Haupteinheit und einem Editor, wo der Benutzer Optionen für die Untereinheiten auswählen und die Haupteinheit speichern kann.

Die Benutzeroberfläche ist mit Vaadin gemacht und es sind Web Sockets beteiligt. Also - kein normaler Http-Tausch.

Mit den normalen Einstellungen bekomme ich die "normale" LazyInitializationException von Hibernate. Sobald die Entität in den Tabellenbildschirm geladen wurde, wird die Sitzung geschlossen. Wenn also die Sammlungen im Editor-Bildschirm aufgerufen werden, lade ich entweder die Entity vollständig neu, was die Mojo-Verwendung eines ORM oder I zunichte macht versuchen, etwas anderes:

  • die Sammlungen EAGER machen (und ziehen sie sie mit mir überall hin, nicht auf die n + 1 Problem zu erwähnen, die trotz der @Fetch(FetchMode=JOIN) geschieht, die es nicht haben in erster Linie sein sollen)
  • Fügen Sie die spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true zu meiner Startkonfiguration, die im Grunde die gleiche wie die erste Option ist, in einen Standard umgewandelt. Nein danke.
  • Verwenden Sie Hibernate.initialize für die Sammlungen unmittelbar nach dem Abrufen der übergeordneten Entität (dh in der gleichen Sitzung), das ist so etwas wie programmatische EAGER-Abruf - aber es ist stark mit Hibernate gekoppelt und es speichert mir nicht die zusätzliche Abfrage (n + 1).
  • abrufen Sie die Daten mit "Fetch Join" in meiner JPQL-Abfrage - außer dies kann dazu führen, dass die Ergebnismenge auf Tausende von Zeilen eskalieren (die dann im Speicher zusammengefasst werden), ganz zu schweigen von A) erfordert JPQL für ansonsten triviale Abfragen , implementiert mit einfachen Interface-Namen in Spring Data und B) wenn Sie Paginierung wollen, müssen Sie die countingQuery auch schreiben (ohne Join Fetch), weil Sie sonst eine schöne, klare und leicht zu debuggen org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list bekommen. Auch, wenn Sie „vergessen“, um die fetchType Optionen auf den @OneToMany oder @ManyToMany Anmerkungen zu entfernen, erhalten Sie eine andere Haarzieh Ausnahme: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
  • erstellen Sie einen Filter nach dem Open-in-View-Muster (oder eher anti-Muster), was würde den gleichen EntityManager für alle Anfragen in der gleichen, na ja, etwas - nicht die HTTP-Sitzung abrufen, weil wir Web Sockets verwenden (oder so ziemlich alles andere, soll Hibernate nicht ein Web-ORM sein); nicht die Vaadin-Sitzung, weil ich vielleicht mehrere Servlets habe oder die gesamte Anwendung verteile; oder hör auf, Vaadin zu benutzen; kein lokaler Thread, da ich Operationen in verschiedenen Thread-Pools ausführen kann, die nicht vom lokalen Thread geerbt wurden: Dies ist wiederum nicht die Frage von Hibernate.

schließlich zu Eclipseschalt, die den vernünftigen Ansatz, einfach Anfordern der Untersammlungen haben, wenn sie in einer neuen Sitzung scheint erforderlich, eher eine lästige Pflicht als Frühlings-Boot in der Regel Angebote (mit noch mehrere Gesprächen im Hinblick auf die Notwendigkeit, die Anwendung mit dem Instrumentierungsagenten auszuführen).

Gibt es letztendlich eine hackfreie Lösung für dieses Problem?

Antwort

1

Es gibt zwei Hauptstrategien, die mit Hibernate arbeiten.

  • lang lebte Sitzung
  • kurzlebig Sitzung (Wurf LazyInitializationException nach Conn-close)

Die LazyInitializationException gibt uns den Hinweis (db-Sitzung paralell zu http-Sitzung halten), den Sie verwenden short-lived-session.

Entweder Sie verwenden langlebige Sitzung und verwenden die Entitäten in der Ansicht des MVC. Oder Sie ordnen alle Werte von den Entitäten neuen Wrappern zu, die keine Entitäten sind.

Ich würde vorschlagen, die short-lived-session Strategie zu behalten und alle Werte auf pojos für die Ansicht neu zuordnen. Denn: Die db-Struktur ist selten die Informationsstruktur, die Sie im Frontend benötigen. Im Beispiel:

Sie haben zwei Entitys:

  • Benutzer
    • lange UserId
    • String Vorname
    • String Nachname
  • Adresse
    • lange AddressId
    • String Straße
    • String Zip
    • String Stadt
    • lange UserId

Aber im Blick haben Sie das Dataobject (DTO):

  • Klasse Benutzer
    • String Vorname
    • String Nachname
    • String Straße
    • String Zip
    • String Stadt

Sie sehen, sind die Bohnen anders und Sie erhalten keine LazyInitializationException nach conn-nah.

+0

Also im Wesentlichen, verwenden DTO? –

+0

Nur zwischen Sicht und Controller, ja. –

+0

Langlebige Sitzungen sind im Wesentlichen das Open-in-View-Muster, oder hatten Sie etwas anderes vor? – Deroude

Verwandte Themen