1

I'm using two EntityManager instances in standalone application with RESOURCE_LOCAL transaction type. I perform such operations:

  • Save entity using first EntityManager (em1)
  • Update entity using second EntityManager (em2)
  • Read entity using first EntityManager (em1)

The problem is that em1 on the 3rd step doesn't see updates done by em2.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");

// Step 1: create entity
EntityManager em1 = emf.createEntityManager();
em1.getTransaction().begin();
Article article = new Article("article_1");
em1.persist(article);
em1.getTransaction().commit();

// Step 2: update entity
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
Article articleForUpdate = em2.find(Article.class, 1L);
articleForUpdate.setName("updated article_1");
em2.persist(articleForUpdate);
em2.getTransaction().commit();

// Step 3: read updated entity
em1.getTransaction().begin();
Article updatedArticle = em1.find(Article.class, 1L);
em1.getTransaction().commit();

log.info("updated entity: {}", updatedArticle); // logs stale data

em1.close();
em2.close();
emf.close();

Can anyone explain why em1 reads stale data?

ako
  • 350
  • 1
  • 3
  • 14
  • 1
    Many reasons can occur ... do You try `flush()` or `evict()` cahce? Described behaviour is generally known and correct. – Jacek Cz Apr 16 '18 at 09:43

2 Answers2

2

The EntityManager looks first in its first level cache if a reference of the entity exists before requesting the database.
The EntityManager instance referenced by the em1 variable has the Article entity with the id 1 in cache as it has persisted it.
So this statement will retrieve the entity from the cache :

Article updatedArticle = em1.find(Article.class, 1L);

To prevent this behavior you have multiple ways :

  • detach the entity from the EntityManager context by invoking EntityManager.detach().
  • refresh the state of the entity from the database,by invoking EntityManager.refresh(). In this case, the the find() query is not any longer required.
  • more radical : clear the EntityManager context by invoking : EntityManager.clear()
davidxxx
  • 104,693
  • 13
  • 159
  • 179
  • 1
    Is it a good idea to create a new EntityManager for each transaction if it's an application managed EntityManager? – ako Apr 16 '18 at 10:21
  • `EntityManager` is designed to work in this way : one transaction by EntityManager. The question I wonder as I read your code is why do you create so many transactions? Transactions could be assimilated to clients requests. If you have 3 distinct requests, yes 3 EntityManager created seems fine. But is it your use case ? – davidxxx Apr 16 '18 at 10:39
  • 1
    Basically, I want to test how pessimistic locking works. It means that I need to run read/write operations in concurrent transactions (i.e in different threads). While writing tests I observed that EntityManager read stale data and couldn't understand why. So I wrote a simplified example without threads and locks. – ako Apr 16 '18 at 10:55
  • In this case your sample code using multiple EM is fine. – davidxxx Apr 16 '18 at 11:02
1

EntityManger 1, has its own cached version of your Article object.

Until it is managed by it, it will give you old version. If you want to do it like that, by two different entity managers you should use REFRESH instead of find.

em1.refresh(article);

About first level cache : LINK

About refreshing LINK

Emil Hotkowski
  • 1,905
  • 11
  • 19