skip to content

Flush Modes & Flush Order

When queued changes actually become SQL: flush modes, the auto-flush-before-query rule, and Hibernate's fixed statement ordering. Interviewers use flush-order questions to explain mysterious constraint violations that only appear at commit.

part ofHibernateoverview, primer and where to startread it →
on this pageshow

questions

4

In JPA/Hibernate, what does it mean to "flush" the persistence context, and how is flushing different from committing the transaction?

level: juniorimportance: must knowfreq 66%

answer

  1. write-behind action queue
  2. flush = SQL out, commit = permanent
  3. commit implies flush, never the reverse
  4. constraint errors surface at flush
  5. IDENTITY forces insert at persist

basics

~20 s

Flushing sends the INSERT/UPDATE/DELETE statements the persistence context has queued to the database inside the open transaction. Committing ends the transaction and makes them permanent. Commit always flushes first, but a flush alone can still be rolled back.

solid answer

~50 s

The persistence context is a write-behind buffer. When you call `persist`, modify a managed entity, or call `remove`, Hibernate normally does not talk to the database immediately — it records the intent in an internal action queue. **Flush** is the moment it turns that queue into SQL and sends it over the current JDBC connection, synchronizing memory with the database. **Commit** is a transaction operation: it ends the unit of work, makes changes durable and visible to other transactions. The relationship is one-way: commit triggers a flush first, but a flush does not commit. Flushed statements are still inside the transaction, invisible to other sessions, and a rollback undoes them completely. Flushes happen at commit, on an explicit `EntityManager.flush()`, and — under the default flush mode — automatically before a query whose results the pending changes could affect.

code

java · 10 lines
java
em.getTransaction().begin();

Order order = new Order("A-1");
em.persist(order);          // no INSERT yet (sequence generator)

order.setStatus("PAID");    // no UPDATE either

em.flush();                 // INSERT ... (status already PAID) is sent now

em.getTransaction().rollback(); // the flushed INSERT is undone

go deeper

for a junior

Recall the core sentence: flush sends SQL inside the transaction, commit ends the transaction and makes it permanent, and commit flushes first.

for a middle

Add why write-behind exists (batching, statement ordering) and name the three flush triggers: commit, explicit flush(), and auto-flush before a query.

for a senior

Connect it to debugging and locking: errors surface at flush time rather than at the offending line, and early flushes lengthen row-lock windows.

for a principal

Frame flush timing as a unit-of-work design decision — where write batches are pushed, how long locks are held, and how failures are surfaced in the request lifecycle.

## Write-behind, not write-through A JPA `EntityManager` (Hibernate `Session` underneath) is an in-memory unit of work called the **persistence context**. It holds every entity you loaded or created, keyed by type plus primary key, plus a snapshot of the loaded state. Operations you perform are recorded as *pending actions* in an internal **action queue** rather than executed at once. This is called write-behind, and it exists so Hibernate can batch statements, avoid writing rows you later change again, and order statements safely. ## What flush does Flushing walks the persistence context and turns pending state into SQL: - entities you called `persist()` on become `INSERT`s; - managed entities whose current field values differ from their load-time snapshot become `UPDATE`s; - entities you called `remove()` on become `DELETE`s; - collection changes become their own insert/update/delete statements. The statements go over the JDBC connection the session is using, inside whatever transaction is currently open. After a flush the database and the persistence context agree; the entities stay managed and keep being tracked. ## What commit does Commit is a *transaction* concept, not a persistence-context one. It tells the database the work of the transaction is final: locks are released, changes become visible to other transactions, and durability guarantees apply. Because Hibernate cannot let queued changes vanish, committing performs a flush first and then commits. That is why plenty of code never calls `flush()` and still writes data correctly. ## Why the distinction matters 1. **A flush is undoable.** If you flush, then throw, then roll back, nothing survives. Candidates who say "flush saves the data" are wrong in the way that matters. 2. **A flush can fail.** Constraint violations, `NOT NULL` breaches and optimistic-lock version mismatches surface *at flush time*, not when you edited the object. That is why a stack trace often points at a commit or at an unrelated query rather than at the line that set the bad value — the write was buffered until then. 3. **A flush takes locks.** Once an `UPDATE` is on the wire the row is write-locked until the transaction ends, so flushing early and committing late widens the lock window. 4. **Some operations force partial writes early.** With `GenerationType.IDENTITY`, Hibernate must run the `INSERT` during `persist()` to obtain the generated key; sequence-based generators avoid this and stay fully write-behind. ## When flushes occur - **At commit** — always. - **Explicitly**, via `EntityManager.flush()` / `Session.flush()`. - **Automatically before a query**, under the default `FlushModeType.AUTO`, so the query sees your own pending changes. Explicit flushes have legitimate uses: forcing a constraint or version error to surface where you can handle it, making pending rows visible to a native SQL statement you are about to run, and the batching idiom `flush()` + `clear()` that pushes accumulated statements out and detaches the entities so the context does not grow without bound. Flushing everywhere "to be safe" is an anti-pattern: it defeats batching, lengthens lock hold times, and hides the real design of the unit of work.

  • After calling flush(), can another transaction see the inserted row?
    No, not under normal isolation. The statements are inside your still-open transaction, so other transactions see the pre-transaction state; a reader trying to modify the same row may block on your locks. Visibility only comes with commit.
  • Give two legitimate reasons to call flush() explicitly.
    First, to make pending changes visible to a native SQL statement or stored procedure you are about to execute, since Hibernate cannot always tell that the SQL touches the same tables. Second, to force a constraint or optimistic-lock failure to surface at a controlled point, or as part of the flush-then-clear batching idiom that pushes statements out and keeps the persistence context small.

Flush is pressing Save in an editor that still holds an exclusive lock on the file; commit is closing the file so everyone else can finally see and open it. A crash before closing can still discard the saved work.

saying these in an interview costs you the question

  • Saying flush() commits or 'saves permanently' — a rollback still discards it
  • Believing every setter immediately issues an UPDATE
  • Calling flush() after every operation 'to be safe', destroying batching and holding row locks longer
  • Thinking commit does not flush, so you must call flush() before every commit
  • Assuming persist() never issues SQL — IDENTITY key generation forces the INSERT immediately

context

open as a page

JPA defines FlushModeType.AUTO and FlushModeType.COMMIT, and Hibernate's native API adds ALWAYS and MANUAL. What does each of those four flush modes do, and why would you move off the default?

level: middleimportance: must knowfreq 54%

basics

~20 s

AUTO (the default) flushes at commit and before queries that pending changes could affect. COMMIT flushes only at commit, so queries may read stale data. Hibernate's ALWAYS flushes before every query; MANUAL flushes only when you call flush() explicitly.

open as a page

A unit of work removes a row and then persists a new entity carrying the same unique key value, and the flush fails with a unique-constraint violation even though the delete should have freed the key. Why does Hibernate order the statements that way, and how do you fix it?

level: seniorimportance: must knowfreq 40%

basics

~20 s

At flush time Hibernate executes actions in a fixed order — inserts first, then updates, then collection actions, with deletes last — not in the order you wrote them. The INSERT therefore runs while the old row still exists. Flush explicitly after the remove, or avoid the collision.

open as a page

Under Hibernate's default flush mode, a JPQL query sometimes triggers a flush of pending changes and sometimes does not. What decides that, and why do native SQL queries behave differently?

level: middleimportance: should knowfreq 42%

basics

~20 s

Hibernate compares the tables a JPQL query reads with the tables the pending actions would modify, and flushes only when they overlap. It cannot parse native SQL, so it conservatively flushes everything unless you declare the query's synchronized entities or query spaces.

open as a page