Flush Modes

When Does Hibernate Flush

Flushing = sending pending changes (SQL) to the database. It is not commit; a rollback still undoes flushed changes.

ModeFlushes whenUse
AUTO (default)before commit, and before a query whose result could be affected by pending changesnormal code
COMMITonly at commit (queries may see stale data)long read-heavy transactions
MANUAL (Hibernate)only when you call flush()read-only transactions (Spring sets it for readOnly = true)
ALWAYS (Hibernate)before every queryrarely needed

AUTO vs COMMIT

Example

A pending stock change and then a "low stock" query. With AUTO the query sees the change; with COMMIT it does not:

import java.util.*;

public class Main {
    enum FlushMode { AUTO, COMMIT }

    static class Session {
        final Map<Long, Integer> db = new TreeMap<>(Map.of(1L, 12, 2L, 3, 3L, 40));
        final Map<Long, Integer> pending = new TreeMap<>();
        final FlushMode mode;
        Session(FlushMode mode) { this.mode = mode; }

        void setStock(long id, int stock) { pending.put(id, stock); }   // dirty entity

        void flush() {
            pending.forEach((id, s) -> System.out.println("  update products set stock=" + s + " where id=" + id));
            db.putAll(pending); pending.clear();
        }

        List<Long> lowStock() {
            if (mode == FlushMode.AUTO && !pending.isEmpty()) flush();   // products table is affected
            List<Long> ids = new ArrayList<>();
            db.forEach((id, s) -> { if (s < 5) ids.add(id); });
            System.out.println("  select id from products where stock<5 -> " + ids);
            return ids;
        }
    }

    public static void main(String[] args) {
        for (FlushMode m : FlushMode.values()) {
            System.out.println(m + ":");
            Session s = new Session(m);
            s.setStock(1L, 2);          // product 1 just sold 10 units
            s.lowStock();
        }
    }
}

Output

AUTO:
  update products set stock=2 where id=1
  select id from products where stock<5 -> [1, 2]
COMMIT:
  select id from products where stock<5 -> [2]

Changing It

// JPA, whole EntityManager or one query
em.setFlushMode(FlushModeType.COMMIT);
query.setFlushMode(FlushModeType.COMMIT);

// Hibernate-specific modes
Session session = em.unwrap(Session.class);
session.setHibernateFlushMode(FlushMode.MANUAL);

Note: Native SQL queries: Hibernate cannot always tell which tables they touch, so in AUTO mode it flushes the whole session before them (Hibernate 6 native queries flush by default). Add addSynchronizedEntityClass(Product.class) on a NativeQuery to be precise.