Uso corretto di flush () in JPA / Hibernate


110

Stavo raccogliendo informazioni sul metodo flush (), ma non sono abbastanza chiaro quando usarlo e come usarlo correttamente. Da quello che ho letto, la mia comprensione è che il contenuto del contesto di persistenza verrà sincronizzato con il database, ovvero l'emissione di dichiarazioni in sospeso o l'aggiornamento dei dati dell'entità.

Ora ho ottenuto il seguente scenario con due entità Ae B(in una relazione uno-a-uno, ma non applicato o modellato da JPA). Aha una PK composita, che viene impostata manualmente, e ha anche un campo IDENTITY generato automaticamente recordId. Questo recordIddovrebbe essere scritto ad un'entità Bcome chiave esterna a A. Sto risparmiando Ae Bin un'unica transazione. Il problema è che il valore generato automaticamente A.recordIdnon è disponibile all'interno della transazione, a meno che non faccio una chiamata esplicita di em.flush()dopo la chiamata em.persist()su A. (Se ho un IDENTITY PK generato automaticamente, il valore viene aggiornato direttamente nell'entità, ma non è questo il caso qui.)

Può em.flush()causare danni quando lo si utilizza all'interno di una transazione?

Risposte:


148

Probabilmente i dettagli esatti em.flush()dipendono dall'implementazione. In generale, comunque, i provider JPA come Hibernate possono memorizzare nella cache le istruzioni SQL che dovrebbero inviare al database, spesso fino a quando non effettui il commit della transazione. Ad esempio, se chiami em.persist(), Hibernate ricorda che deve creare un database INSERT, ma non esegue effettivamente l'istruzione finché non effettui il commit della transazione. Afaik, questo viene fatto principalmente per motivi di prestazioni.

In alcuni casi, comunque, si desidera che le istruzioni SQL vengano eseguite immediatamente; generalmente quando è necessario il risultato di alcuni effetti collaterali, come una chiave generata automaticamente o un trigger di database.

Quello che em.flush()fa è svuotare la cache delle istruzioni SQL interna ed eseguirla immediatamente nel database.

In conclusione: non viene fatto alcun danno, solo tu potresti avere un (minore) calo delle prestazioni poiché stai sovrascrivendo le decisioni del provider JPA per quanto riguarda il momento migliore per inviare istruzioni SQL al database.


1
Cosa ha detto. Il comportamento di em.flush () fa eco a java.io.Flushable.flush () dove tutti i dati memorizzati nel buffer vengono inviati a qualsiasi destinazione sia appropriata.
Erik

4
se flush () invia i dati al database? cosa succede se viene generata un'eccezione dopo? Il gestore entità ripristinerà tutto? anche i dati scritti nel primo flush?
Jaime Hablutzel

101
flush () invia istruzioni SQL al database come INSERT, UPDATE ecc. Non invierà un COMMIT, quindi se hai un'eccezione dopo un flush (), puoi comunque avere un rollback completo.
Flavio

17
È possibile eseguire il rollback del DB, ma non ripristinerà alcuna modifica agli oggetti, ad esempio, "versione" incrementata automaticamente, ID generato automaticamente, ecc. Inoltre, il gestore entità verrà chiuso dopo un rollback. Fai solo attenzione che se provi a "unire" l'oggetto a un'altra sessione, la "versione" incrementata automaticamente in particolare potrebbe causare un'eccezione OptimisticLockException.
Peter Davis

11
Oltre a innescare effetti collaterali, un altro motivo per utilizzare flush () è se si desidera essere in grado di leggere gli effetti di un'operazione nel database utilizzando JPQL / HQL (ad esempio in un test). JPA non può utilizzare i dati memorizzati nella cache durante l'esecuzione di queste query, quindi verranno letti solo gli elementi effettivamente presenti nel database.
sleske

2

In realtà, em.flush()fai di più che inviare semplicemente i comandi SQL memorizzati nella cache. Prova a sincronizzare il contesto di persistenza con il database sottostante. Può causare un notevole dispendio di tempo nei processi se la cache contiene raccolte da sincronizzare.

Attenzione nell'usarlo.


1

Em.flush () può causare danni quando viene utilizzato all'interno di una transazione?

Sì, potrebbe mantenere i blocchi nel database per una durata maggiore del necessario.

In generale, quando si utilizza JPA si delega la gestione delle transazioni al contenitore (noto anche come CMT - utilizzando l'annotazione @Transactional sui metodi aziendali), il che significa che una transazione viene avviata automaticamente quando si immette il metodo e alla fine viene confermata / annullata. Se si lascia che EntityManager gestisca la sincronizzazione del database, l'esecuzione delle istruzioni sql verrà attivata solo subito prima del commit, portando a blocchi di breve durata nel database. In caso contrario, le operazioni di scrittura scaricate manualmente potrebbero mantenere blocchi tra lo scaricamento manuale e il commit automatico che possono essere lunghi in base al tempo di esecuzione rimanente del metodo.

Rileva che alcune operazioni attivano automaticamente un flush: l'esecuzione di una query nativa sulla stessa sessione (lo stato EM deve essere scaricato per essere raggiungibile dalla query SQL), inserendo entità utilizzando l'id generato nativo (generato dal database, quindi l'istruzione insert deve essere attivato così l'EM è in grado di recuperare l'id generato e gestire adeguatamente le relazioni)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.