Найти в Дзене
Записки о Java

Когда Hibernate автоматически сохраняет изменения в Entity?

Краткий ответ:
Да, если вы работаете в рамках транзакции, и ваша сущность находится в состоянии managed (persistent), то изменения будут автоматически записаны в БД при коммите транзакции — без вызова flush().
Но есть нюансы: не все операции триггерят flush, и вне транзакции изменения могут быть проигнорированы. Давайте разберёмся по шагам. В JPA/Hibernate каждая сущность (@Entity) может находиться в одном из трёх состояний: Transient Новый объект, не связан с EntityManager (ещё не сохранён). Managed (Persistent) Объект загружен через EntityManager или сохранён в нём. Hibernate отслеживает его изменения. Detached Объект был managed, но EntityManager закрыт или транзакция завершена. Изменения не отслеживаются. 💡 Только managed-сущности участвуют в механизме automatic dirty checking (автоматического отслеживания изменений). Метод EntityManager.flush() принудительно синхронизирует состояние managed-сущностей с базой данных, выполняя SQL-запросы (INSERT, UPDATE, DELETE). Но вызывать его
Оглавление

Понимаем flush, транзакции и грязные сущности в JPA/Hibernate

Краткий ответ:
Да, если вы работаете в рамках
транзакции, и ваша сущность находится в состоянии managed (persistent), то изменения будут автоматически записаны в БД при коммите транзакции — без вызова flush().
Но есть нюансы:
не все операции триггерят flush, и вне транзакции изменения могут быть проигнорированы.

Давайте разберёмся по шагам.

📌 Контекст: что такое «управляемая» (managed) сущность?

В JPA/Hibernate каждая сущность (@Entity) может находиться в одном из трёх состояний:

Transient

Новый объект, не связан с EntityManager (ещё не сохранён).

Managed (Persistent)

Объект загружен через EntityManager или сохранён в нём. Hibernate отслеживает его изменения.

Detached

Объект был managed, но EntityManager закрыт или транзакция завершена. Изменения не отслеживаются.

💡 Только managed-сущности участвуют в механизме automatic dirty checking (автоматического отслеживания изменений).

🔁 Что такое flush() и зачем он нужен?

Метод EntityManager.flush() принудительно синхронизирует состояние managed-сущностей с базой данных, выполняя SQL-запросы (INSERT, UPDATE, DELETE).

Но вызывать его вручную почти никогда не нужно — Hibernate делает это автоматически в определённые моменты.

✅ Когда Hibernate автоматически вызывает flush()?

Hibernate выполняет автоматический flush в следующих случаях:

1. Перед коммитом транзакции

Рисунок
Рисунок

→ При выходе из метода (и коммите транзакции) изменения попадут в БД.

2. Перед выполнением JPQL/HQL-запроса, который может затронуть изменённые данные

user.setName("New Name");

// flush() вызывается ПЕРЕД этим запросом, чтобы результат был актуальным

List<User> users = entityManager.createQuery("SELECT u FROM User u WHERE u.name = 'New Name'", User.class).getResultList();

→ Это гарантирует консистентность между состоянием в памяти и тем, что вернёт запрос.

3. При явном вызове entityManager.flush() (редко нужно)

Когда изменения НЕ попадут в БД?

Случай 1. Изменение detached-сущности

User user = userRepository.findById(1L).orElseThrow();

// Транзакция завершена → user теперь detached

user.setName("Hacker"); // ← изменение НЕ отслеживается!

// Никакой flush не поможет — Hibernate "не знает" об этом объекте

Решение: использовать merge():

userRepository.save(user); // в Spring Data — это merge()

Случай 2. Изменение внутри транзакции, но без коммита

Рисунок
Рисунок

→ Изменения не попадут в БД, потому что транзакция откатится (rollback).

Случай 3. Работа вне транзакции

Рисунок
Рисунок

В Spring Boot без @Transactional методы не имеют открытой транзакции, даже если вы используете JpaRepository.

Случай 4. Изменение поля, не отслеживаемого Hibernate

Например:

  • transient поля,
  • поля без геттеров/сеттеров (если используется field access),
  • коллекции, изменённые напрямую (без clear() + addAll()).

🧪 Практический пример: когда flush() нужен вручную?

Редкий, но реальный кейс: вы хотите получить сгенерированный ID или timestamp сразу после persist(), но до коммита.

Рисунок
Рисунок

Но даже здесь можно обойтись без flush(), если использовать GenerationType.IDENTITY или @GeneratedValue(strategy = GenerationType.SEQUENCE)

Ответ на ваш вопрос

Правильно ли я понимаю, что если Entity меняется в рамках транзакции, то после завершения транзакции не надо вызывать EntityManager.flush(), и изменения сразу попадут в БД?

Да, абсолютно правильно.
Если:

  • сущность managed,
  • вы находитесь в активной транзакции (@Transactional),
  • изменения сделаны до коммита,

→ то Hibernate автоматически вызовет flush() перед коммитом, и изменения попадут в БД.

Вызов flush() вручную нужен только в специфических случаях (например, получить ID до коммита или гарантировать порядок SQL-запросов).