Home/Learn/Hibernate & JPA/EntityManager & Persistence Context

EntityManager & Persistence Context

Intermediate
Fundamentals

The EntityManager is the gateway to JPA operations; the persistence context is its first-level cache holding managed entity instances for the duration of a transaction.

Overview

The EntityManager is the JPA API for all persistence operations: persist, merge, remove, find, and query. It manages the Persistence Context — a first-level cache (L1 cache) that holds all managed entity instances for the duration of a transaction. When you find or query an entity, the persistence context tracks it; changes are automatically flushed (dirty checking) to the database at transaction commit. Entity lifecycle states are: Transient (new, not associated), Managed (tracked by context), Detached (was managed, context closed), and Removed (marked for deletion).

Entity Lifecycle States

Understanding entity states is essential for debugging unexpected SQL behaviour. Managed entities are automatically synced to the DB on flush. Detached entities must be re-attached via merge() to persist changes.

Java — entity lifecycle states
@Service
@Transactional
public class OrderService {

    @PersistenceContext
    private EntityManager em;

    public void demonstrateLifecycle() {

        // 1. TRANSIENT — new object, not yet associated with persistence context
        Order order = new Order(customerId, items);   // transient

        // 2. MANAGED — persisted; tracked by persistence context
        em.persist(order);                             // managed (INSERT on flush)
        // order.getId() is now populated (IDENTITY strategy)

        // 3. Dirty checking — change detected automatically
        order.setStatus(OrderStatus.PLACED);           // no explicit update needed
        // Hibernate compares snapshot vs current state on flush → generates UPDATE

        // 4. DETACHED — entity no longer tracked
        em.detach(order);
        order.setStatus(OrderStatus.SHIPPED);          // change NOT persisted

        // 5. Re-attach with merge
        Order managed = em.merge(order);               // SELECT + UPDATE
        managed.setStatus(OrderStatus.DELIVERED);      // this change IS persisted

        // 6. REMOVED — marked for deletion
        em.remove(managed);                            // DELETE on flush
    }
}

Persistence Context as L1 Cache

The persistence context is a first-level cache — a find() for the same ID within the same transaction returns the same Java object without a DB query. This is why em.find() is always safe to call multiple times within a transaction.

Java — L1 cache behaviour
@Transactional
public void demonstrateL1Cache() {

    // First find — hits the database
    Order order1 = em.find(Order.class, 42L);   // SELECT issued

    // Second find — returns SAME instance from L1 cache (no SQL)
    Order order2 = em.find(Order.class, 42L);   // NO SELECT — cache hit

    assert order1 == order2;   // same Java object reference (true)

    // JPQL queries bypass L1 cache (they hit the DB)
    Order order3 = em.createQuery(
        "SELECT o FROM Order o WHERE o.id = 42", Order.class
    ).getSingleResult();
    // SQL issued BUT the result is merged into the persistence context
    // order3 == order1 (same identity within the context)

    // Clear the persistence context (lose all managed entities)
    em.clear();   // all entities become DETACHED — useful before bulk operations

    // Evict a single entity
    em.detach(order1);
}

Flush Modes & Manual Flush

By default, Hibernate flushes (synchronises the persistence context to the DB) before executing a query (FlushModeType.AUTO) and at transaction commit. You can flush manually or switch to COMMIT mode to defer all writes until commit.

Java — flush modes and manual flush
@Transactional
public void flushExample() {
    Order order = new Order(customerId, items);
    em.persist(order);

    // AUTO flush mode (default):
    // Hibernate flushes BEFORE this query to ensure it sees the new order
    List<Order> allOrders = em.createQuery("SELECT o FROM Order o", Order.class)
        .getResultList();   // flush happens here before SELECT

    // Manual flush
    em.persist(new Order(...));
    em.flush();   // forces INSERT NOW (before commit or query)

    // COMMIT mode — defer all writes to transaction commit
    em.setFlushMode(FlushModeType.COMMIT);
    // Queries no longer trigger flush — risk: query won't see uncommitted changes
}

// Useful: COMMIT flush mode for read-heavy methods in a transaction
@Transactional(readOnly = true)  // Spring sets FlushMode.MANUAL (never flushes)
public OrderDTO getOrder(Long id) {
    return em.find(Order.class, id);  // no flush, no dirty checking
}

Key Points to Remember

  • 1EntityManager manages the Persistence Context — a first-level (L1) cache per transaction.
  • 2Entity states: Transient → Managed → Detached (or Removed).
  • 3Dirty checking: Hibernate compares entity snapshots at flush time and generates UPDATE if changed.
  • 4em.find() with the same ID in the same transaction returns the same Java object (cache hit).
  • 5JPQL queries bypass L1 cache but results are merged into the persistence context.
  • 6FlushModeType.AUTO flushes before queries; COMMIT defers to transaction commit.

Interview Questions

Sign in to ask Aria
1

What is the JPA Persistence Context and what role does it play?

MediumAmazon
2

What are the four entity lifecycle states in JPA?

MediumPivotal
3

What is dirty checking in Hibernate and when does it occur?

MediumInfosys
4

What is the difference between persist() and merge()?

HardNetflix
5

Why does em.find() called twice in one transaction not issue two SQL queries?

MediumTCS

Ask Aria about EntityManager & Persistence Context

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…