Question 13 · Section 16

How Does the Flush Mechanism Work in Hibernate

Flush is the process of synchronizing data from the persistence context (Hibernate cache) to the database. Understanding the flush mechanism is critical for transaction manageme...

Language versions: English Russian Ukrainian

Overview

Flush is the process of synchronizing data from the persistence context (Hibernate cache) to the database. Understanding the flush mechanism is critical for transaction management and performance optimization.


Junior Level

What is Flush

Flush is the process of writing all changes from memory (persistence context) to the database.

User user = entityManager.find(User.class, 1L);
user.setName("New Name");  // changed in memory

// Flush hasn't happened yet - DB NOT updated!

entityManager.flush();  // explicit flush - UPDATE executed in DB
// or
// On commit - automatic flush

When Flush Happens

  1. On transaction commit - automatically
  2. On entityManager.flush() - explicitly
  3. Before executing a query - to return actual data
@Transactional
public void updateUser() {
    User user = entityManager.find(User.class, 1L);
    user.setName("New");

    // flush hasn't happened yet
    // DB still contains old name

    // On method exit -> @Transactional commit -> flush
}

Manual Flush Example

@Transactional
public void batchInsert(List<User> users) {
    for (int i = 0; i < users.size(); i++) {
        entityManager.persist(users.get(i));

        if (i % 50 == 0) {
            entityManager.flush();  // flush to DB
            entityManager.clear();  // clear cache
        }
    }
}

Middle Level

FlushMode

// AUTO (default) - flush when necessary
entityManager.setFlushMode(FlushModeType.AUTO);

// COMMIT - flush only on commit
entityManager.setFlushMode(FlushModeType.COMMIT);

// ALWAYS - before every query (rarely used)
entityManager.setFlushMode(FlushModeType.ALWAYS);
// (Note: FlushModeType.ALWAYS was deprecated in Hibernate 5 and removed in Hibernate 6. In modern versions, use AUTO or COMMIT.)

FlushMode.AUTO vs COMMIT

// AUTO - flush before query
User user = entityManager.find(User.class, 1L);
user.setName("New");

// Before this query - flush (to return actual data)
List<User> users = entityManager.createQuery("FROM User", User.class)
    .getResultList();  // includes "New" user

// COMMIT - flush only on commit
entityManager.setFlushMode(FlushModeType.COMMIT);
user.setName("New");

// This query will NOT see "New" user (flush hasn't happened)
List<User> users = entityManager.createQuery("FROM User", User.class)
    .getResultList();  // does NOT include "New" user

Manual Flush for Batch Operations

@Transactional
public void batchUpdate(List<User> users) {
    for (int i = 0; i < users.size(); i++) {
        entityManager.merge(users.get(i));

        if (i % 50 == 0) {
            entityManager.flush();  // flush 50 UPDATEs to DB
            entityManager.clear();  // clear persistence context
        }
    }
    // Final flush for remaining
    entityManager.flush();
    entityManager.clear();
}

Common Mistakes

// Frequent flush - slow
for (User user : users) {
    entityManager.persist(user);
    entityManager.flush();  // every time - very slow!
}

// Periodic flush
for (int i = 0; i < users.size(); i++) {
    entityManager.persist(users.get(i));
    if (i % 50 == 0) {
        entityManager.flush();
        entityManager.clear();
    }
}

Senior Level

Internal Implementation

Flush process:

1. Dirty checking
   - Traverse all entities in persistence context
   - Compare snapshot with current state
   - For changed ones - schedule UPDATE

2. Generation order
   - INSERT first (to get IDs)
   - UPDATE next
   - DELETE last (to avoid FK violations)

3. SQL generation
   - For each scheduled operation
   - Generates SQL considering dirty fields

4. Execution
   - Executes SQL in DB (JDBC batch)
   - Updates snapshot

5. Post-flush
   - Updates EntityEntry states
   - Resets dirty flags

Order of Operations

On flush, Hibernate follows this order:

1. All entity INSERTs (in persist() order)
2. All entity UPDATEs (in dirty detection order)
3. All collection UPDATEs (for collection changes)
4. All collection DELETEs (for removed elements)
5. All entity DELETEs (in remove() order)

This is important for:
- Foreign key constraints
- Referential integrity
- Identity generation

This order is critical because of foreign key constraints. If Hibernate
tries to INSERT a child before INSERTing the parent, the DB throws FK violation.
Similarly - DELETE parent before DELETE child.

Flush and Optimistic Locking

@Version
private Integer version;

// On flush:
// UPDATE users SET name = ?, version = version + 1
// WHERE id = ? AND version = ?

// If version doesn't match -> OptimisticLockException

Advanced Settings

spring:
  jpa:
    properties:
      hibernate:
        # Flush order for different operations
        order_inserts: true       # group INSERTs
        order_updates: true       # group UPDATEs
        order_deletes: true       # group DELETEs

        # JDBC batch size
        jdbc.batch_size: 50

Flush and Transactions

// @Transactional defines flush boundaries
@Transactional
public void method1() {
    user.setName("A");
    // flush on commit
}

@Transactional
public void method2() {
    user.setName("B");
    entityManager.flush();  // explicit flush
    // another flush on commit (if there were changes)
}

Best Practices

AUTO flush mode by default
Manual flush for batch operations
flush + clear every 50-100 entities
COMMIT mode when actual data not needed
order_inserts/order_updates for optimization

ALWAYS mode (avoid)
Flush without reason
Without clear after flush in batches
Ignoring operation order

Interview Cheat Sheet

Must know:

  • Flush - synchronization of data from persistence context to DB
  • Happens on: commit, entityManager.flush(), before query (AUTO mode)
  • FlushMode: AUTO (default), COMMIT (only on commit), ALWAYS (deprecated)
  • Operation order: INSERT -> UPDATE -> DELETE (for FK constraints)
  • For batch: flush + clear every 50-100 entities
  • order_inserts/order_updates - SQL grouping for optimization

Frequent follow-up questions:

  • AUTO vs COMMIT? AUTO - flush before query (actual data), COMMIT - only on commit
  • Why is INSERT->UPDATE->DELETE order important? Foreign key constraints: cannot INSERT child before INSERT parent
  • Why is frequent flush slow? Each flush = SQL queries, 1000 entities x flush = 1000 SQL
  • What is JDBC batch_size? Groups SQL queries for batch sending, reduces network round trips

Red flags (DO NOT say):

  • “Flush after every persist” - very slow, better batch flush
  • “ALWAYS mode for all queries” - deprecated, huge overhead
  • “Without clear after flush in batches” - persistence context grows, OOM
  • “I don’t understand operation order” - can cause FK violation

Related topics:

  • [[12. What is Dirty Checking in Hibernate]]
  • [[7. Describe the Entity Lifecycle in Hibernate]]
  • [[14. What is the Difference Between persist() and merge()]]
  • [[17. How to Implement Optimistic Locking in JPA]]