Dirty checking and flushing
Learn why one setter updates a row, and when the SQL is actually sent.
Open this lesson in the learning hubKey points
- Hibernate snapshots every entity as it was loaded. At flush time it compares, then writes an
UPDATEfor whatever changed. - So a setter on a managed entity is a database write. Calling
save()afterwards changes nothing. - Flush is when the queued SQL is sent. Commit is when the transaction ends. Flush always happens before commit.
- With the default
AUTOflush mode Hibernate also flushes before a query whose result the pending changes could affect. - Statement order is up to Hibernate: at flush it groups by operation type, so your call order is not the SQL order.
- Loading entities you only read wastes snapshot memory. Use a DTO projection or
@Transactional(readOnly = true)on read paths.
Example
@Transactional
public void rename(Long id, String newTitle) {
Book book = em.find(Book.class, id);
book.setTitle(newTitle);
// No save() call. At flush Hibernate compares the snapshot and sends:
// update book set title=?, published_on=? where id=?
}
@Transactional
public void needTheIdNow(Book book) {
em.persist(book);
em.flush(); // force the INSERT now, e.g. before a native query
auditLog.record(book.getId());
// still one transaction: a rollback undoes the flushed INSERT too
}
Change a managed entity and the UPDATE writes itself.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Hibernate course, and every lesson in it is listed on the Hibernate contents page.