Foundation · Build
The Persistence Context & Entity Lifecycle
Track an entity through transient, managed, detached, and removed states, and see why dirty checking—not your setters—writes changes.
Primary source: Hibernate 6.6 Reference: Persistence Context & Entity Lifecycle
Lesson 4 of 17
1d streakThe Architectural Question
Developers who treat entities as plain rows call save in loops, merge detached copies blindly, and lose updates under concurrency because they never modeled the states Hibernate actually moves data through.
What this guide proves
- Explain what the persistence context stores and when it flushes
- Distinguish managed from detached entities before mutating them
- Keep state transitions inside entities instead of setter calls
The four entity lifecycle states
Hibernate does not treat objects as simple records; it manages them inside an in-memory PersistenceContext (the First-Level Cache). An entity exists in one of four distinct states at any moment in time.
| State | Attached to L1 Cache? | Has Database Identity? | Mutations Auto-Saved (Dirty Checking)? |
|---|---|---|---|
| Transient (New) | No | No (id is null) | No |
| Managed (Persistent) | Yes | Yes | Yes (automatically flushed at commit) |
| Detached | No (session closed) | Yes | No (must call merge() to reattach) |
| Removed | Yes (marked for deletion) | Yes | Yes (SQL DELETE executed at flush) |
Dirty checking: Why you do not need to call repository.save() on updates
When an entity is loaded inside a @Transactional boundary, Hibernate takes an initial snapshot of its state. When the transaction finishes, Hibernate performs dirty checking: it compares the current entity values against the snapshot and generates SQL UPDATE statements automatically.
@Service
public class OrderService {
private final OrderRepository repository;
@Transactional
public void updateStatus(UUID orderId, String newStatus) {
OrderEntity order = repository.findById(orderId).orElseThrow();
order.setStatus(newStatus);
// NO CALL TO repository.save(order) IS NEEDED!
// Hibernate dirty checking flushes the UPDATE on transaction commit.
}
}Break it deliberately: LazyInitializationException
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.orders.domain.OrderEntity.items: could not initialize proxy - no Session
The root cause
@Transactional method returned and the Persistence Context closed. Hibernate cannot query the database without an open session.Active Recall Checkpoint
Why is spring.jpa.open-in-view=true considered an antipattern in production?
Hint: Consider database connection holding time during HTTP response rendering.
Show answer & explanation
Open Session in View (OSIV) holds physical database connections open during the entire HTTP request/response cycle, including JSON serialization and slow network writes. This causes connection pool starvation under modest traffic.
Managing entity lifecycles
Use it when
Rely on dirty checking for entity updates within @Transactional methods. Map query results to DTOs before crossing the controller boundary.
Reconsider when
Always set spring.jpa.open-in-view=false to prevent hidden lazy queries outside transaction boundaries.
Synthesis & Production Verification
Production Acceptance Criteria
- Draw the four entity states and the transitions between them
- Trigger dirty-checking with a plain state-transition method and no save()
- Explain when flush happens relative to query execution and commit
Key Takeaway
The Persistence Context manages entity state and flushes updates via dirty checking; close sessions cleanly to avoid connection starvation.
Architectural & Technical Review
Core Interview Questions
Q01What are the four entity lifecycle states in JPA?Show answer
Transient (new, unmanaged), Managed (attached to L1 cache, tracked for dirty checking), Detached (session closed), and Removed (marked for delete).
Q02Why is repository.save() unnecessary when mutating managed entities?Show answer
Hibernate dirty checking compares the entity against its loaded snapshot and flushes SQL UPDATE statements automatically upon commit.
Q03Why is spring.jpa.open-in-view=true discouraged in production?Show answer
OSIV keeps physical database connections open during HTTP response rendering, causing connection pool exhaustion under modest load.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Accessing lazy collections outside open transactions — Accessing uninitialized lazy associations after the Persistence Context closes throws LazyInitializationException.
- Pitfall: Calling merge() on detached entities blindly — Calling merge() returns a new managed instance while leaving the argument detached, causing silent lost updates if discarded.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources