12

I have a question regarding Hibernate caching mechanism.I have read in the articles that native SQLquery execution in hibernate, invalidates all regions of cache because hibernate does not have any idea regarding which particular entity it is going to affect. Here all regions of cache means are we talking about various regions of the second level cache or both levels of cache(first level cache,second level cache) or only second level cache or only first level cache?

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
Beast
  • 639
  • 2
  • 14
  • 29
  • 1
    just a remark, that: execution of native query with UPDATE statement will invalidate all regions of second level cache. If you execute: Insert or Delete native query, then cache will not be invalidated. Correct me, if i'm wrong. – edward_wong Sep 15 '15 at 14:12

1 Answers1

29

Using SQLQuery, Hibernate couldn't know what cache regions you might affect, but luckily you can explicitly instruct it:

SQLQuery sqlQuery = session.createSQLQuery(
    "UPDATE CUSTOMER SET ... WHERE ..."); 
sqlQuery.addSynchronizedEntityClass(Person.class); int
int updateCount = sqlQuery.executeUpdate();

This way it knows what query caches to invalidate, otherwise it may discard everything:

private static class EntityCleanup {
    private final EntityRegionAccessStrategy cacheAccess;
    private final SoftLock cacheLock;

    private EntityCleanup(EntityRegionAccessStrategy cacheAccess) {
        this.cacheAccess = cacheAccess;
        this.cacheLock = cacheAccess.lockRegion();
        cacheAccess.removeAll();
    }

    private void release() {
        cacheAccess.unlockRegion( cacheLock );
    }
}

private static class CollectionCleanup {
    private final CollectionRegionAccessStrategy cacheAccess;
    private final SoftLock cacheLock;

    private CollectionCleanup(CollectionRegionAccessStrategy cacheAccess) {
        this.cacheAccess = cacheAccess;
        this.cacheLock = cacheAccess.lockRegion();
        cacheAccess.removeAll();
    }

    private void release() {
        cacheAccess.unlockRegion( cacheLock );
    }
}

private class NaturalIdCleanup {
    private final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy;
    private final SoftLock cacheLock;

    public NaturalIdCleanup(NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy) {
        this.naturalIdCacheAccessStrategy = naturalIdCacheAccessStrategy;
        this.cacheLock = naturalIdCacheAccessStrategy.lockRegion();
        naturalIdCacheAccessStrategy.removeAll();
    }

    private void release() {
        naturalIdCacheAccessStrategy.unlockRegion( cacheLock );
    }
}

So, as you can see the whole data from the Region is evicted.

This only affects the second-level cache. The first level cache (a.k.a. Session) is not cleared every time you run a native query, because that would detach all your current "attached entities", having unexpected consequences in entity state expectations. But before every query (HQL or native) the session is flushed so the DB and the session are in sync prior to executing the query, hence the 1st level cache is consistent before issuing a new select.

A whole region would get invalidated, not the whole second-level cache. An entity defines a cache region, so updating a specific entity table would only remove all the entities that belong to that particular table(s) that were affected by the native query.

But overriding the query-space definition associated with a native query is a way to customize Hibernate not to clear the cache region as it would do using the default implementation.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • so it will invalidate only the second level cache ? But what about the first level cache then it won't invalidate it? As far I know this particluar function tells hibernate that which particular entity to invalidate rather than invalidating the whole cache. – Beast May 31 '14 at 19:52
  • Any body has answer to the above question? Can anybody clear my understanding? – Beast Jun 01 '14 at 08:51
  • Check my updated response, I hope it's clear enough. – Vlad Mihalcea Jun 01 '14 at 10:21
  • @Thanks Vlad for your answer. Apology for late reply. – Beast Jun 06 '14 at 08:38
  • One question apology for eating your head, If i dont add the statement sqlQuery.addSynchronizedEntityClass(Person.class) still it would flush the first level cache ? Because hibernate does not have any idea regarding which particular table the native query has an effect? – Beast Jun 06 '14 at 09:22
  • 1
    The first level cache is flushed when you run any query. Hibernate has pending changes and any query must go to the DB, so the pending queries need to be flushed so the query returns consistent results. – Vlad Mihalcea Jun 06 '14 at 09:24
  • That should depend upon you FlushMode If you FlushMode is Auto then it will automatically determines when should it flush the changes to the database. If your changes in the session or first level cache affect the query result then it would flush the changes. Is my understanding correct ? – Beast Jun 06 '14 at 09:32
  • "The Session is sometimes flushed before query execution in order to ensure that queries never return stale state." I think it's smart after-all. – Vlad Mihalcea Jun 06 '14 at 09:36
  • Vlad, does hibernate 4.x also clear 2nd level cache for every native query with update statement? Or this problem is actual only for hibernate 3.x? – Johnny Nov 30 '15 at 06:19
  • @VladMihalcea: I am using a native SQL query in JPA which loads some data from the db. The problem is when I update the data in one of columns from sql developer, and then try to load the same data, the changes are not reflected in the JPA side. If I restart the web applcn, then it works fine. We are using this databsource- com.mchange.v2.c3p0.ComboPooledDataSource. What could be the issue? Why is JPA Hibernate using the old data from the session rather than loading the latest data from db. Please suggest. – Farhan stands with Palestine Nov 15 '17 at 14:35
  • Sounds like a question, rather than a comment to an answer. – Vlad Mihalcea Nov 15 '17 at 14:38