skip to content

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%

answer

  1. AUTO = commit + affected queries
  2. COMMIT = stale query results risk
  3. ALWAYS = flush before every query
  4. MANUAL = only explicit flush, even at commit
  5. per-query override beats session mode

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.

solid answer

~50 s

**AUTO** is the JPA default: flush at transaction commit, and flush before executing a query whose result the pending changes could affect, so a query in the same unit of work sees your own writes. **COMMIT** suppresses the pre-query flush — the context is only written at commit. It saves the auto-flush check, but a query can then return rows that contradict what you already changed in memory, which is a real source of bugs. Hibernate adds two native modes. **ALWAYS** flushes before *every* query with no overlap check — safe but costly, occasionally used with native SQL that Hibernate cannot analyse. **MANUAL** never flushes automatically, not even at commit; only an explicit `flush()` writes. It is used for read-only work where you want zero writes, and for multi-request conversations where changes accumulate and are flushed once at the end. Set it per session or per query: `em.setFlushMode(...)` or `query.setFlushMode(...)`.

code

java · 10 lines
java
em.setFlushMode(FlushModeType.COMMIT);

Order o = em.find(Order.class, 1L);
o.setStatus("CANCELLED");   // queued, not sent

List<Order> open = em.createQuery(
        "select o from Order o where o.status = 'OPEN'", Order.class)
    .getResultList();       // still returns order 1

em.getTransaction().commit(); // UPDATE finally sent here

go deeper

for a junior

Know that AUTO is the default and that it flushes at commit and before queries so you read your own writes.

for a middle

Contrast all four modes precisely, and describe the stale-read bug COMMIT introduces with a concrete example.

for a senior

Discuss when a non-default mode is justified, how per-query overrides scope the risk, and why MANUAL's silent data loss makes it a narrow tool.

for a principal

Treat flush mode as a unit-of-work contract: default everywhere, deviations documented and locally scoped, with restructuring preferred over global mode changes.

## The four modes | Mode | API | Flush at commit | Flush before a query | |---|---|---|---| | `AUTO` | JPA + Hibernate | yes | only if the query could be affected by pending changes | | `COMMIT` | JPA + Hibernate | yes | never | | `ALWAYS` | Hibernate only | yes | always, unconditionally | | `MANUAL` | Hibernate only (`FlushMode.MANUAL`) | **no** | never | In JPA you use `FlushModeType` via `EntityManager.setFlushMode(...)` or `Query.setFlushMode(...)`; the native modes come from `org.hibernate.FlushMode` and are set with `Session.setHibernateFlushMode(...)`. A flush mode set on a query overrides the session-level mode for that query only. ## AUTO — the default, and why it exists The hard problem with write-behind is *read-your-own-writes*. If you change a managed entity and then run a JPQL query, the database still holds the old row, so the query would contradict memory. AUTO solves this by flushing pending changes before the query runs, so query results and the persistence context agree. Hibernate does not flush before *every* query under AUTO — it compares the tables the query reads with the tables the pending actions touch and skips the flush when they cannot overlap. That optimization is what makes AUTO affordable. ## COMMIT — the tempting trap `COMMIT` says: never mind queries, write at commit. The upside is fewer flushes — no mid-transaction writes, so no early row locks and no repeated flush overhead in a loop that alternates writes and queries. The downside is severe. Consider marking an order cancelled in memory and then running `select o from Order o where o.status = 'OPEN'`: under COMMIT the cancelled order is still returned, because the `UPDATE` has not been sent. Application logic that mixes entity mutation and querying silently reads a stale world. Use COMMIT only when you control the whole unit of work and know reads and writes do not overlap. ## ALWAYS — the blunt instrument `ALWAYS` skips the overlap analysis and flushes before every query. It costs performance but is correct by construction. Its main real use is code paths where Hibernate cannot know what a statement touches — typically native SQL or a stored procedure — and you would rather pay for unconditional flushes than reason about it. ## MANUAL — nothing is written unless you say so `MANUAL` (formerly `NEVER`) disables automatic flushing entirely, including at commit. Two uses matter: - **Read-only work.** With MANUAL, an accidental setter on a managed entity cannot turn into an `UPDATE`. Some teams use it as a hard guarantee that a reporting path issues no writes. - **Long conversations.** A multi-step wizard can keep an extended persistence context across requests, accumulate changes, and flush exactly once when the user finally confirms, so intermediate steps never touch the database. The danger is symmetric: if you set MANUAL and forget the explicit `flush()`, the work is *silently discarded* at close. There is no error — just missing data. MANUAL should be a deliberate, narrowly scoped choice, never a global default. ## Choosing Stay on AUTO unless you have a measured reason. If a hot loop shows excessive flush overhead, prefer restructuring the unit of work — do the reads first, then the writes — over switching to COMMIT, because the restructure keeps correctness local instead of making every future query in that unit of work suspect.

  • Under FlushModeType.COMMIT, does a query still see changes made earlier in the same transaction?
    Only changes that were already flushed for some other reason, such as an explicit flush() or an IDENTITY insert. Purely queued changes are invisible to the query, so results reflect the database as of the last flush rather than the persistence context. Entities already managed in the context are still returned from it by identity, which makes the inconsistency look even stranger.
  • What is the failure mode of Hibernate's MANUAL flush mode?
    Silent data loss. MANUAL suppresses the flush at commit as well, so if the code path never calls flush() the transaction commits with nothing written and no exception is raised. That is why MANUAL should be scoped tightly to a read-only path or an explicit conversation that always ends with a flush.

saying these in an interview costs you the question

  • Claiming COMMIT mode is 'faster with no downside' while ignoring stale query results
  • Thinking MANUAL still flushes at commit — it does not, the work is lost
  • Believing AUTO flushes before every single query rather than only affected ones
  • Assuming ALWAYS and AUTO are the same thing
  • Setting a global non-default flush mode to fix one slow loop instead of restructuring the unit of work

context