Prawidłowe użycie flush () w JPA / Hibernate

111

Zbierałem informacje o metodzie flush (), ale nie jestem do końca jasne, kiedy jej używać i jak używać jej poprawnie. Z tego, co przeczytałem, rozumiem, że zawartość kontekstu trwałości będzie synchronizowana z bazą danych, tj. Wydawanie zaległych oświadczeń lub odświeżanie danych encji.

Teraz mam następujący scenariusz z dwoma podmiotami Ai B(w relacji jeden do jednego, ale nie wymuszony ani modelowany przez JPA). Ama złożoną PK, która jest ustawiana ręcznie, a także ma automatycznie generowane pole IDENTITY recordId. To recordIdpowinno być napisane do podmiotu Bjako obcego-klucz do A. Oszczędzam Ai to Bw jednej transakcji. Problem polega na tym, że wartość generowane automatycznie A.recordIdnie są dostępne w ramach transakcji, chyba że zrobić wyraźne wezwanie em.flush()po wywołaniu em.persist()na A. (Jeśli mam automatycznie wygenerowany identyfikator PK IDENTITY, wartość jest bezpośrednio aktualizowana w encji, ale tak nie jest w tym przypadku).

Może em.flush()wyrządzić jakąś szkodę podczas używania go w ramach transakcji?

MicSim
źródło

Odpowiedzi:

150

Prawdopodobnie dokładne szczegóły em.flush()zależą od implementacji. Ogólnie rzecz biorąc, dostawcy JPA, tacy jak Hibernate, mogą buforować instrukcje SQL, które mają wysłać do bazy danych, często do momentu faktycznego zatwierdzenia transakcji. Na przykład, dzwonisz em.persist(), Hibernate pamięta, że ​​musi wykonać bazę danych INSERT, ale w rzeczywistości nie wykonuje instrukcji, dopóki nie zatwierdzisz transakcji. Afaik, odbywa się to głównie ze względu na wydajność.

W niektórych przypadkach chcesz, aby instrukcje SQL były wykonywane natychmiast; zwykle, gdy potrzebujesz wyniku niektórych efektów ubocznych, takich jak automatycznie wygenerowany klucz lub wyzwalacz bazy danych.

To, co em.flush()robi, to opróżnienie wewnętrznej pamięci podręcznej instrukcji SQL i natychmiastowe wykonanie jej w bazie danych.

Podsumowując: nic się nie dzieje, tylko możesz mieć (niewielki) spadek wydajności, ponieważ unieważniasz decyzje dostawcy JPA dotyczące najlepszego czasu wysyłania instrukcji SQL do bazy danych.

Flavio
źródło
1
Co on powiedział. Zachowanie em.flush () przypomina java.io.Flushable.flush (), gdzie wszystkie buforowane dane są wysyłane do dowolnego miejsca docelowego.
Erik
4
czy flush () wysyła dane do bazy danych? co się stanie, jeśli po tym zostanie zgłoszony wyjątek? Czy kierownik jednostki wycofa wszystko? nawet dane zapisane w pierwszym rzucie?
Jaime Hablutzel
101
flush () wysyła instrukcje SQL do bazy danych, takie jak INSERT, UPDATE itp. Nie wyśle ​​polecenia COMMIT, więc jeśli po flush () wystąpi wyjątek, nadal można uzyskać pełne wycofanie.
Flavio
17
Możesz wycofać bazę danych, ale nie cofnie to żadnych zmian w obiektach, np. Automatycznie zwiększonej „wersji”, automatycznie wygenerowanego identyfikatora itp. Ponadto menedżer encji zostanie zamknięty po wycofaniu. Tylko uważaj, jeśli spróbujesz „scalić” obiekt z inną sesją, w szczególności automatycznie zwiększona „wersja” może spowodować wyjątek OptimisticLockException.
Peter Davis
11
Oprócz wywoływania efektów ubocznych, innym powodem używania flush () jest chęć odczytania efektów operacji w bazie danych za pomocą JPQL / HQL (np. W teście). JPA nie może używać danych z pamięci podręcznej podczas wykonywania tych zapytań, więc odczytane zostaną tylko rzeczy znajdujące się w bazie danych.
sleske
2

W rzeczywistości em.flush()rób więcej niż tylko wysyłanie buforowanych poleceń SQL. Próbuje zsynchronizować kontekst trwałości z bazową bazą danych. Może to spowodować duże zużycie czasu w procesach, jeśli pamięć podręczna zawiera kolekcje do synchronizacji.

Ostrożność podczas korzystania z niego.

Ricardo Nakashima
źródło
2

Czy em.flush () może spowodować jakąkolwiek szkodę podczas używania jej w ramach transakcji?

Tak, może utrzymywać blokady w bazie danych dłużej niż to konieczne.

Ogólnie rzecz biorąc, podczas korzystania z JPA delegujesz zarządzanie transakcjami do kontenera (aka CMT - przy użyciu adnotacji @Transactional w metodach biznesowych), co oznacza, że ​​transakcja jest automatycznie uruchamiana po wprowadzeniu metody i zatwierdzana / wycofywana na końcu. Jeśli pozwolisz EntityManagerowi obsłużyć synchronizację bazy danych, wykonanie instrukcji sql zostanie wyzwolone tylko tuż przed zatwierdzeniem, co doprowadzi do krótkotrwałych blokad w bazie danych. W przeciwnym razie ręcznie przepłukane operacje zapisu mogą zachować blokady między ręcznym opróżnieniem a automatycznym zatwierdzeniem, które mogą być długie w zależności od pozostałego czasu wykonania metody.

Zauważa, że ​​niektóre operacje automatycznie wyzwalają opróżnianie: wykonanie natywnego zapytania w tej samej sesji (stan EM musi zostać opróżniony, aby było osiągalne przez zapytanie SQL), wstawianie jednostek przy użyciu natywnego generowanego identyfikatora (generowanego przez bazę danych, więc instrukcja insert musi być wyzwalane, dzięki czemu EM jest w stanie pobrać wygenerowany identyfikator i odpowiednio zarządzać relacjami)

Gadanina
źródło