0

Why does JPA EntityManager execute a mutable query e.g. UPDATE against DB directly, but choose to execute a SELECT query first against the persistence context (aka second-level cache) below (tested with Hibernate 5)?

It doesn't seem the JPA spec has prescribed to do so. Is it simply to gain better performance at risk of inconsistency?

/* ... */

String name = em.find(Person.class, 1).getName();

em.getTransaction().begin();
em.createQuery("update Person set name='new' where id=1")
  .executeUpdate(); 
em.getTransaction().commit(); // DB updated but the entity not

Person p = em
  .createQuery("select p from Person p where id=1", Person.class)
  .getSingleResult(); // the stale entity returned

assertEqual(name, p.getName()); // true
sof
  • 9,113
  • 16
  • 57
  • 83
  • 1
    Because the spec says so. UPDATE/DELETE queries are against the DB only and don't respect cascade semantics or updating of fields in in-memory instances – Neil Stockton Sep 30 '15 at 06:52

1 Answers1

1

Probably for performance reasons, if JPQL query matches any entity already loaded in entity manager in memory, the specification does not require to refresh the entity in memory with the current state in the DB.

In your example, this is what happens in the background - Person entitiy with id 1 is retrieved from DB (it now exists in EntityManager cache) - person's name is updated in DB via JPQL (the entity remains unchanged) - entity person with id 1 is to be retrieved from DB -- but entity with id 1 already exists in EntityManager, therefore this instance is returned (it has old value in name) - the entity is not merged with the DB. For more info see also answer to this question.

This behavior is in compliance with the JPA requirement that in single EntityManager, there cannot be 2 instances of an entity for a single row in DB. There are only two options how to solve it, one is to merge existing entity with the state in DB, another is to ignore data in DB. JPA spec chose the second option.

If you would not retrieve Person with id 1 in the beginning, it would retrieve a new entity in the select query with the new values.

A solution to have fresh data in the entity could be to use em.clean() between the update and select queries, but mind that it will clear all entities from cache, not only the person(id=1) entity, and it could have other sideeffects.

However, a safer solution is to use em.refresh() after you changed entities with update script.

Community
  • 1
  • 1
OndroMih
  • 7,280
  • 1
  • 26
  • 44
  • This SO answer could be also helpful: http://stackoverflow.com/questions/5295386/entitymanager-doesnt-refresh-the-data-after-querying – OndroMih Sep 30 '15 at 11:33