@Modifying Bulk Updates and Deletes in Spring Data JPA
Fast recall
Quick revision
Core answer
@Modifying requires an active transaction), `clearAutomatically = true` (clears L1 after the operation so subsequent findById does not return stale entities), and `flushAutomatically = true` (flushes pending writes BEFORE the bulk SQL runs). Hidden costs: @Modifying bypasses lifecycle callbacks (@PreUpdate, @PostUpdate, Spring Data Auditing), cascade rules (CascadeType.REMOVE does not propagate), @Version increment, and Hibernate interceptors. For audited or cascading entities, prefer per-entity loops with `em.flush() + em.clear()` per batch so callbacks fire; for true bulk operations on simple state-change tables, @Modifying is orders of magnitude faster.Whether the candidate knows the limitations of Hibernate's first-level cache when bypassing entity-level operations with bulk DML
Using @Modifying without clearing the persistence context — subsequent reads in the same transaction return stale entities from the first-level cache rather than the updated values
Explain that @Modifying bypasses entity lifecycle callbacks (@PreUpdate, @PostUpdate) and Hibernate interceptors, so audit logging via these mechanisms will not fire. Recommend using entity-level operations for audited changes
Interview-ready guide
Interview answer
Bulk updates and deletes operate directly in SQL, bypassing Hibernate's entity lifecycle. Dramatically more efficient for large datasets but require explicit cache management.
Basic pattern:
@Modifying
@Query("UPDATE Order o SET o.status = :status WHERE o.createdAt < :cutoff")
int updateStatusForOldOrders(@Param("status") String status, @Param("cutoff") LocalDateTime cutoff);@Modifyingis required for UPDATE/DELETE JPQL- Returns the number of affected rows
The first-level cache problem:
- L1 is not updated by bulk SQL
- A
findById()after a bulk update returns the pre-update version from cache - Fix:
@Modifying(clearAutomatically = true)
What is bypassed:
@PreUpdate,@PostUpdatecallbacks — not called- Spring Data
@LastModifiedDateauditing — not updated automatically - Cascade rules — child entities are not affected unless SQL explicitly targets them
@Version— not incremented unless explicit
For `lastModifiedAt`: include it in the JPQL: SET o.status = :status, o.lastModifiedAt = CURRENT_TIMESTAMP.
@Modifying Bulk Updates and Deletes in Spring Data JPA
Direct Answer
Bulk UPDATE/DELETE in Spring Data JPA uses @Modifying with @Query to issue DML directly to the database, bypassing per-entity loads and dirty checking — essential for thousands of rows. Three critical configurations: @Transactional (@Modifying requires an active transaction), clearAutomatically = true (clears L1 after the operation so subsequent findById does not return stale entities), and flushAutomatically = true (flushes pending writes BEFORE the bulk SQL runs). Hidden costs: @Modifying bypasses lifecycle callbacks (@PreUpdate, @PostUpdate, Spring Data Auditing), cascade rules (CascadeType.REMOVE does not propagate), @Version increment, and Hibernate interceptors. For audited or cascading entities, prefer per-entity loops with em.flush() + em.clear() per batch so callbacks fire; for true bulk operations on simple state-change tables, @Modifying is orders of magnitude faster.
Common Mistakes & Pitfalls
Using @Modifying without clearing the persistence context — subsequent reads in the same transaction return stale entities from the first-level cache rather than the updated values
Check your understanding
Answer these without looking back at the article.
- Why does a bulk DELETE not trigger `CascadeType.REMOVE` on child entities?
- What happens if you use `@Modifying` without `@Transactional`?
- How would you handle bulk updates that also need to be captured by an audit log that uses `@PreUpdate`?
- When should you reach for Spring Batch instead of `@Modifying`?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.