In the previous posts we saw the Hibernate cache system and the Cache modes. We saw the query cache is disabled by default.
The first step to getting the Query cache working is in the configuration file.
If I were to run the above code:
On running the same code the logs have changed when executing the cached query a second time:
What does this mean ?
The Query cache and the secondary level cache are meant to work hand in hand. In the above example the query cache did not cache the entire result set of BOOK_TYPE_MASTER records. Instead it only saved the identifier for each record. From these identifiers it then checks if the record is available - may be in the session cache, then in the second level cache. If not then it executes a select query for the missing record. In effect the absence of a second level cache could result in n additional queries being fired on the database - an "N+1" kind of scenario.
Visually imagined it would be something like this
In case of scalar data, the same is directly held in the Query cache. Any entities will be referred by their identifiers only.
The first step to getting the Query cache working is in the configuration file.
<property name="hibernate.cache.use_query_cache">true</property>This does not means all queries will be cached.If we need a particular query to be cached:
public static void testQuery() { Session session = sessionFactory.openSession(); Query query1 = session.createQuery("from BookType where name like :type"); query1.setString("type", "%"); query1.setCacheable(true); System.out.println("Query execute once"); List<BookType> bookTypes = query1.list(); for (BookType bookType : bookTypes) { System.out.println(bookType.getName()); } session.close(); session = sessionFactory.openSession(); Query query2 = session.createQuery("from BookType where name like :type"); query2.setString("type", "%"); query2.setCacheable(true); System.out.println("Query execute once again..."); bookTypes = query2.list(); for (BookType bookType : bookTypes) { System.out.println(bookType.getName()); } session.close(); }As seen above unless we specifically set a query to be cached, it does not happen. We have used two separate query objects from two different sessions to test the same query. Both have to have the setCacheable property set to true.
If I were to run the above code:
- When the code started execution:
56 [main] DEBUG net.sf.ehcache.store.MemoryStore - Initialized net.sf.ehcache .store.LruMemoryStore for com.object.cache.BookType 57 [main] DEBUG net.sf.ehcache.store.LruMemoryStore - com.object.cache.BookTy pe Cache: Using SpoolingLinkedHashMap implementation 57 [main] DEBUG net.sf.ehcache.Cache - Initialised cache: com.object.cache.Bo okType ... 229 [main] INFO org.hibernate.cache.UpdateTimestampsCache - starting update t imestamps cache at region: org.hibernate.cache.UpdateTimestampsCache ... 236 [main] DEBUG org.hibernate.cache.EhCacheProvider - started EHCache region: org.hibernate.cache.StandardQueryCache
As I have both secondary cache and the query cache enabled, they two were started. On enabling query caching, Hibernate sets up an additional cache, holding timestamps of the most recent updates to tables against which queries are cached. - When the first query was executed:
Query execute once 357 [main] DEBUG org.hibernate.cache.StandardQueryCache - checking cached quer y results in region: org.hibernate.cache.StandardQueryCache 357 [main] DEBUG org.hibernate.cache.EhCache - key: sql: select booktype0_.ID as ID0_, booktype0_.NAME as NAME0_ from BOOK_TYPE_MASTER booktype0_ where bookty pe0_.NAME like ?; parameters: ; named parameters: {type=%} 357 [main] DEBUG net.sf.ehcache.store.MemoryStore - org.hibernate.cache.Standa rdQueryCacheCache: org.hibernate.cache.StandardQueryCacheMemoryStore miss for sq l: select booktype0_.ID as ID0_, booktype0_.NAME as NAME0_ from BOOK_TYPE_MASTER booktype0_ where booktype0_.NAME like ?; parameters: ; named parameters: {type= %} 357 [main] DEBUG net.sf.ehcache.Cache - org.hibernate.cache.StandardQueryCache cache - Miss ... select booktype0_.ID as ID0_, booktype0_.NAME as NAME0_ from BOOK_TYPE_MASTER booktype0_ where booktype0_.NAME like ? ... 383 [main] DEBUG org.hibernate.cache.ReadOnlyCache - Caching: com.object.cache .BookType#1 385 [main] DEBUG org.hibernate.cache.ReadOnlyCache - Caching: com.object.cache .BookType#2 385 [main] DEBUG org.hibernate.cache.ReadOnlyCache - Caching: com.object.cache .BookType#3 385 [main] DEBUG org.hibernate.cache.StandardQueryCache - caching query result s in region: org.hibernate.cache.StandardQueryCache; timestamp=5678681352962048 Detective Romance Autobigraphy
Our BookType class is using a read only cache (second -level)<class name="BookType" table="BOOK_TYPE_MASTER"> <cache usage="read-only" />
When the query was executed, Hibernate checked if there is any entry in the Query Cache for this particular query. On finding none, it fired the actual query. It also added these records to the second level cache. - When the query was executed a second time:
Query execute once again... 387 [main] DEBUG org.hibernate.cache.StandardQueryCache - checking cached quer y results in region: org.hibernate.cache.StandardQueryCache 387 [main] DEBUG org.hibernate.cache.EhCache - key: sql: select booktype0_.ID as ID0_, booktype0_.NAME as NAME0_ from BOOK_TYPE_MASTER booktype0_ where bookty pe0_.NAME like ?; parameters: ; named parameters: {type=%} 387 [main] DEBUG net.sf.ehcache.store.MemoryStore - org.hibernate.cache.Standa rdQueryCacheCache: org.hibernate.cache.StandardQueryCacheMemoryStore hit for sql : select booktype0_.ID as ID0_, booktype0_.NAME as NAME0_ from BOOK_TYPE_MASTER booktype0_ where booktype0_.NAME like ?; parameters: ; named parameters: {type=% } 387 [main] DEBUG org.hibernate.cache.StandardQueryCache - Checking query space s for up-to-dateness: [BOOK_TYPE_MASTER] 387 [main] DEBUG org.hibernate.cache.EhCache - key: BOOK_TYPE_MASTER 387 [main] DEBUG net.sf.ehcache.store.MemoryStore - org.hibernate.cache.Update TimestampsCacheCache: org.hibernate.cache.UpdateTimestampsCacheMemoryStore miss for BOOK_TYPE_MASTER 387 [main] DEBUG net.sf.ehcache.Cache - org.hibernate.cache.UpdateTimestampsCa che cache - Miss 387 [main] DEBUG org.hibernate.cache.EhCache - Element for BOOK_TYPE_MASTER is null 387 [main] DEBUG org.hibernate.cache.StandardQueryCache - returning cached que ry results 395 [main] DEBUG org.hibernate.cache.EhCache - key: com.object.cache.BookType#1 395 [main] DEBUG net.sf.ehcache.store.MemoryStore - com.object.cache.BookTypeC ache: com.object.cache.BookTypeMemoryStore hit for com.object.cache.BookType#1 395 [main] DEBUG org.hibernate.cache.ReadOnlyCache - Cache hit: com.object.cac he.BookType#1
...
Detective Romance Autobigraphy
As seen here, Hibernate checked the query cache and found entries for the particular query. It also used the UpdateTimeStamps cache to check if there were any updates against the BOOK_TYPE_MASTER table. On finding none it decided to go ahead and used the cached information. Using the entries in the query cache it retrieved the entities from the secondary cache.
<property name="hibernate.cache.use_second_level_cache">false</property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.cache.use_query_cache">true</property>This fragment is from the cfg file where I have disabled second level caching.
On running the same code the logs have changed when executing the cached query a second time:
377 [main] DEBUG org.hibernate.cache.StandardQueryCache - returning cached que ry results 378 [main] DEBUG org.hibernate.SQL - /* load com.object.cache.BookType */ select booktype0_.ID as ID0_0_, booktype0_.NAME as NAME0_0_ from BOOK_TYPE_MASTER booktype0_ where booktype0_.ID=? ... Detective Romance AutobigraphyThe select query was now executed for a particular Id. In fact it was executed three times for each of the records in the result.
What does this mean ?
The Query cache and the secondary level cache are meant to work hand in hand. In the above example the query cache did not cache the entire result set of BOOK_TYPE_MASTER records. Instead it only saved the identifier for each record. From these identifiers it then checks if the record is available - may be in the session cache, then in the second level cache. If not then it executes a select query for the missing record. In effect the absence of a second level cache could result in n additional queries being fired on the database - an "N+1" kind of scenario.
In case of scalar data, the same is directly held in the Query cache. Any entities will be referred by their identifiers only.
Like the explanation
ReplyDeleteGreat blog I enjooyed reading
ReplyDelete