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

25 min

Lesson 4 of 17

1d streak

The 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.

StateAttached to L1 Cache?Has Database Identity?Mutations Auto-Saved (Dirty Checking)?
Transient (New)NoNo (id is null)No
Managed (Persistent)YesYesYes (automatically flushed at commit)
DetachedNo (session closed)YesNo (must call merge() to reattach)
RemovedYes (marked for deletion)YesYes (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.

OrderService.java
@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

Terminal / Diagnostic Output
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

A controller or JSON serializer accessed a lazy association after the @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

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