EntityManager & Persistence Context
IntermediateThe 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.
@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.
@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.
@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 AriaWhat is the JPA Persistence Context and what role does it play?
What are the four entity lifecycle states in JPA?
What is dirty checking in Hibernate and when does it occur?
What is the difference between persist() and merge()?
Why does em.find() called twice in one transaction not issue two SQL queries?
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.