One of Hibernate’s biggest strengths as an ORM is its ability to automatically detect changes made to entities and persist them to the database. This powerful behavior is enabled by two core concepts: Dirty Checking and the Flushing Mechanism.
Think of dirty checking as a personal assistant who remembers every edit you make to a draft document. Flushing is like pressing “Save” to sync your edits with the central server. Together, they make Hibernate both intelligent and efficient in handling persistence.
In this tutorial, we’ll cover how Hibernate manages dirty checking and flushing, their real-world importance, configurations, pitfalls, and best practices.
What is Dirty Checking in Hibernate?
Dirty checking is Hibernate’s ability to automatically detect changes in persistent entities and synchronize them with the database when a transaction is committed or the session is flushed.
Example
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = session.get(Employee.class, 1L);
emp.setSalary(emp.getSalary() + 1000); // Change tracked automatically
tx.commit(); // Hibernate detects change and issues UPDATE query
session.close();
You don’t explicitly call session.update(emp)
here. Hibernate detects the change using dirty checking and updates the row.
How Hibernate Performs Dirty Checking
- When an entity is loaded, Hibernate keeps a snapshot of its initial state.
- When a transaction is committed or flushed, Hibernate compares the current state with the snapshot.
- If differences exist, an UPDATE SQL statement is generated.
What is Flushing in Hibernate?
Flushing is the process of synchronizing the in-memory state of the persistence context with the database.
Important: Flushing does not mean committing.
- Flush = Hibernate issues SQL to sync changes.
- Commit = Database transaction is permanently saved.
Example
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = new Employee("John", "IT", 5000);
session.save(emp); // Insert queued
session.flush(); // SQL INSERT executed immediately
tx.commit();
session.close();
Here, flush()
forces Hibernate to issue SQL before commit.
Flush Modes in Hibernate
Hibernate allows control over flushing behavior via FlushMode
.
1. AUTO
(Default)
Flush occurs automatically before query execution and transaction commit.
session.setFlushMode(FlushMode.AUTO);
2. COMMIT
Flush only at transaction commit.
session.setFlushMode(FlushMode.COMMIT);
3. ALWAYS
Flush before every query execution.
session.setFlushMode(FlushMode.ALWAYS);
4. MANUAL
Flush happens only when explicitly called.
session.setFlushMode(FlushMode.MANUAL);
session.flush();
Step-by-Step CRUD Example with Dirty Checking and Flushing
Create
Employee emp = new Employee("Alice", "HR", 6000);
session.save(emp); // INSERT queued
Read
Employee emp = session.get(Employee.class, 1L);
System.out.println(emp.getName());
Update
Employee emp = session.get(Employee.class, 1L);
emp.setDepartment("Finance"); // Dirty checking will trigger UPDATE
Delete
Employee emp = session.get(Employee.class, 2L);
session.delete(emp); // DELETE issued at flush/commit
Real-World Use Case: Spring Boot + Hibernate
spring:
jpa:
properties:
hibernate:
flushMode: AUTO
hibernate:
ddl-auto: update
With Spring Data JPA repositories, dirty checking ensures updated entities are persisted automatically without explicit save calls.
Common Pitfalls & Anti-Patterns
-
Forgetting that Flush != Commit
Developers often confuse flushing with committing. -
Unnecessary Updates
Modifying entities without actual value change still triggers updates. -
Manual Flush in Wrong Place
Excessive manual flush calls lead to performance degradation. -
Large Transactions
Dirty checking compares all entities in the persistence context, leading to memory overhead.
Best Practices
- Use default AUTO flush mode for most cases.
- Keep transactions short and focused.
- Avoid unnecessary field modifications.
- Explicitly call
flush()
only when needed (e.g., batch inserts). - Monitor SQL logs to detect redundant updates.
📌 Hibernate Version Notes
Hibernate 5.x
- Uses
javax.persistence
. - Default flush mode =
AUTO
. - Dirty checking relies on entity snapshots.
Hibernate 6.x
- Migrated to
jakarta.persistence
. - Improved dirty checking with better bytecode enhancement.
- Enhanced support for batch flushing and query plan caching.
Conclusion & Key Takeaways
- Dirty Checking: Detects entity changes automatically.
- Flushing: Synchronizes persistence context with the database.
- Both are essential for Hibernate’s ORM magic.
- Always distinguish between flush and commit.
- Optimize transactions and avoid redundant flushes for better performance.
FAQ: Expert-Level Questions
Q1: What’s the difference between Hibernate and JPA?
Hibernate is a JPA implementation that provides extra features like dirty checking and caching.
Q2: How does Hibernate caching improve performance?
It reduces database calls by storing frequently accessed data in memory.
Q3: What are the drawbacks of eager fetching?
It loads unnecessary data, slowing queries and using more memory.
Q4: How do I solve the N+1 select problem in Hibernate?
Use fetch joins, @BatchSize
, or entity graphs.
Q5: Can I use Hibernate without Spring?
Yes, Hibernate can work standalone with SessionFactory
.
Q6: What’s the best strategy for inheritance mapping?
Depends on use case: SINGLE_TABLE
, JOINED
, or TABLE_PER_CLASS
.
Q7: How does Hibernate handle composite keys?
Using @Embeddable
with @EmbeddedId
or @IdClass
.
Q8: How is Hibernate 6 different from Hibernate 5?
Hibernate 6 uses jakarta.persistence
, improves dirty checking, and enhances flushing performance.
Q9: Is Hibernate suitable for microservices?
Yes, but smaller ORMs like jOOQ or MyBatis may suit lightweight services better.
Q10: When should I not use Hibernate?
Avoid Hibernate when low-level SQL control and raw performance are required (e.g., analytics-heavy apps).