Have you ever added entityManager.flush() and entityManager.clear() inside a Spring Boot JPA transaction to make deletes and inserts “reflect immediately” — only to see your performance tank?
You're not alone.
This is one of those subtle Hibernate behaviors that bites even experienced developers.
Let’s unpack why it happens, and what to do instead.
🧩 The Real-World Scenario
You have a Spring Boot service method like this:
@Transactional
public void refreshData() {
myRepository.deleteByType("A");
entityManager.flush();
entityManager.clear();
List<MyEntity> newRecords = fetchNewData();
myRepository.saveAll(newRecords);
}
You added flush() and clear() because deletes weren’t visible before inserts — and it “fixed” the logic.
But now, your transaction runs 3× slower, the DB CPU spikes, and Hibernate logs show hundreds of SQL statements being executed one by one.
What’s going on?
⚙️ Understanding flush() and clear()
🔹 flush()
Tells JPA:
“Send all pending SQL statements to the database now.”
Normally, Hibernate batches and optimizes SQL statements until transaction commit.
When you call flush(), it forces immediate execution — breaking batching and hitting the database right away.
🔹 clear()
Tells JPA:
“Forget everything you know about the current persistence context.”
This detaches all managed entities.
Hibernate now has to re-fetch them from the database if you reference them again, and you lose the in-memory first-level cache.
⚠️ Why It Becomes Slow
| Cause | Effect |
|---|---|
flush() executes pending SQL immediately |
Hibernate loses batching and optimization |
clear() wipes first-level cache |
Every next entity access triggers a new SELECT
|
| Same-table deletes + inserts | DB locks and index contention |
| Large transaction scope | Hibernate keeps snapshots for dirty checking |
| Cascades or orphan removal | Extra SQLs during flush |
In short — you gain “visibility” but lose performance and batching efficiency.
💡 Smarter Alternatives
✅ 1. Use Bulk Deletes Instead of Entity Deletes
@Modifying
@Query("DELETE FROM MyEntity e WHERE e.type = :type")
void deleteByType(@Param("type") String type);
Bulk deletes:
- Run a single SQL
DELETE - Don’t load entities into memory
- Don’t trigger cascade or dirty checking
Perfect for refreshing data tables.
✅ 2. Split the Transaction Logically
@Transactional
public void refreshData() {
myRepository.deleteByType("A");
insertNewRecordsInNewTx();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertNewRecordsInNewTx() {
myRepository.saveAll(fetchNewData());
}
Now, the delete commits before the insert starts — no need to manually flush() or clear().
✅ 3. Use Batching for Inserts
In application.properties:
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
Then in code:
int i = 0;
for (MyEntity entity : newRecords) {
entityManager.persist(entity);
if (++i % 50 == 0) {
entityManager.flush();
entityManager.clear();
}
}
You’ll get massive performance gains for large datasets.
✅ 4. Avoid clear() Unless You Really Need It
Instead of clearing everything:
entityManager.detach(entity);
This detaches only the specific entity you need, preserving most of the persistence context.
🚀 The TL;DR Summary
| Problem | Cause | Fix |
|---|---|---|
| Transaction runs slowly |
flush() executes SQL immediately |
Avoid frequent flushes |
| Too many selects |
clear() wipes cache |
Detach selectively |
| Delete + insert on same table | DB contention | Split into separate transactions |
| High memory usage | Large persistence context | Batch + periodic flush/clear |
| Cascades killing performance | Entity-based delete | Use bulk delete query |
🧠 Key Takeaway
Don’t reach for flush() + clear() to “fix visibility.”
They’re low-level hammers that often break performance and caching.
Instead:
- Use bulk operations
- Batch inserts
- Split transactions when visibility matters
- Or use native SQL when you’re replacing data wholesale
Your Spring Boot + JPA app will thank you with faster, cleaner transactions.
💬 What about you?
Have you run into flush() / clear() performance issues before?
How did you handle them — batching, bulk queries, or native SQL?
Drop your experience below 👇 — it helps others avoid the same trap.
Top comments (0)