Explain Codes LogoExplain Codes Logo

What is Persistence Context?

java
entity-manager
persistence-context
entity-lifecycle
Anton ShumikhinbyAnton Shumikhin·Jan 28, 2025
TLDR

The Persistence Context is a set of managed entity instances whose purpose is to synchronize entity state with the database. As a cache managed by the EntityManager, it minimizes the number of database hits by tracking changes throughout a transaction. Like a write-behind cache, these updates are only sent to the database at transaction commit:

// EntityManager setup: like you set up for a first date, carefully and precisely EntityManager em = entityManagerFactory.createEntityManager(); EntityTransaction transaction = em.getTransaction(); // The deal opens transaction.begin(); // Entity manipulation: in the persistence world, it's completely normal to "find" an object in one line Book book = em.find(Book.class, id); // Cached in Persistence Context // Operations on entity within transaction transaction.commit(); // Magic happens: The DB knows everything em.close(); // Always remember to close

In this scenario, the em.find() inspects the Persistence Context before querying the database. And transaction.commit() triggers the flush operation where the DB gets updated, keeping the book instance up-to-date.

EntityManager and its Factory

EntityManager is your gateway to the Persistence Context. Its role is to manage entity life-cycle, typically involving creation and deletion of entities, finding entities, and running queries.

EntityManagerFactory, thoroughly heavy, creates EntityManager instances. Conventionally, it's made once when the application boots:

// It's Factory time, not tea time! Though you can grab one EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit"); EntityManager em = emf.createEntityManager(); // Factory doing its job

Entity lifecycle: Birth to Death

Every entity in the Persistence Context has a lifecycle:

  • New/Transient: Entity just got created. It's new in the town, yet to be recognized.
  • Managed: Entity got street cred. It's now under the Persistence Context's surveillance.
  • Detached: Entity is on vacation. The Persistence Context no longer babysits it.
  • Removed: Entity is remembered in memories. It‘s scheduled to be extinct from the database on transaction commit.

First-level cache: Your secret weapon

The built-in first-level cache of the Persistence Context preempts the need to hit the database every time data is needed. If an entity is already in the cache, no expensive database call is made:

// The cache works undercover. The database can relax Customer customer = em.find(Customer.class, customerID);

Flushing: The unseen hero

Flush, in Persistence Context terms, is a forced synchronization of the cache state with the database. If you were to update an entity within a transaction, those changes won't hit the database immediately. Instead, the updates are flushed—sent to the database all at once when the transaction commits:

// Updates are like secrets, not immediately visible to the database em.getTransaction().begin(); Book book = em.find(Book.class, 1); book.setTitle("Coding with Java"); // Might as well change Harry Potter's name internally // The secret is out: flush happens here! em.getTransaction().commit();

Concurrency control: Chaos manager

Ever wondered how to manage concurrency? The Persistence Context can help by minimizing the duration of possible conflicting updates using optimistic locking. This approach, combined with managing the lifecycle of entities, is an effective way to address concurrent data access.

Entity reattachment: Detach, relax, and merge

An entity may get detached when its Persistence Context closes or it's explicitly evicted. Fear not, you can merge it and sync its state with the latest data in the database:

// Vacation's over. Time to merge and get back to work. EntityManager newEm = entityManagerFactory.createEntityManager(); newEm.getTransaction().begin(); Book detachedBook = newEm.merge(book); // Re-attaching a detached entity newEm.getTransaction().commit();

CRUD operations: Make the loop

The Persistence Context ropes in CRUD operations: Create, Read, Update, and Delete. Here's probably how your usual interaction with an entity looks like:

  • Create: persist(entity)
  • Read: find(entityClass, primaryKey) or createQuery()
  • Update: Just modify a managed entity. It's a different world here
  • Delete: remove(entity)