Und welche alternativen Strategien verwenden Sie, um LazyLoadExceptions zu vermeiden?
Ich verstehe, dass die offene offene Sitzung Probleme hat mit:
- Überlagerte Anwendungen, die in verschiedenen JVMs ausgeführt werden
- Transaktionen werden erst am Ende festgeschrieben, und höchstwahrscheinlich möchten Sie die Ergebnisse vorher.
Wenn Sie jedoch wissen, dass Ihre Anwendung auf einer einzelnen VM ausgeführt wird, können Sie Ihre Schmerzen durch die Verwendung einer offenen Sitzungsstrategie lindern.
java
hibernate
jpa
lazy-loading
open-session-in-view
HeDinges
quelle
quelle
Antworten:
Da das Senden möglicherweise nicht initialisierter Proxies, insbesondere von Sammlungen, in der Ansichtsebene und das Auslösen des Ladens im Ruhezustand von dort aus sowohl aus Sicht der Leistung als auch des Verständnisses problematisch sein kann.
Verständnis :
Durch die Verwendung von OSIV wird die Ansichtsebene mit Bedenken in Bezug auf die Datenzugriffsschicht "verschmutzt".
Die Ansichtsebene ist nicht darauf vorbereitet, eine zu behandeln,
HibernateException
die beim verzögerten Laden auftreten kann, vermutlich jedoch die Datenzugriffsebene.Leistung :
OSIV neigt dazu, das richtige Laden von Entitäten unter den Teppich zu ziehen - Sie bemerken nicht, dass Ihre Sammlungen oder Entitäten träge initialisiert sind (möglicherweise N + 1). Mehr Komfort, weniger Kontrolle.
Update: Weitere Informationen zu diesem Thema finden Sie unter Das OpenSessionInView-Antimuster . Der Autor listet drei wichtige Punkte auf:
quelle
Eine längere Beschreibung finden Sie in meinem Artikel " Open Session In View Anti-Pattern ". Andernfalls finden Sie hier eine Zusammenfassung, warum Sie Open Session In View nicht verwenden sollten.
Open Session In View verfolgt einen schlechten Ansatz beim Abrufen von Daten. Anstatt die Geschäftsschicht entscheiden zu lassen, wie alle Zuordnungen, die von der Ansichtsebene benötigt werden, am besten abgerufen werden, wird der Persistenzkontext geöffnet, damit die Ansichtsebene die Proxy-Initialisierung auslösen kann.
OpenSessionInViewFilter
ruft dieopenSession
Methode des Basiswerts aufSessionFactory
und erhält eine neueSession
.Session
ist an das gebundenTransactionSynchronizationManager
.OpenSessionInViewFilter
ruft diedoFilter
diejavax.servlet.FilterChain
Objektreferenz und die Anforderung wird weiter verarbeitetDispatcherServlet
wird aufgerufen und leitet die HTTP-Anforderung an den Basiswert weiterPostController
.PostController
ruft dasPostService
auf, um eine Liste derPost
Entitäten zu erhalten.PostService
öffnet eine neue Transaktion und dasHibernateTransactionManager
wiederverwendet das gleicheSession
, das von der geöffnet wurdeOpenSessionInViewFilter
.PostDAO
ruft die Liste derPost
Entitäten ab, ohne eine verzögerte Zuordnung zu initialisieren.PostService
schreibt die zugrunde liegende Transaktion fest, wird jedochSession
nicht geschlossen, da sie extern geöffnet wurde.DispatcherServlet
Rendering der Benutzeroberfläche beginnt, die wiederum durch die verzögerten Zuordnungen navigiert und deren Initialisierung auslöst.OpenSessionInViewFilter
kann die schließenSession
, und die zugrunde liegende Datenbankverbindung wird ebenfalls freigegeben.Auf den ersten Blick mag dies nicht schrecklich aussehen, aber sobald Sie es aus einer Datenbankperspektive betrachten, werden eine Reihe von Fehlern offensichtlicher.
Die Serviceschicht öffnet und schließt eine Datenbanktransaktion, danach findet jedoch keine explizite Transaktion statt. Aus diesem Grund wird jede zusätzliche Anweisung aus der UI-Rendering-Phase im Auto-Commit-Modus ausgeführt. Das automatische Festschreiben übt Druck auf den Datenbankserver aus, da jede Anweisung das Transaktionsprotokoll auf die Festplatte leeren muss, wodurch auf der Datenbankseite viel E / A-Verkehr verursacht wird. Eine Optimierung wäre, das
Connection
als schreibgeschützt zu markieren, wodurch der Datenbankserver das Schreiben in das Transaktionsprotokoll vermeiden kann.Es gibt keine Trennung von Bedenken mehr, da Anweisungen sowohl von der Service-Schicht als auch vom UI-Rendering-Prozess generiert werden. Das Schreiben von Integrationstests, die die Anzahl der generierten Anweisungen bestätigen, erfordert das Durchlaufen aller Ebenen (Web, Service, DAO), während die Anwendung auf einem Webcontainer bereitgestellt wird. Selbst wenn eine In-Memory-Datenbank (z. B. HSQLDB) und ein leichtgewichtiger Webserver (z. B. Jetty) verwendet werden, werden diese Integrationstests langsamer ausgeführt als wenn Schichten getrennt würden und die Back-End-Integrationstests die Datenbank verwenden würden, während die Front-End-Integrationstests verspotteten die Service-Schicht insgesamt.
Die UI-Ebene ist auf das Navigieren in Assoziationen beschränkt, was wiederum N + 1-Abfrageprobleme auslösen kann. Obwohl Hibernate Angebote
@BatchSize
zum Abrufen von Zuordnungen in Stapeln undFetchMode.SUBSELECT
zur Bewältigung dieses Szenarios anbietet , wirken sich die Anmerkungen auf den Standardabrufplan aus, sodass sie auf jeden Geschäftsanwendungsfall angewendet werden. Aus diesem Grund ist eine Datenzugriffsschichtabfrage viel besser geeignet, da sie auf die aktuellen Anforderungen für das Abrufen von Anwendungsfalldaten zugeschnitten werden kann.Last but not least kann die Datenbankverbindung während der gesamten Rendering-Phase der Benutzeroberfläche (abhängig von Ihrem Verbindungsfreigabemodus) gehalten werden, wodurch die Verbindungslease-Zeit verlängert und der gesamte Transaktionsdurchsatz aufgrund einer Überlastung des Datenbankverbindungspools begrenzt wird. Je länger die Verbindung gehalten wird, desto mehr andere gleichzeitige Anforderungen warten darauf, eine Verbindung aus dem Pool zu erhalten.
Entweder wird die Verbindung zu lange gehalten, oder Sie erwerben / geben mehrere Verbindungen für eine einzelne HTTP-Anforderung frei, wodurch der zugrunde liegende Verbindungspool unter Druck gesetzt und die Skalierbarkeit eingeschränkt wird.
Frühlingsstiefel
Leider ist Open Session in View in Spring Boot standardmäßig aktiviert .
Stellen Sie also sicher, dass Sie in der
application.properties
Konfigurationsdatei den folgenden Eintrag haben:Dies wird deaktivieren OSIV, so dass Sie den Griff ,
LazyInitializationException
den richtigen Weg .quelle
Transaktionen können in der Service-Schicht festgeschrieben werden - Transaktionen beziehen sich nicht auf OSIV. Es ist das
Session
, was offen bleibt, keine Transaktion - läuft.Wenn Ihre Anwendungsebenen auf mehrere Computer verteilt sind, können Sie OSIV so gut wie nicht verwenden. Sie müssen alles initialisieren, was Sie benötigen, bevor Sie das Objekt über das Kabel senden.
OSIV ist eine nette und transparente Möglichkeit (dh keiner Ihrer Codes weiß, dass dies geschieht), um die Leistungsvorteile des verzögerten Ladens zu nutzen
quelle
Ich würde nicht sagen, dass Open Session In View als schlechte Praxis angesehen wird. Was gibt dir diesen Eindruck?
Open-Session-In-View ist ein einfacher Ansatz zur Behandlung von Sitzungen mit Hibernate. Weil es einfach ist, ist es manchmal simpel. Wenn Sie eine differenzierte Kontrolle über Ihre Transaktionen benötigen, z. B. mehrere Transaktionen in einer Anfrage, ist Open-Session-In-View nicht immer ein guter Ansatz.
Wie andere bereits betont haben, gibt es einige Kompromisse bei OSIV - Sie sind viel anfälliger für das N + 1-Problem, da Sie weniger wahrscheinlich erkennen, welche Transaktionen Sie starten. Gleichzeitig bedeutet dies, dass Sie Ihre Service-Schicht nicht ändern müssen, um sich an geringfügige Änderungen in Ihrer Ansicht anzupassen.
quelle
Wenn Sie einen IoC-Container (Inversion of Control) wie Spring verwenden, sollten Sie sich über das Bean-Scoping informieren . Im Wesentlichen fordere ich Spring auf, mir ein Hibernate-
Session
Objekt zu geben, dessen Lebenszyklus die gesamte Anforderung umfasst (dh es wird zu Beginn und am Ende der HTTP-Anforderung erstellt und zerstört). Ich muss mich weder umLazyLoadException
s kümmern noch die Sitzung schließen, da der IoC-Container dies für mich verwaltet.Wie bereits erwähnt, müssen Sie über Leistungsprobleme bei N + 1 SELECT nachdenken. Sie können Ihre Hibernate-Entität danach jederzeit so konfigurieren, dass sie an Stellen, an denen die Leistung ein Problem darstellt, eifrig Join-Ladevorgänge ausführt.
Die Bohnen-Scoping-Lösung ist nicht federspezifisch. Ich weiß, dass PicoContainer die gleiche Funktion bietet, und ich bin sicher, dass andere ausgereifte IoC-Container etwas Ähnliches bieten.
quelle
Nach meiner eigenen Erfahrung ist OSIV nicht so schlecht. Die einzige Vereinbarung, die ich getroffen habe, besteht darin, zwei verschiedene Transaktionen zu verwenden: - die erste, die in der "Serviceschicht" geöffnet ist, wo ich die "Geschäftslogik" habe - die zweite, die unmittelbar vor dem Rendern der Ansicht geöffnet wurde
quelle
Ich habe gerade einen Beitrag über einige Richtlinien geschrieben, wann die offene Sitzung in meinem Blog angezeigt werden soll. Probieren Sie es aus, wenn Sie interessiert sind.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
quelle
Ich bin im Ruhezustand sehr verrostet. Aber ich denke, es ist möglich, mehrere Transaktionen in einer Ruhezustandssitzung durchzuführen. Ihre Transaktionsgrenzen müssen also nicht mit den Start / Stopp-Ereignissen der Sitzung identisch sein.
OSIV, imo, ist in erster Linie nützlich, weil wir vermeiden können, Code zum Starten eines 'Persistenzkontexts' (auch als Sitzung bezeichnet) zu schreiben, wenn die Anforderung einen DB-Zugriff benötigt.
In Ihrer Service-Schicht müssen Sie wahrscheinlich Methoden aufrufen, die unterschiedliche Transaktionsanforderungen haben, z. B. "Erforderlich, Neu Erforderlich usw.". Das einzige, was diese Methoden benötigen, ist, dass jemand (dh der OSIV-Filter) den Persistenzkontext gestartet hat, sodass sie sich nur um Folgendes kümmern müssen: "Hey, gib mir die Ruhezustandssitzung für diesen Thread. Ich muss einige tun DB Zeug ".
quelle
Dies wird nicht allzu viel helfen, aber Sie können mein Thema hier überprüfen: * Cache1 OutOfMemory mit OpenSessionInView in den Ruhezustand versetzen
Ich habe einige OutOfMemory-Probleme aufgrund von OpenSessionInView und vielen geladenen Entitäten, weil sie im Ruhezustand Cache Level1 bleiben und nicht durch Müll gesammelt werden (ich lade viele Entitäten mit 500 Elementen pro Seite, aber alle Entitäten bleiben im Cache).
quelle