11

Shortly, I have an entity mapped to view in DB (Oracle) with enabled 2nd level Cache (read only strategy) -- ehcache.

If I manually update some column in DB -- cache will not be updated.

I did not find any ways to do this. Only if updates will be done through Hibernate entity.

May I somehow implement this feature?

Maybe Job to monitor table (or view)? Or maybe there is some method to notify Hibernate about change in DB in concrete table.

Thanks for future answers!

Artem Vereschaka
  • 125
  • 1
  • 1
  • 9
  • 2
    Just add some function to your app that refreshes the cache. Then call this function every time it should be refreshed. There is no automagic version of such functionality - either don't change DB manually or do an function in your app to refresh cache on "administrator" (yours) request, you can also restart your app - it should force the cache to be reloaded. – Krzysztof Cichocki Oct 28 '16 at 09:52

6 Answers6

6

According to Hibernate JavaDoc, you can use org.hibernate.Cache.evictAllRegions() :

evictAllRegions() Evict all data from the cache.

Using Session and SessionFactory:

Session session = sessionFactory.getCurrentSession();

if (session != null) {
    session.clear(); // internal cache clear
}

Cache cache = sessionFactory.getCache();

if (cache != null) {
    cache.evictAllRegions(); // Evict data from all query regions.
}

1) If you need update only one entity (if directly from db you will update only certain entities) not whole session, you can use

evictEntityRegion(Class entityClass) Evicts all entity data from the given region (i.e.

2) If you have a lot of entities, that can be updated directly from db you can use this method that evicts all entities from 2nd level cache (we can expose this method to admins through JMX or other admin tools):

/**
 * Evicts all second level cache hibernate entites. This is generally only
 * needed when an external application modifies the game databaase.
 */
public void evict2ndLevelCache() {
    try {
        Map<String, ClassMetadata> classesMetadata = sessionFactory.getAllClassMetadata();
        Cache cache = sessionFactory.getCache();
        for (String entityName : classesMetadata.keySet()) {
            logger.info("Evicting Entity from 2nd level cache: " + entityName);
            cache.evictEntityRegion(entityName);
        }
    } catch (Exception e) {
        logger.logp(Level.SEVERE, "SessionController", "evict2ndLevelCache", "Error evicting 2nd level hibernate cache entities: ", e);
    }
}

3) Another approach is described here for postgresql+hibernate, I think you can do something similar for Oracle like this

Corwin Newall
  • 508
  • 8
  • 18
Daniyar
  • 815
  • 1
  • 9
  • 22
  • I do not need to clear all the cache. I need to update only cache related to one entity. – Artem Vereschaka Oct 28 '16 at 09:38
  • then you should use session.refresh(entity) as Vimal wrote – Daniyar Oct 28 '16 at 09:39
  • that means that I must call refresh *before* retrieving of data? I cannot see any sense to use cache in case with refresh call – Artem Vereschaka Oct 28 '16 at 09:41
  • look on this, I hope it will help you this is about postgresql but i think it also possible in oracle http://stackoverflow.com/questions/13258976/how-to-refresh-jpa-entities-when-backend-database-changes-asynchronously – Daniyar Oct 28 '16 at 09:46
  • This might work with some scheduler. Thanks for links and such a detailed answer. – Artem Vereschaka Oct 28 '16 at 10:49
  • @ArtemVereschaka I'm glad I was able to help :) – Daniyar Oct 28 '16 at 11:06
  • @Daniyar If my application is deployed on multiple servers. Will this solution work in that case as well? – oOXAam Feb 27 '20 at 07:44
  • @oOXAam do you mean you have 2 or more replicas of your application? – Daniyar Feb 27 '20 at 08:00
  • @Daniyar yes. I have a load balancer on which we have 4 servers and we are running the same application on all of them. I have following confusion: 1- If I updated the user entity from server 1 and clear the cache by code (as mentioned above) will the cache of all other 3 servers be updated or not? – oOXAam Feb 27 '20 at 08:05
2

Use debezium for asynchronous cache updation from your database. You can know more by visiting https://debezium.io/

Also this article is very helpful as it gives direct implementation https://debezium.io/blog/2018/12/05/automating-cache-invalidation-with-change-data-capture/

Vishal Pawar
  • 750
  • 7
  • 11
  • 1
    I've blogged about this particular CDC use case of cache invalidation here: https://debezium.io/blog/2018/12/05/automating-cache-invalidation-with-change-data-capture/. – Gunnar Apr 23 '20 at 21:31
1

As mentioned, when you update DB from the back-end manually (not though application/ hibernate session), the cache is not updated. And your application remains ignorant about it.

To tell the app about the change, you need to refresh the entire cache or part of the cache related to the entity depending on the case. This can be one in two ways:

1- Restart the application - In this case the cache will be rebuild with updated DB change.

2- Trigger the update w/o restarting the app - You need not restart the app but you want to tell you application that the current cache in invalid and it should be refreshed.

  • You can give this external push to you app in many ways. Few are listed below.

    1. Through JMX.
    2. Through a servlet with a published URL to refresh the cache. Hit the URL after you change tables in DB.
    3. Implementing a trigger on the database that call a listener on the application.
  • While implementing the external push/ admin task, you can call a suitable cache related method based on your requirement to invalidate cache/ refresh cache. Examples: Session.refresh(), Cache.evictAllRegions(), Cache.evictEntityRegion(entityName) etc as described in other posts.

Dexter
  • 4,036
  • 3
  • 47
  • 55
0

You will find here the way to control the second level cache:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html#performance-sessioncache

mvera
  • 904
  • 12
  • 23
-1

You can use session.refresh() method to reload the objects which are currently held in session.

Read object loading for more detail.

Vimal Bera
  • 10,346
  • 4
  • 25
  • 47
  • Each time when I am trying to use my entity -- call refresh method? Is there any sense to use cache if I will call refresh each time on access? – Artem Vereschaka Oct 28 '16 at 09:36
  • You can have separate query that does this refresh, and call it when u only want to refresh the data. – Oleg Kuts Jul 04 '19 at 18:06
-1

As of JEE 7.0:

myStatelessDaoBean.getSession().evict(MyEntity.class);
Zon
  • 18,610
  • 7
  • 91
  • 99