1

I'm using Eclipselink for JPA, and has a webapp which reads the tables in a configured interval. The database is modified externally by a tool to publish some data. Now my webapp makes a getAll() on an Entity and gets the result as a list. However, each time I get the same result list as it was from the first query. Surprisingly, new entries and deleted entries are reflected as expected in the result list. If I update an entry it still gets the original/old result.

From my search, most of the answers in stackoverflow points to JPA level cache, and can be summarized to these

  • Disable shared cache by settings <shared-cache-mode>NONE</shared-cache-mode>
  • Optionally, selectively disable cache on entities by using @Cacheable(false)
  • Evict cache by doing em.getEntityManagerFactory().getCache().evictAll()
  • There are some other/old/pre-JPA 2.0 and vendor specific solutions which sets some properties in persistence.xml

However, none of these solutions work in my case. The closest answers I came across was this, which says that

Any IDs that are already present in the Session cache are matched up to known entities, while any IDs that are not are populated based on database state. The previously known entities are not refreshed from the database at all.

Although this is in the context of Hibernate, I hit the same issue in Eclipselink. So after introducing a refresh on the list I get the expected result.

T objects = getAll(); 
for (T objects : object) 
    em.refresh(object) 

List<T> getAll() {
  EntityManager em = entityManagerFactory.createEntityManager();
  return  em.createQuery(em.getCriteriaBuilder().createQuery(type)).getResultList();
}

Can someone explain this Session cache in the context of Eclipselink, and explain why shared-cache configuration has no impact on this ? Does a transaction transparently solve this problem (read transaction)? Is there an elegant solution/JPA configuration than doing refresh on object each time ?

Alavalathi
  • 713
  • 2
  • 9
  • 21

2 Answers2

2

From JPA 2.0 onwards, you can use query hints to bypass or refresh the cache.

Query query = entitymanager.createQuery("select student FROM Student student");
query.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);

Here is a nice explanation

Amit P
  • 467
  • 6
  • 20
  • 1
    Thanks. I'm aware of the query hint. This poses 2 problems 1) We may have to apply this to each and every type of query 2) What if we just wanted to selectively disable cache for some entities. For eg. like in `@Cacheable` annotation. – Alavalathi Jun 11 '17 at 17:21
1

Everything you've mentioned refers to the shared cache, but there are 2 levels of caching in JPA. The shared cache (2nd level) and EntityManager managed entity cache (1st level). EntityManagers are meant to represent transactional workspaces. You should obtain new Entitymanager instances where appropriate rather than use a single one for everything, or call em.clear() on it to release resources.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • I use a new entity manager instance each time. Please see the snippet in the question. – Alavalathi Jun 08 '17 at 19:24
  • Seems strange that you call getAll which obtains its own em then call refresh outside of this method call - is it the same em or a different one? Where did you make these changes, how are you configuring things (ie what server, how are you getting the entitymanagerFactory, how is it configured etc). – Chris Jun 09 '17 at 12:57
  • I use guice-persist and inject a Provider for EntityManager. The guice persistence service is started at the webapp startup. The `findAll()` will get an entityManager by doing a `emProvider.get()`. – Alavalathi Jun 11 '17 at 17:12
  • It likely gives you a proxy overtop of an EM. You'll have to check if it gives you a new one each time, or reuses EntityManagers when outside a transaction. – Chris Jun 12 '17 at 14:35