CodeGym /Kursy /Hibernate deep-dive /Tryby flush w Hibernate

Tryby flush w Hibernate

Hibernate deep-dive
Poziom 4, Lekcja 2
Dostępny

1. Flush mode: autozapis dla bazy

Jeśli flush — to sam fakt synchronizacji, to flush mode — to polityka, która odpowiada na pytanie: „A kiedy ty, Hibernate, w ogóle masz prawo lub obowiązek zrobić flush automatycznie?" To jak ustawienia autozapisu w edytorze tekstu: można zapisywać co N minut, można tylko przy zamykaniu, można wyłączyć autozapis i wciskać Ctrl+S ręcznie, a można ustawić tryb „zapisuj po każdym kichnięciu" — i szybko znienawidzić życie.

Ważny niuans: flush mode nie anuluje dirty checking i nie zmienia stanu encji. Twój Product jak był managed i „brudny", tak nim zostanie. Zmienia się tylko kiedy Hibernate zdecyduje „przetłumaczyć brud na SQL" automatycznie.

flowchart TD
    A[Zmieniłeś managed-entity] --> B[dirty checking zaznacza zmiany]
    B --> C{"flush mode decyduje kiedy flush jest dozwolony/wymagany"}
    C --> D[flush wysyła SQL]
    D --> E[commit/rollback kończy transakcję]

2. Tryby JPA i Hibernate

W świecie JPA pracujesz z jakarta.persistence.FlushModeType, i tam są tylko dwa tryby: AUTO i COMMIT. W świecie Hibernate jest org.hibernate.FlushMode, gdzie trybów jest więcej: AUTO, COMMIT, MANUAL, ALWAYS.

Poziom API Enum Dostępne tryby Co to oznacza w dwóch słowach
JPA FlushModeType AUTO, COMMIT Standardowe zachowanie i „odłóż do commitu"
Hibernate FlushMode AUTO, COMMIT, MANUAL, ALWAYS To samo + „tylko ręcznie" + „przed każdym zapytaniem"

3. AUTO: tryb domyślny

AUTO — podstawowa polityka. Hibernate robi flush przed commit i może zrobić go wcześniej, jeśli inaczej zapytanie wewnątrz transakcji zobaczyłoby przestarzały stan względem już nagromadzonych zmian. Sens AUTO nie w tym, żeby „pisać częściej", lecz żeby nie kłamać zapytaniom wewnątrz bieżącego unit of work.

4. COMMIT: flush na końcu transakcji

Tryb COMMIT brzmi jak dobra optymalizacja: „bez wczesnych UPDATE, zrób wszystko na końcu". I co do zasady tak jest: Hibernate stara się nie robić automatycznego flush przed zapytaniami i odkłada synchronizację do momentu commit. Ale zapłata jest oczywista: zapytania wewnątrz transakcji mogą działać tak, jakby twoich zmian „jeszcze nie było".

import jakarta.persistence.EntityManager;
import jakarta.persistence.FlushModeType;

entityManager.setFlushMode(FlushModeType.COMMIT);

Product p = entityManager.find(Product.class, 1L); // managed-encja
p.setName("Phone Pro (commit)");

Long count = entityManager.createQuery(
        "select count(p) from Product p where p.name = :name", Long.class)
    .setParameter("name", "Phone Pro (commit)")
    .getSingleResult();

System.out.println("count = " + count); // często będzie 0, bo UPDATE jeszcze nie poszedł

5. MANUAL: flush tylko ręcznie

Tryb MANUAL — to gdy wyłączasz autozapis i mówisz Hibernate: „Nic sam nie wysyłasz. Chcesz SQL — zadzwonię". To tryb specyficzny dla Hibernate, nie ma go w JPA. Najuczciwszy ze wszystkich: bez jawnego flush() SQL nie pójdzie.

import jakarta.persistence.EntityManager;
import org.hibernate.FlushMode;
import org.hibernate.Session;

Session session = entityManager.unwrap(Session.class);
session.setHibernateFlushMode(FlushMode.MANUAL); // wyłączamy auto-flush

Product p = entityManager.find(Product.class, 1L);
p.setName("Phone Pro (manual)");

// W MANUAL nie liczymy ani na flush przed zapytaniami, ani na flush przy commit.
// Jeśli potrzebna synchronizacja — wywołujemy flush() jawnie.
session.flush();

6. ALWAYS: flush przed każdym zapytaniem

ALWAYS — to odwrotność COMMIT. Przed dowolnym zapytaniem — upewnij się, że baza jest zsynchronizowana z kontekstem. Na poziomie poprawności — tak. Na poziomie wydajności i nieoczekiwanych efektów ubocznych — czasem katastrofa.

import jakarta.persistence.EntityManager;
import org.hibernate.FlushMode;
import org.hibernate.Session;

Session session = entityManager.unwrap(Session.class);
session.setHibernateFlushMode(FlushMode.ALWAYS); // flush przed KAŻDYM zapytaniem

Product p = entityManager.find(Product.class, 1L);
p.setName("Phone Pro (always)");

entityManager.createQuery("select c from Customer c", Customer.class)
    .getResultList(); // w ALWAYS flush nastąpi przed tym zapytaniem, nawet jeśli dotyczy Customer

7. Konfiguracja flush mode w kodzie

Najczęstszy błąd inżynierski z flush mode — nie „pomylić AUTO i COMMIT", lecz ustawiać tryb zbyt szeroko. Dlatego rozumienie, gdzie można go zadawać i jaki ma „promień rażenia" — ważne.

Wygodne podejście — ustawianie trybu flush na konkretnym zapytaniu, żeby nie zmieniać polityki całego kontekstu:

import jakarta.persistence.FlushModeType;
import java.util.List;

List<Product> products = entityManager.createQuery(
        "select p from Product p", Product.class)
    .setFlushMode(FlushModeType.COMMIT)
    .getResultList();

8. Przykład: jedna zmiana i count

Tryb Flush przed zapytaniem count Co zobaczy count Główne odczucie
AUTO Zwykle tak „Jakby zmiany są już w bazie" Poprawnie, ale czasem nieoczekiwanie wcześnie
COMMIT Zwykle nie „Jakby zmian jeszcze nie ma" Ciszej, ale możliwe dziwne wyniki pośrednie
MANUAL Nie (tylko ręcznie) „Jakby zmian nie ma, dopóki nie flush()" Maksymalna kontrola, maksymalna odpowiedzialność
ALWAYS Tak „Zawsze świeże" Kosztowne i może irytować liczbą flush

9. Typowe błędy przy pracy z flush modes

Błąd nr 1: postrzeganie COMMIT jako „bezpiecznego przyspieszenia".
Jeśli wewnątrz transakcji są zapytania agregujące, możesz dostać wyniki sprzeczne z tym, co właśnie zrobiłeś w kodzie.

Błąd nr 2: włączenie MANUAL i zapomnienie, że flush nie następuje już sam.
MANUAL łatwo zamienia zmiany w „brudnopis", który takim i pozostanie. Obiekty wyglądają poprawnie w pamięci, a baza żyje własnym życiem.

Błąd nr 3: oczekiwanie, że flush mode zmienia dirty checking lub stan encji.
Flush mode — to nie „tryb zapisu encji". Nie robi encji detached, nie zamienia managed w transient i nie anuluje snapshotów. Jeśli encja jest managed i zmieniłeś pole, dirty checking i tak to zauważy.

Błąd nr 4: włączenie ALWAYS i dziwienie się, że „nawet odczyt" prowokuje zapisy.
ALWAYS robi flush przed każdym zapytaniem. Czytasz Customer, a aktualizuje się Product. Nie hałas — to bardzo restrykcyjny tryb, który sam włączyłeś.

Błąd nr 5: zmienianie flush mode zbyt szeroko i na zbyt długo.
Gdy tryb jest ustawiany na poziomie EntityManager/Session i zapominasz przywrócić poprzednią wartość, zachowanie aplikacji staje się „zależne od historii wykonania kodu". Dziś ta metoda wywołana pierwsza — wszystko OK, jutro druga — SQL idzie inaczej.

1
Zadanie
Hibernate deep-dive, poziom 4, lekcja 2
Niedostępne
Zapytanie w trybie COMMIT i stary stan bazy
Zapytanie w trybie COMMIT i stary stan bazy
1
Zadanie
Hibernate deep-dive, poziom 4, lekcja 2
Niedostępne
Tryb MANUAL i dwa zapytania przed i po flush
Tryb MANUAL i dwa zapytania przed i po flush
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION