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.
- Der
OpenSessionInViewFilter
ruft die openSession
Methode des Basiswerts auf SessionFactory
und erhält eine neue Session
.
- Das
Session
ist an das gebunden TransactionSynchronizationManager
.
- Das
OpenSessionInViewFilter
ruft die doFilter
die javax.servlet.FilterChain
Objektreferenz und die Anforderung wird weiter verarbeitet
- Das
DispatcherServlet
wird aufgerufen und leitet die HTTP-Anforderung an den Basiswert weiter PostController
.
- Das
PostController
ruft das PostService
auf, um eine Liste der Post
Entitäten zu erhalten.
- Das
PostService
öffnet eine neue Transaktion und das HibernateTransactionManager
wiederverwendet das gleiche Session
, das von der geöffnet wurde OpenSessionInViewFilter
.
- Der
PostDAO
ruft die Liste der Post
Entitäten ab, ohne eine verzögerte Zuordnung zu initialisieren.
- Das
PostService
schreibt die zugrunde liegende Transaktion fest, wird jedoch Session
nicht geschlossen, da sie extern geöffnet wurde.
- Das
DispatcherServlet
beginnt mit dem Rendern der Benutzeroberfläche, die wiederum durch die verzögerten Zuordnungen navigiert und deren Initialisierung auslöst.
- Das
OpenSessionInViewFilter
kann 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 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 und FetchMode.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 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.properties
Konfigurationsdatei den folgenden Eintrag haben:
spring.jpa.open-in-view=false
Dies wird deaktivieren OSIV , so dass Sie den Griff , LazyInitializationException
den 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 .