Gymterview
middle

Что такое Dirty Checking и Flush?

Dirty Checking — механизм Hibernate, автоматически отслеживающий изменения Persistent-сущностей. При flush Hibernate сравнивает текущее состояние объекта с его снимком (snapshot), сделанным при загрузке, и генерирует UPDATE для изменённых полей.

Пример
@Transactional
public void updateUser(Long id) {
    User user = entityManager.find(User.class, id);  // загрузка + snapshot
    user.setName("New Name");  // изменение в памяти
    // НЕ нужен вызов save() или update()!
    // При commit Hibernate обнаружит изменение и выполнит UPDATE
}

Flush

Flush — процесс синхронизации Persistence Context с базой данных. При flush все накопленные изменения (INSERT, UPDATE, DELETE) отправляются в БД.

Когда происходит flush:

  1. При commit() транзакции (автоматически)
  2. Перед выполнением JPQL/HQL-запроса (чтобы запрос видел актуальные данные)
  3. При явном вызове entityManager.flush()

FlushMode

Пример
// AUTO (по умолчанию) — flush перед запросами и при commit
entityManager.setFlushMode(FlushModeType.AUTO);

// COMMIT — flush только при commit (не перед запросами)
entityManager.setFlushMode(FlushModeType.COMMIT);

Порядок операций при flush

  1. INSERT (новые сущности — persist())
  2. UPDATE (изменённые сущности — Dirty Checking)
  3. DELETE коллекций
  4. INSERT/UPDATE коллекций
  5. DELETE (удалённые сущности — remove())

Важное

  • Dirty Checking автоматический — изменили поле Persistent-объекта → UPDATE при flush
  • Flush =/= commit; flush отправляет SQL, commit фиксирует транзакцию
  • @DynamicUpdate — генерирует UPDATE только для изменённых столбцов (по умолчанию — все столбцы)
  • @Transactional(readOnly = true) — подсказка Hibernate отключить Dirty Checking (оптимизация)

Частые ошибки

  • Вызов save() для уже managed-сущности — Spring Data save() вызовет merge(), создав лишнюю копию; для managed-сущностей достаточно изменить поля
  • Неожиданный UPDATE — изменили поле managed-объекта случайно → Hibernate выполнит UPDATE при flush
  • Порядок flush — DELETE выполняется после INSERT/UPDATE; это может нарушить ограничения уникальности
  • Flush в цикле — entityManager.flush() в цикле может привести к деградации производительности

Как используется в 2026

  • Dirty Checking — фундаментальный механизм, работает прозрачно
  • @DynamicUpdate рекомендуется для таблиц с большим количеством столбцов
  • readOnly = true — обязательный паттерн для read-only транзакций (экономит память и CPU)

На собеседовании: объясните, что не нужен явный save() для managed-сущностей — Hibernate сам обнаружит изменения через снимок (snapshot) при загрузке. Разделяйте flush и commit: flush отправляет SQL, commit фиксирует транзакцию. Упомяните @DynamicUpdate и readOnly = true как оптимизации.