1. Overview

In this tutorial, we’ll review how entities are managed in JPA and then explore a scenario where the persistence context may not return fresh data due to external changes.

2. Persistence Context

Each EntityManager is associated with a persistence context that stores managed entities in memory. Whenever we perform any data operations on an entity via the EntityManager, the entity becomes managed by the persistence context.

When we retrieve the entity again, JPA returns the managed entity from the persistence context instead of fetching it from the database. This caching mechanism helps improve the performance without fetching the same data repetitively from the database.

The persistence context is also known as the first-level (L1) cache in JPA. 

3. Scenario Setup

First, let’s create a simple entity class:

@Entity
@Table(name = "person")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    // constructors, getters and setters
}

Next, we’ll create a Person entity and persist it to the database:

EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();

Person person = new Person();
person.setName("David Jones");
entityManager.persist(person);

transaction.commit();

4. Without Clearing Managed Entities

Our sample data is ready. Let’s mask the Person‘s name field using an SQL update query. We obtain a JDBC connection from the EntityManager and execute the update query:

Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> {
    try (PreparedStatement pStmt = connection.prepareStatement("UPDATE person SET name=? WHERE id=?")) {
        pStmt.setString(1, "*****");
        pStmt.setLong(2, 1);
        pStmt.executeUpdate();
    }
});

Now, we can retrieve the same entity again by the EntityManager:

Person updatedPerson = entityManager.find(Person.class, 1);

Intuitively, we’d assume the retrieved Person entity’s name has been masked already due to the update query. However, the name remains “David Jones” when we verify the name of the entity:

assertThat(updatedPerson.getName()).isNotEqualTo("*****");

This occurs because JPA retrieves the managed entity from the persistence context which hasn’t been updated accordingly since the JDBC data update. When data changes occur outside of the EntityManager, it’s  unaware of them and is unable to update the corresponding managed entities.

5. With Clearing Managed Entities

Consequently, whenever we make any changes to the database data without using EntityManager, we must force JPA to clear all managed entities from the persistence context such that entities can be fetched again from the database.

To do so, we invoke the clear() method of the EntityManager:

entityManager.clear();

This action detaches all managed entities from the persistence context ensuring JPA no longer keeps track of those entities.

Subsequently, we can retrieve the same entity using the EntityManager again. We’ll see the following SQL being executed from the console log, if we enable hibernate.show_sql option in the persistence unit configuration:

Hibernate: 
    select
        p1_0.id,
        p1_0.name 
    from
        person p1_0 
    where
        p1_0.id=?

This SQL statement indicates that the EntityManager executes a select query to retrieve fresh data from the database. We can expect the Person entity’s name has been masked this time:

assertThat(updatedPerson.getName()).isEqualTo("*****");

6. Conclusion

In this article, we’ve learned the role of persistence context in JPA. The persistence context won’t reload the entities if we perform data changes without involving EntityManager. We need to invoke the clear() method of EntityManager to remove all manage entities and allow reloading entities from the database again.

As always, the full source code of the examples can be found over on GitHub.