Perché la Hibernate Open Session in View è considerata una ctriggers pratica?

E che tipo di strategie alternative usi per evitare LazyLoadExceptions?

Capisco che la sessione aperta in vista abbia problemi con:

  • Applicazioni a strati in esecuzione in diversi jvm
  • Le transazioni vengono eseguite solo alla fine e molto probabilmente ti piacerebbe prima i risultati.

Ma, se sai che la tua applicazione è in esecuzione su un singolo VM, perché non allevi il tuo dolore utilizzando una sessione aperta nella strategia di visualizzazione?

Poiché l’invio di proxy probabilmente non inizializzati, in particolare le raccolte, nel livello di visualizzazione e l’triggerszione del caricamento di ibernazione da lì può essere problematico dal punto di vista delle prestazioni e della comprensione.

Comprensione :

L’uso di OSIV “inquina” il livello di vista con preoccupazioni relative al livello di accesso ai dati.

Il livello di vista non è in grado di gestire una HibernateException che può verificarsi quando il caricamento è lazy, ma presumibilmente il livello di accesso ai dati lo è.

Prestazioni :

OSIV tende a tirare il carico dell’ quadro corretta sotto il tappeto: tendi a non notare che le tue collezioni o quadro sono inizializzate pigramente (forse N + 1). Più convenienza, meno controllo.


Aggiornamento: vedi l’antipattern OpenSessionInView per una discussione più ampia su questo argomento. L’autore elenca tre punti importanti:

  1. ogni inizializzazione pigra ti farà ottenere una query indicante che ogni quadro avrà bisogno di query N + 1, dove N è il numero di associazioni lazy. Se il tuo schermo presenta dati tabulari, leggere il diario di Hibernate è un grande indizio che non fai come dovresti
  2. questo sconfigge completamente l’architettura a strati, dal momento che le tue unghie con DB sono imbrattate nello strato di presentazione. Questo è un concetto concettuale, quindi potrei conviverci, ma c’è un corollario
  3. ultimo ma non meno importante, se si verifica un’eccezione durante il recupero della sessione, si verificherà durante la scrittura della pagina: non è ansible presentare una pagina di errore pulita all’utente e l’unica cosa che si può fare è scrivere un messaggio di errore nel corpo

Per una descrizione più lunga, è ansible leggere l’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 richiede un cattivo approccio al recupero dei dati. Invece di lasciare che il livello aziendale decida come recuperare tutte le associazioni necessarie per il livello View, impone che il contesto di persistenza rimanga aperto in modo che il livello View possa triggersre l’inizializzazione del proxy.

inserisci la descrizione dell'immagine qui

  • OpenSessionInViewFilter chiama il metodo openSession della SessionFactory sottostante e ottiene una nuova Session .
  • La Session è associata a TransactionSynchronizationManager .
  • OpenSessionInViewFilter chiama doFilter del riferimento all’object javax.servlet.FilterChain e la richiesta viene ulteriormente elaborata
  • Viene chiamato il DispatcherServlet e indirizza la richiesta HTTP al PostController sottostante.
  • PostController chiama PostService per ottenere un elenco di quadro Post .
  • PostService apre una nuova transazione e HibernateTransactionManager riutilizza la stessa Session aperta da OpenSessionInViewFilter .
  • PostDAO recupera l’elenco delle entity framework Post senza inizializzare alcuna associazione PostDAO .
  • PostService commette la transazione sottostante, ma la Session non viene chiusa perché è stata aperta esternamente.
  • DispatcherServlet avvia il rendering dell’interfaccia utente, che a sua volta naviga le associazioni pigre e triggers la loro inizializzazione.
  • OpenSessionInViewFilter può chiudere la Session e anche la connessione del database sottostante viene rilasciata.

Ad una prima occhiata, questa potrebbe non sembrare una cosa terribile da fare, ma, una volta vista dal punto di vista del database, una serie di difetti inizia a diventare più ovvia.

Il livello di servizio apre e chiude una transazione di database, ma in seguito non è in corso alcuna transazione esplicita. Per questo motivo, ogni ulteriore istruzione 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 sul disco, causando quindi un sacco di traffico I / O sul lato del database. Un’ottimizzazione consisterebbe nel contrassegnare Connection come di sola lettura che consentirebbe al server di database di evitare di scrivere nel log delle transazioni.

Non c’è più alcuna separazione di preoccupazioni perché le istruzioni sono generate sia dal livello di servizio che dal processo di rendering dell’interfaccia utente. Scrivere test di integrazione che affermano il numero di istruzioni generate richiede di passare attraverso 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 es. Jetty), questi test di integrazione saranno più lenti da eseguire rispetto a se i livelli fossero separati e i test di integrazione back-end utilizzassero il database, mentre il i test di integrazione front-end stavano prendendo in giro il livello di servizio.

Il livello dell’interfaccia utente è limitato alle associazioni di navigazione che possono, a loro volta, triggersre problemi di query N + 1. Sebbene Hibernate offra @BatchSize per il recupero delle 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 di utilizzo 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 tutta la fase di rendering dell’interfaccia utente (a seconda della modalità di rilascio della connessione) che aumenta il tempo di lease della connessione e limita il throughput complessivo della transazione dovuto alla congestione nel pool di connessioni del database. Più la connessione viene mantenuta, più altre richieste concorrenti attenderanno una connessione dal pool.

Quindi, se la connessione viene mantenuta troppo a lungo, o si acquisiscono / si rilasciano più connessioni per una singola richiesta HTTP, quindi si esercita una pressione sul pool di connessioni sottostante e si riduce la scalabilità.

Spring Boot

Sfortunatamente, Open Session in View è abilitato di default in Spring Boot .

Quindi, assicurati che nel file di configurazione application.properties , hai la seguente voce:

 spring.jpa.open-in-view=false 

Questo disabiliterà OSIV, in modo che tu possa gestire LazyInitializationException nel modo giusto .

  • le transazioni possono essere eseguite nel livello di servizio – le transazioni non sono correlate a OSIV. È la Session che rimane aperta, non una transazione: in esecuzione.

  • se i livelli dell’applicazione sono distribuiti su più macchine, non è ansible utilizzare OSIV: è necessario inizializzare tutto ciò che è necessario prima di inviare l’object sul filo.

  • OSIV è un modo simpatico e trasparente (ad esempio – nessuno del tuo codice è consapevole che ciò accada) per sfruttare i vantaggi prestazionali del caricamento lazy

Non direi che Open Session In View è considerato una ctriggers pratica; cosa ti dà quell’impressione?

Open-Session-In-View è un approccio semplice alla gestione delle sessioni con Hibernate. Perché è semplice, a volte è semplicistico. Se hai bisogno di un controllo preciso sulle tue transazioni, come avere più transazioni in una richiesta, Open-Session-In-View non è sempre un buon approccio.

Come altri hanno sottolineato, ci sono alcuni compromessi per OSIV: sei molto più incline al problema N + 1 perché hai meno probabilità di rendersi conto di quali transazioni stai dando il via. Allo stesso tempo, significa che non è necessario cambiare il livello di servizio per adattarsi a modifiche minori nella vista.

Se stai usando un contenitore di Inversion of Control (IoC) come Spring, potresti voler leggere su scope scope. In sostanza, sto dicendo a Spring di darmi un object Hibernate Session cui ciclo di vita copre l’intera richiesta (cioè, viene creato e distrutto all’inizio e alla fine della richiesta HTTP). Non devo preoccuparmi di LazyLoadException s né chiudere la sessione poiché il contenitore IoC lo gestisce per me.

Come accennato, dovrai pensare a N + 1 SELECT problemi di prestazioni. È sempre ansible configurare l’entity framework di Hibernate in un secondo momento per eseguire il caricamento di join ansioso in luoghi in cui le prestazioni rappresentano un problema.

La soluzione di scoping dei bean non è specifica per Spring. So che PicoContainer offre le stesse funzionalità e sono certo che altri contenitori IoC maturi offrono qualcosa di simile.

Nella mia esperienza personale, OSIV non è così male. L’unico accordo che ho fatto è stato l’utilizzo di due diverse transazioni: – la prima, aperta in “livello di servizio”, in cui ho la “logica aziendale” – la seconda aperta appena prima del rendering della vista

Ho appena fatto un post su alcune linee guida su quando utilizzare la sessione aperta in vista nel mio blog. Controllalo se sei interessato.

http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/

Sono v. Arrugginito su Hibernate .. ma penso che sia ansible avere più transazioni in una sessione di Hibernate. Quindi i tuoi limiti di transazione non devono essere gli stessi degli eventi di inizio / arresto della sessione.

OSIV, imo, è utile soprattutto perché possiamo evitare di scrivere codice per avviare un “contesto di persistenza” (ovvero una sessione) ogni volta che la richiesta deve effettuare un accesso al DB.

Nel tuo livello di servizio, probabilmente dovrai effettuare chiamate a metodi che hanno esigenze di transazione diverse, ad esempio “Richiesto, Nuovo richiesto, ecc.” L’unica cosa di cui hanno bisogno questi metodi è che qualcuno (cioè il filtro OSIV) abbia avviato il contesto di persistenza, in modo che l’unica cosa di cui devono preoccuparsi sia: “hey dammi la sessione di ibernazione per questa discussione .. Devo fare qualcosa Roba di DB “.

Questo non aiuterà troppo ma puoi controllare il mio argomento qui: * Hibernate Cache1 OutOfMemory con OpenSessionInView

Ho alcuni problemi di OutOfMemory a causa di OpenSessionInView e di molte quadro caricate, perché rimangono in Hibernate cache level1 e non sono garbage collection (carico un sacco di quadro con 500 elementi per pagina, ma tutte le quadro restano nella cache)