Per una descrizione più lunga, puoi leggere il mio articolo Open Session In View Anti-Pattern . Altrimenti, ecco un riepilogo del motivo per cui non dovresti usare Open Session In View.
Open Session In View ha un approccio sbagliato per il recupero dei dati. Invece di lasciare che il livello aziendale decida come è meglio recuperare tutte le associazioni necessarie dal livello di visualizzazione, forza il contesto di persistenza a rimanere aperto in modo che il livello di visualizzazione possa attivare l'inizializzazione del proxy.
- Il
OpenSessionInViewFilter
chiama il openSession
metodo del sottostante SessionFactory
e ottiene una nuova Session
.
- Il
Session
è legato al TransactionSynchronizationManager
.
- Le
OpenSessionInViewFilter
chiamate doFilter
del javax.servlet.FilterChain
riferimento all'oggetto e la richiesta viene ulteriormente elaborata
- La
DispatcherServlet
si chiama, e instrada la richiesta HTTP al sottostante PostController
.
- Le
PostController
chiamate i PostService
per ottenere una lista di Post
entità.
- La
PostService
apre una nuova transazione, e lo HibernateTransactionManager
riutilizza lo stesso Session
che è stato aperto dal OpenSessionInViewFilter
.
- Il
PostDAO
recupera l'elenco delle Post
entità senza inizializzare alcuna associazione pigra.
- Il
PostService
commit della transazione sottostante, ma Session
non è chiuso perché è stato aperto esternamente.
- Le
DispatcherServlet
partenze di rendering dell'interfaccia utente, che, a sua volta, naviga le associazioni pigri e fa scattare la loro inizializzazione.
- Il
OpenSessionInViewFilter
grado di chiudere la Session
, e la connessione al database sottostante è rilasciato come pure.
A prima vista, questa potrebbe non sembrare una cosa terribile da fare, ma, una volta visualizzata dalla prospettiva di un database, una serie di difetti iniziano a diventare più evidenti.
Il livello di servizio apre e chiude una transazione di database, ma in seguito non è in corso alcuna transazione esplicita. Per questo motivo, ogni istruzione aggiuntiva emessa dalla fase di rendering dell'interfaccia utente viene eseguita in modalità di commit automatico. Il commit automatico mette sotto pressione il server del database perché ogni istruzione deve scaricare il log delle transazioni su disco, causando quindi molto traffico di I / O sul lato database. Un'ottimizzazione sarebbe contrassegnare Connection
come di sola lettura, il che consentirebbe al server database di evitare la scrittura nel registro delle transazioni.
Non c'è più separazione delle preoccupazioni perché le istruzioni vengono generate sia dal livello di servizio che dal processo di rendering dell'interfaccia utente. La scrittura di test di integrazione che affermano il numero di istruzioni generate richiede il passaggio a tutti i livelli (Web, servizio, DAO), mentre l'applicazione viene distribuita su un contenitore Web. Anche quando si utilizza un database in memoria (ad esempio HSQLDB) e un server web leggero (ad esempio Jetty), questi test di integrazione saranno più lenti da eseguire rispetto a se i livelli fossero separati e i test di integrazione back-end usassero il database, mentre il i test di integrazione front-end deridevano del tutto il livello di servizio.
Il livello dell'interfaccia utente è limitato alla navigazione nelle associazioni che possono, a loro volta, attivare N + 1 problemi di query. Sebbene Hibernate offra il @BatchSize
recupero di associazioni in batch e FetchMode.SUBSELECT
per far fronte a questo scenario, le annotazioni influenzano il piano di recupero predefinito, quindi vengono applicate a ogni caso d'uso aziendale. Per questo motivo, una query del livello di accesso ai dati è molto più adatta perché può essere adattata ai requisiti di recupero dei dati del caso d'uso corrente.
Ultimo ma non meno importante, la connessione al database potrebbe essere mantenuta durante la fase di rendering dell'interfaccia utente (a seconda della modalità di rilascio della connessione), il che aumenta il tempo di lease della connessione e limita il throughput complessivo della transazione a causa della congestione nel pool di connessioni del database. Più la connessione viene mantenuta, più altre richieste simultanee attenderanno per ottenere una connessione dal pool.
Quindi, o si mantiene la connessione troppo a lungo, si acquisiscono / rilasciano più connessioni per una singola richiesta HTTP, mettendo quindi sotto pressione il pool di connessioni sottostante e limitando la scalabilità.
Spring Boot
Sfortunatamente, l' opzione Apri sessione in visualizzazione è abilitata per impostazione predefinita in Spring Boot .
Quindi, assicurati application.properties
di avere la seguente voce nel file di configurazione:
spring.jpa.open-in-view=false
Questo disabiliterà OSIV, in modo che tu possa gestirlo nel LazyInitializationException
modo giusto .