Hibernate Persistence Context

Emre DALCI
emlakjet
Published in
6 min readJul 27, 2023

In this article, we’ll talk about persistence context and its assorted properties. Before we look at persistence context, we dissect the following terms: persistence lifecycle, unit of work, and application-managed scope.

Entity States

When interacting with the persistence mechanism, the application must concern itself with the state and lifecycle of entity instances with respect to persistence. We refer to this as the persistence lifecycle: the states an entity instance goes through during its life.

JPA(Java Persistence API) defines four states. Figure 1 shows these states and their transitions.

Figure 1 Entity States and their transition

Transient State

Instances created with the new Java operator are transient, which means instances only exist in memory and the garbage collector removes them as soon as they are no longer referenced.

For example, if you create an instance with a new Item(), that object is transient and there is no expectation that it is represented in a database as long as it is in transient state.

Hibernate doesn’t provide any rollback functionality for transient instances. If we modify the name of a transient Item, we can’t automatically undo the change.

In order to change an instance state from transient to persistent state requires either a call to the persist() method or the creation of a reference from an already-persistent instance and enabled cascading of state for that mapped association.

Persistent State

A persistent entity instance has a representation in the database. It’s stored in the database — or it will be stored when the unit of work completes. Hibernate will keep the database representation up to date when the application marks the changes as to be committed.

The application may have created instances and then made them persistent by calling persist(). Instances may have also become persistent when the application created a reference to the object from another persistent instance that the JPA provider already manages. A persistent entity instance may be an instance retrieved from the database by executing a query.

Figure 2 Persistent State

Detached State

Detached instances have a representation in the database, but changes to the instances will not be reflected in the database. A detached instance can be created by ending the unit of work and close persistence context. We could discard the reference and let the garbage collector reclaim the memory. Or, we could continue working with the data in the detached state and later call the merge() method to save our modifications in a new unit of work.

Figure 3 Detached State

Removed State

We can delete a persistent entity instance from the database in several ways. For example, we can remove it with remove(). It may also become available for deletion if we remove a reference to it from a mapped collection with orphan removal enabled.

An entity instance is then in the removed state: the provider will delete it at the end of a unit of work. We should discard any references we may hold to it in the application after we finish working with it.

EntityManager Interface

Any transparent persistence tool includes a persistence manager API. This persistence manager usually provides services for basic CRUD (create, read, update, delete) operations, query execution, and controlling the persistence context. The main interface we interact with is EntityManager to create units of work.

Unit Of Work

Unit of work is a set of state-changing operations considered one (usually atomic) group. Hibernate uses sessions to express the notion of the unit of work. In order to begin a unit of work you have to open a Session and in order to end it you have to close a session.

Persistence Context

We can think of the persistence context as a service that remembers all the modifications and state changes we made to the data within a particular unit of work.

An EntityManager has a persistence context. We create a persistence context when we call createEntityManager(). We closed the context when we call close(). In JPA terminology, this is an application-managed persistence context; our application defines the scope of the persistence context, demarcating the unit of work. Refer to the link for differences between application-managed and container-managed persistence contexts.

Automatic Dirty Checking

The persistence context allows the persistence engine to perform automatic dirty checking, detecting which entity instances the application modified. The provider then synchronizes with the database the state of instances monitored by a persistence context, either automatically or on demand. Typically, when a unit of work completes, the provider propagates a state that’s held in memory to the database through the execution of SQL statements.

This flushing procedure may also occur at other times. For example, Hibernate may synchronize with the database before the execution of a query. This ensures that queries are aware of changes made earlier during the unit of work.

First-Level Cache

The persistence context also acts as a first-level cache; it remembers all entity instances handled in a particular unit of work. For example, if we ask Hibernate to load an entity instance using a primary key value, Hibernate can first check the current unit of work in the persistence context. If Hibernate finds the entity instance in the persistence context, no database hit occurs — this is a repeatable read for an application.

This cache also affects the results of the javax.persistence.Query API. Hibernate reads the SQL result set of a query and transforms it into entity instances. This process first tries to resolve every entity instance in the persistence context by identifier lookup. Only if an instance with the same identifier value can’t be found in the current persistence context, Hibernate read the rest of the data from the result-set row. Hibernate ignores any potentially new data in the result set, due to read-committed transaction isolation at the database level, if the entity instance is already present in the persistence context.

The persistence context cache ensures the following:

  • The persistence layer is resilient to stack overflows in the case of circular references.
  • There can never be conflicting representations of the same database row at the end of a unit of work. The provider can safely write all changes made to an entity instance to the database.
  • Likewise, changes made in a particular persistence context are always immediately visible to all other code executed inside that unit of work and its persistence context. JPA guarantees repeatable entity-instance reads.

Guaranteed Scope Of Object Identity

The persistence context guarantees the equivalence of persistent identity (database row) and Java identity in the scope of a single persistence context. So if we ask a Hibernate to load a specific instance multiple times we will actually get back the same instance.

We should keep in mind that when the instance state is changed, the persistence context doesn’t provide a guaranteed scope of object identity. Whenever we work with instances in a detached state and test them for equality(usually in hash-based collections), we need to supply our own implementation of the equals() and hashCode() methods for our mapped entity class.

In conclusion, we explore Hibernate Persistence Context and its properties, focusing on entity states, automatic dirty checking, and object identity scope. Understanding these concepts is crucial for efficient data management and optimizing application performance in the context of persistence.

--

--