Search This Blog

Saturday, 31 August 2013

CacheModes in Hibernate - continued

In the previous post we saw the GET and IGNORE values for CacheMode. The other values are PUT and REFRESH. Consider the below code:
public static void testPutMode() {
    Statistics statistics = sessionFactory.getStatistics();

    Session session1 = sessionFactory.openSession();
    System.out.println("Cache Put Count : "
        + statistics.getSecondLevelCachePutCount());
    session1.get(BookType.class, 1);
    System.out.println("Cache Put Count : "
        + statistics.getSecondLevelCachePutCount());
    session1.close();
    System.out.println("Running in PUT mode");
    Session session2 = sessionFactory.openSession();
    session2.setCacheMode(CacheMode.PUT);
    session2.get(BookType.class, 1);
    session2.get(BookType.class, 2);
    session2.get(BookType.class, 3);
    System.out.println("Cache Put Count : "
        + statistics.getSecondLevelCachePutCount());
    System.out.println("Cache Hit Count : "
        + statistics.getSecondLevelCacheHitCount());
    session2.close();
}
In the PUT mode, the session will add records to the second level cache. However it will not read any records from the cache. As the second session uses the PUT CacheMode it does nor read the record with id 1 from the cache. It fires three select queries for all three records, adding two of them to the cache. The output from above method is :
Cache Put Count : 0
Hibernate: 
    /* load com.object.cache.BookType */ 
    select
        booktype0_.ID as ID0_0_,
        booktype0_.NAME as NAME0_0_ 
    from
        BOOK_TYPE_MASTER booktype0_ 
    where
        booktype0_.ID=?
Cache Put Count : 1
Running in PUT mode
Hibernate: 
    /* load com.object.cache.BookType */ 
    select
        booktype0_.ID as ID0_0_,
        booktype0_.NAME as NAME0_0_ 
    from
        BOOK_TYPE_MASTER booktype0_ 
    where
        booktype0_.ID=?
Hibernate: 
    /* load com.object.cache.BookType */ 
    select
        booktype0_.ID as ID0_0_,
        booktype0_.NAME as NAME0_0_ 
    from
        BOOK_TYPE_MASTER booktype0_ 
    where
        booktype0_.ID=?
Hibernate: 
    /* load com.object.cache.BookType */ 
    select
        booktype0_.ID as ID0_0_,
        booktype0_.NAME as NAME0_0_ 
    from
        BOOK_TYPE_MASTER booktype0_ 
    where
        booktype0_.ID=?
Cache Put Count : 3
Cache Hit Count : 0
The other CacheMode is REFRESH. This has the same behavior as PUT (Or does it ?) From the Hibernate docs:
CacheMode.PUT: will write items to the second-level cache. 
Do not read from the second-level cache
CacheMode.REFRESH: will write items to the second-level cache. 
Do not read from the second-level cache. 
Bypass the effect of hibernate.cache.use_minimal_puts forcing 
a refresh of the second-level cache for all items read from the database
I tested the above method for Refresh but did not find any difference in the result. I found one link that seemed to add something to this explanation.
What is the default value for CacheMode ?
public static void testDefaultCacheMode() {
    Session session = sessionFactory.openSession();
    System.out.println(session.getCacheMode());
}
The output is :
NORMAL

7 comments:

  1. Difference between PUT and REFRESH, it might be this:

    - as you can see PUT adds only the last 2 new objects, it does not touch the first one, existing in the second level cache
    - REFRESH would also add the last 2 new objects AND trigger a refresh the first one, existing in the second level cache

    ReplyDelete
    Replies
    1. Thanks for the very useful examples. I learned it all from here about CachMode options and, at the end of your article, i made the connection above.

      There is also a bit in the Hibernate doc, about REFRESH (neglect the use of the region parameter and focus only on the REFRESH part):



      20.4.2. Query cache regions

      If you require fine-grained control over query cache expiration policies, you can specify a named cache region for a particular query by calling Query.setCacheRegion().

      List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
      .setEntity("blogger", blogger)
      .setMaxResults(15)
      .setCacheable(true)
      .setCacheRegion("frontpages")
      .list();
      If you want to force the query cache to refresh one of its regions (disregard any cached results it finds there) you can use org.hibernate.Query.setCacheMode(CacheMode.REFRESH). In conjunction with the region you have defined for the given query, Hibernate will selectively force the results cached in that particular region to be refreshed. This is particularly useful in cases where underlying data may have been updated via a separate process and is a far more efficient alternative to bulk eviction of the region via org.hibernate.SessionFactory.evictQueries().

      Delete
    2. This comment has been removed by the author.

      Delete
    3. I admit the HIbernate doc extract i posted is about "querry cache" whereas you use "session.get" in your examples which does NOT use query caches, but the understanding of REFRESH is the same: If an object with the Id specified in the method call (in case on session.get) or with the id extracted from the DB (in the case of a querry, which always interogates the DB, unless query cash is used) is present in the second level cache, it will be refreshed (session.refresh(p))

      Delete
    4. Interesting thoughts Vlad, This is good. Let me try and verify the same. Ill get back on this. Thanks

      Delete