The labs on this site need JavaScript to run. The same content is written out below.
Fundamentals › JPA
What is a persistence context, and what does it do for you?
A persistence context is the place where the EntityManager (em in the code) holds entity objects and watches them. An entity is a Java object that corresponds to one row in the database, and here it is Member. An entity that you read from the database with em.find goes into the persistence context (an entity passed to em.persist, which you use to save a new entity, goes in too, but this lab does not use it). JPA watches the entities inside it, so if you only change a value, JPA builds an UPDATE statement and sends it when the transaction commits. If you call find again with the same id, it does not go to the database and gives you the object it was holding. On the other hand, an object you create yourself with new, or one you removed with em.detach, is an ordinary Java object that is not in the persistence context, so changing its values does not reach the database. This lab changes how you get the Member and runs the same code (change name to Bob and commit), and shows the name in the database after the commit and the number of UPDATE and SELECT statements that were sent.
Checked against real execution · Ran all 6 combinations on Hibernate 7.4.5 and H2 2.4.240, and checked against them (the rules were checked against the Jakarta Persistence 3.2 specification)
em.getTransaction().begin();
// find: the entity read from the database goes into the persistence context
Member m = em.find(Member.class, 1L);
m.setName("Bob");
em.getTransaction().commit();
This was checked against real execution on Hibernate 7.4.5 and H2 2.4.240 (in-memory database). A SessionFactory was built directly without Spring, on JDK 21. All 6 combinations were compiled and run with the lines of code on the screen exactly as they are (including the comment line). For each combination, the database and the SessionFactory were created anew, and one row (1, Alice) was put into the database with JDBC before the run. The SQL of this setup was not counted. What does not appear on the screen is the Member entity (with @Entity, an id with @Id, a name, a constructor, setName and getName) and the code that gets and closes the EntityManager, and the transaction was started and committed directly on em. What was checked against the run is the name read through another connection (plain JDBC) after the commit, the number of UPDATE and SELECT statements counted with Hibernate's StatementInspector (the SQL text and the lines that sent it were kept in the output of the checking run), and the m == m2 that the code printed on the screen. The facts that the step sentences rely on were also measured in a second run that inserts a measuring line right below each line of code. They are em.contains(m) (true after find, and false for an object created with new and after detach), that the UPDATE is sent not on the m.setName line but on the commit line, that when find is called again with the same id, the find combination does not add a SELECT and receives the same object whose name is already Bob even though the commit has not happened yet (and that the new and detach combinations add a SELECT and receive a new object whose name is Alice), and that Hibernate separately remembers the values from the time it read them (seen through an internal API, [Alice] after the read and no memory after detach). It was also run separately that if you change the name to Bob and then change it back to Alice before the commit, no UPDATE is sent, and this was used as the evidence that it compares with the first values, not with whether a changing call was made. It was also run that if you pass an object created with new to em.persist, em.contains becomes true and an INSERT is sent at commit. What the documentation defines is in the Jakarta Persistence 3.2 specification: Section 3.1 (each EntityManager has a persistence context, and it holds only one entity instance for any given identifier), Section 3.3 (the lifecycle of an entity instance: managed is the state of belonging to a persistence context, and detached is the state of not belonging or no longer belonging), Section 3.3.2 (becoming managed with persist), Section 3.3.4 (the state of a persistent entity is synchronized to the database at transaction commit), and Sections 3.3.6 and 3.3.7 (an instance removed from the persistence context lives on outside it). The specification page came back only as a summary, so the full original text of these sections was not read. The way of remembering the first values and comparing them, and the shape of the SQL that is sent, are not defined by the specification but are behavior observed on Hibernate 7.4.5, so the screen shows only the counts, not the SQL text. This lab has the shape of creating em directly, so it does not cover using it with Spring's @Transactional (where the persistence context is created and ended together with the transaction), write-behind, the flush just before a query runs, or saving a new entity with em.persist, and the details are covered in the lab "When does JPA send SQL?". A check against execution on MySQL was not done.
Previous lab: Entities · Next lab: Repositories
Struck-through text was removed from the first version of the code; highlighted text was added.