В чем разница между save и flush в Hibernate?
Что проверяют
Понимаете ли вы, как устроен контекст персистентности: где сущность живёт до записи в базу и в какой момент Hibernate отправляет SQL. Следом почти всегда спрашивают, чем flush() отличается от коммита и почему после save() в логах иногда сразу виден INSERT.
Как отвечать
save(): переводит новую сущность из состояния transient в persistent, то есть кладёт её в контекст сессии, и возвращает идентификатор. Это собственный метод Hibernate, в Hibernate 6 он помечен устаревшим в пользу JPA-методаpersist(), который ничего не возвращает;flush(): синхронизирует контекст с базой. Hibernate сравнивает сущности сессии с их снимком (dirty checking) и выполняет накопленныеINSERT,UPDATE,DELETE. Транзакция остаётся открытой, иrollbackвсё отменит;- когда flush происходит сам: в режиме по умолчанию
FlushMode.AUTOперед коммитом и перед запросом, результат которого зависит от несохранённых изменений. РежимCOMMITоткладывает запись до коммита,MANUALоставляет её вам.
Важная оговорка про генерацию ключа. При GenerationType.IDENTITY идентификатор известен только после вставки, поэтому save() и persist() выполняют INSERT сразу, а пакетная вставка через JDBC batching для таких сущностей не работает. При SEQUENCE Hibernate берёт значение из последовательности, а вставку откладывает до flush.
Явный flush() нужен, чтобы получить ошибку ограничения в конкретном месте, перед нативным SQL или JDBC-вызовом в той же транзакции и в пакетной обработке, где каждые N записей делают flush() и clear(). В Spring Data для этого есть saveAndFlush().
Где спотыкаются
Говорят, что flush() сохраняет данные насовсем. Это не коммит: другие транзакции при READ COMMITTED изменений не видят, а откат их убирает.
Утверждают, что save() никогда не ходит в базу до flush. С IDENTITY это неверно, и именно на этом кандидатов ловят.
Путают Session.save() с save() из Spring Data. Репозиторий для новой сущности вызывает persist(), а для сущности с уже заданным id уходит в merge(), который возвращает другой экземпляр, и изменения исходного объекта дальше не отслеживаются.