Amongst the performance related tasks I have been through, this has been one of them. The concern is that if the same query is invoked every time for a particular entity and the table data is not liable to change for a particular time slot, we can possibly cache the query results with Hibernate. This means that, if we want the details of a Student with id 1234, the query executes and hits the database only for the first request. The subsequent requests are served with the results from the query cache. This brings a high impact in the response time which we could notice. When we do this, we are also concerned about when the cache refreshes itself. We can do that easily with a simple configuration, which we will be exploring.
If a query cache is applied, then no subsequent SQL statement is sent to the database. The query results are retrieved from the query cache, and then the cached entity identifiers are used to access the second level cache.
To enable query cache, below are the set of steps that needs to be followed –
- Set the hibernate.cache.use_query_cache property to true and make sure the second level cache is enabled. Follow this link to understand the second level cache.
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <!-- Enable second level cache --> <prop key="hibernate.cache.use_second_level_cache">true</prop> <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop> <prop key="net.sf.ehcache.configurationResourceName">/ehCache.xml</prop> </props> </property> <property name="mappingResources"> <list> <value>User.hbm.xml</value> </list> </property> </bean>
- With that done, org.hibernate.cache.internal.StandardQueryCache holds the cached query results.
- In the ehCache configuration file, add the below snippet –
<cache name="org.hibernate.cache.StandardQueryCache" maxElementsInMemory="10000" eternal="false" timeToLiveSeconds="86400" overflowToDisk="false" memoryStoreEvictionPolicy="LRU" />
- The query cache does not cache the state of the actual entities in the cache. It caches identifier values and results of value type. Therefore, always use the query cache in conjunction with the second-level cache for those entities which should be cached as part of a query result cache – https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch06.html
- To cache the concerned entity as specified in point 4, we need to add the following snippet into the XML entity mapping file as –
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <cache usage="transactional" include="non-lazy" /> <id name="id" type="int" column="ID"> <generator class="native"/> </id> <property name="firstName" column="FNAME" type="string"/> <property name="lastName" column="LNAME" type="string"/> </class> </hibernate-mapping>
The above makes sure that the non-lazy components of the entity are cached as part of a query result cache.
- With all the above points covered, the final point is to explicitly enable the query caching to the individual queries as –
Query query = session.createQuery("FROM EMPLOYEE"); query.setCacheable(true); List users = query.list();
With all this done, maybe you can start your server in debug mode and see the magic happen!