Was ist diese spring.jpa.open-in-view = true-Eigenschaft in Spring Boot?

119

Ich habe spring.jpa.open-in-view=truein der Spring Boot-Dokumentation eine Eigenschaft für die JPA-Konfiguration gesehen.

  • Ist der trueStandardwert für diese Eigenschaft, wenn er überhaupt nicht angegeben ist?;
  • Was macht das wirklich? Ich fand keine gute Erklärung dafür;
  • Verwendet es Sie SessionFactoryanstelle von EntityManagerFactory? Wenn ja, wie kann ich festlegen, dass ich EntityManagerFactorystattdessen verwenden darf ?

Vielen Dank!

Carlos Alberto
quelle

Antworten:

51

Diese Eigenschaft registriert eine OpenEntityManagerInViewInterceptor, die eine EntityManagerfür den aktuellen Thread registriert , sodass Sie dieselbe haben, EntityManagerbis die Webanforderung abgeschlossen ist. Es hat nichts mit einem Ruhezustand SessionFactoryusw. zu tun .

dunni
quelle
Im Moment habe ich den Filter OpenEntityManagerInViewFilter, um den EntityManager zu steuern, bis die Webanforderung abgeschlossen ist. Dieser Interceptor, den Sie mit "OpenEntityManagerInViewInterceptor" gemeint haben, ist derselbe wie "OpenEntityManagerInViewFilter"? Was ist der Unterschied zwischen ihnen? Also hätte ich nicht mehr diesen Filter in meinem Servlet-Kontext für Spring Boot?
Carlos Alberto
1
Der Interceptor funktioniert nur, wenn Sie das DispatcherServlet in Spring verwenden (da der Interceptor ein Spring-Mechanismus ist). Der Filter kann allen konfigurierten Servlets zugeordnet werden (wir verwenden ihn für das FacesServlet in einer unserer Anwendungen). Wenn Sie also nur das DispatcherServlet verwenden, können Sie die Eigenschaft hinzufügen und den Filter entfernen. Andernfalls verwenden Sie den Filter.
Dunni
295

Das OSIV-Anti-Pattern

Anstatt die Business-Schicht entscheiden zu lassen, wie alle Zuordnungen, die von der View-Schicht benötigt werden, am besten abgerufen werden, erzwingt OSIV (Open Session in View), dass der Persistenzkontext geöffnet bleibt, damit die View-Schicht die Proxy-Initialisierung auslösen kann, wie dargestellt durch das folgende Diagramm.

Geben Sie hier die Bildbeschreibung ein

  • Der OpenSessionInViewFilterruft die openSessionMethode des Basiswerts auf SessionFactoryund erhält eine neue Session.
  • Das Sessionist an das gebunden TransactionSynchronizationManager.
  • Das OpenSessionInViewFilterruft die doFilterdie javax.servlet.FilterChainObjektreferenz und die Anforderung wird weiter verarbeitet
  • Das DispatcherServletwird aufgerufen und leitet die HTTP-Anforderung an den Basiswert weiter PostController.
  • Das PostControllerruft das PostServiceauf, um eine Liste der PostEntitäten zu erhalten.
  • Das PostServiceöffnet eine neue Transaktion und das HibernateTransactionManagerwiederverwendet das gleiche Session, das von der geöffnet wurde OpenSessionInViewFilter.
  • Der PostDAOruft die Liste der PostEntitäten ab, ohne eine verzögerte Zuordnung zu initialisieren.
  • Das PostServiceschreibt die zugrunde liegende Transaktion fest, wird jedoch Sessionnicht geschlossen, da sie extern geöffnet wurde.
  • Das DispatcherServletbeginnt mit dem Rendern der Benutzeroberfläche, die wiederum durch die verzögerten Zuordnungen navigiert und deren Initialisierung auslöst.
  • Das OpenSessionInViewFilterkann die schließen Session, 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 Connectionals 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 @BatchSizezum Abrufen von Zuordnungen in Stapeln und FetchMode.SUBSELECTzur 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 wird die Datenbankverbindung während der gesamten UI-Rendering-Phase gehalten, 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.

Spring Boot und OSIV

Leider ist OSIV (Open Session in View) in Spring Boot standardmäßig aktiviert , und OSIV ist aus Sicht der Leistung und Skalierbarkeit eine wirklich schlechte Idee .

Stellen Sie also sicher, dass Sie in der application.propertiesKonfigurationsdatei den folgenden Eintrag haben:

spring.jpa.open-in-view=false

Dies wird deaktivieren OSIV , so dass Sie den Griff , LazyInitializationExceptionden richtigen Weg .

Ab Version 2.0 gibt Spring Boot eine Warnung aus, wenn OSIV standardmäßig aktiviert ist, sodass Sie dieses Problem erkennen können, lange bevor es ein Produktionssystem betrifft.

Weitere Informationen zu OSIV finden Sie in diesem Artikel .

Vlad Mihalcea
quelle
14
Heutzutage wird eine WARNUNG protokolliert.
Vlad Mihalcea
Gilt dies für Spring im Allgemeinen oder nur für Spring Boot? Kann dies über eine mit @ Configuration annotierte Klasse deaktiviert werden, anstatt eine Eigenschaft festzulegen?
Gordon
2
Dies gilt nur für Spring Boot. In Standard Spring wählen Sie explizit aus, welche Beans verwendet werden sollen oder ob Sie einen Webfilter wie OSIV möchten. Ich weiß nicht, ob Sie es über eine Anmerkung deaktivieren können. Ich kenne nur die Konfigurationseinstellung.
Vlad Mihalcea
Es ist kein Anti-Muster. Es hat Auswirkungen auf die Leistung, manchmal negativ, oft recht neutral und in vielen Fällen positiv: Wenn Sie zunächst eine faule Beziehung wünschen, müssen Sie die Abfrage nicht in allen Fällen durchführen und kann es bei Bedarf vermeiden, indem Open-in-View verwendet wird.
Ymajoros
5
Laut Wikipedia ist "ein Anti-Muster eine häufige Antwort auf ein wiederkehrendes Problem, das normalerweise unwirksam ist und das Risiko birgt, sehr kontraproduktiv zu sein". Genau das ist Open Session in View.
Vlad Mihalcea