Hibernate Cache

Posted by Anandhan Subbiah on Apr 2, 2008 in Java, Technical Articles12 comments

Database operations are typically very expensive. A typical query would have to be first transmitted over the network. The database has to create a query plan for it and then execute it. The response then has to be transmitted over the network again before the client can process the results. Most databases will cache the results and some will cache queries thereby preventing the execution of the queries and sometimes eliminate the need for the query plan. But these are largely dependent on the number of unique queries and the network overhead is still there.

The natural and obvious answer is to have a cache at the client end of the database connection. Hibernateprovides one cache (the first-level cache) through which all requests must pass. A second-level cache is optional and configurable.

The L1 cache ensures that within a session the request for a object from the database always returns the same object instance hence preventing the object from being reloaded. You can always use object.evict() to discard the object and force a reload.To discard all objects in a session you can issue session.clear().This can also be called called a Java Virtual Machine (JVM) or SessionFactory-level class. . If you have a second-level cache at the SessionFactory level, the application objects are cached in a manner where they are available across sessions.

The L2 cache also called as query cache is external to hibernate . Hibernate provides a pluggable interface for the L2 cache. This allows a cache to be shared between applications on the same machine or even between multiple applications on multiple machines. The L2 cache will not be able to react to changes made by an external application so you have to be careful not to lose the state of the data. Also a distributed cache creates lot of network traffic and takes up memory as well.

Java threads do not timeout when there is a deadlock so don’t share sessions between different threads. If you absolutely must maintain an instance for a longer duration, maintain the instance within a ThreadLocal object. For most purposes, however, the lightweight nature of the Session object makes it practical to construct, use, and destroy an instance, rather than to store a session.

Some of the widely used L2 cache implementations are

EHCache
An in-process cache which is not cluster safe but supports Query Cache. It can use memory as well as disk.

OSCache
An in-process cache which is not cluster safe but supports Query Cache. It can use memory as well as disk.

SwarmCache
A multicast distributed cache which is cluster safe(clustered invalidation) but does not support Query Cache.

TreeCache
A multicast distributed transactional cache which is cluster safe but does not support Query Cache.

Configuration of EH Cache

When the objects being cached need to be updated, the read-write usage mechanism is an appropriate option to choose. If you use database imports or have an alternate way to update the database this option will not be suitable. It becomes even more difficult if the cache is clustered. The database has to be locked for the cache and the database to be in sync.

The nonstrict read-write cache is the same as read write except that it writes to the database occasionally.

The following properties are available in the Hibernate Configuration files to handle cache setup:

hibernate.cache.use_minimal_puts: Optimizes second-level cache operation to minimize writes, at the cost of more frequent reads (useful for clustered caches). Possible values are true and false.

hibernate.cache.use_query_cache: Enables the query cache. The query cache is disabled by default. Possible values are true and false.

Example
Session session = SessionFactory.openSession();
Query query = session.createQuery("from Users");
query.setCacheable(true);
List users = query.list();
SessionFactory.closeSession();

To explictly use EHCache :
Add this property to the hibernate .cfg.xml

<property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate
.Provider</property>

Create a file ehcache.xml and make sure it is available in the classpath

<ehcache>

    <diskStore path="java.io.tmpdir"/>

    <defaultCache
        maxElementsInMemory="1000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

    <cache name="example.survey.Survey"
        maxElementsInMemory="1000"
        eternal="false"
        timeToIdleSeconds="3600"
        overflowToDisk="true"
        />

    <cache name="example.survey"
        maxElementsInMemory="1000"
        eternal="true"
        overflowToDisk="true"
        />
</ehcache>

timeToIdleSeconds specifies how long an object can be idle before it expires. The timeToLiveSecondsvalue specifies the overall length of time that an object can be cached before being expired.

When not to use Cache
1) When the database can be modified by an external source. You may be able to deal with this issue by specifying a version or timestamp property for your object and using the Session.lock() method to verify that the object hasn’t been changed.
2)Caching only helps when the same data is used multiple times within the expiry time period. There is no point in caching data which expires before it can be used.
3)In financial applications where auditing is very important.
4)When the dataset is too large
5) when you don’t know what the hell you are doing :)

Tags:

12 comments

» Comments RSS Feed
  1. “1) When the database can be modified by an external source. You may be able to deal with this issue by specifying a version or timestamp property for your object and using the Session.lock() method to verify that the object hasn’t been changed.”

    Anandhan, would you please elaborate on this with an example? I see that many Hibernate users have commented on this issue, and calling session.flush(), session.clear(), setCacheable(false) all still return what’s in the L1 cache. Would you need to update the version/timestamp on the object every time it is updated? If the object has been changed, how would you force Hibernate to check the DB instead of the L1 cache?

  2. Each interaction with the persistent store occurs in a new Session. However, the same persistent instances are reused for each interaction with the database. The application manipulates the state of detached instances originally loaded in another Session and then reattaches them using Session.update(), Session.saveOrUpdate(), or Session.merge().

    To force a version check without a version or timestamp property mapping, with a comparison of the state of all fields in a row, turn on optimistic-lock=”all” in the mapping. Note that this conceptually only works if Hibernate can compare the old and new state, i.e. if you use a single long Session and not session-per-request-with-detached-objects.

    Sometimes concurrent modification can be permitted as long as the changes that have been made don’t overlap. If you set optimistic-lock=”dirty” when mapping the , Hibernate will only compare dirty fields during flush.

    In both cases, with dedicated version/timestamp columns or with full/dirty field comparison, Hibernate uses a single UPDATE statement (with an appropriate WHERE clause) per entity to execute the version check and update the information. If you use transitive persistence to cascade reattachment to associated entities, Hibernate might execute unnecessary updates. This is usually not a problem, but on update triggers in the database might be executed even when no changes have been made to detached instances. You can customize this behavior by setting select-before-update=”true” in the mapping, forcing Hibernate to SELECT the instance to ensure that changes did actually occur, before updating the row.

  3. Why did you write that OSCache is “not cluster safe”?
    I use it in my cluster!

    Maybe i do something wrong… :o /

    Regards

  4. Great info. Is it possible not to use query.setCacheable(true); and still achieve the same? My specifications are a bit different and I am currently managing the session using HibernateTemplate provided by the Spring Framework and I am not managing the session explicitly.

  5. OScache is not transactional which makes it non-cluster friendly

  6. Nice article… clear explanation… Thanks Anandhan… :)

  7. Biju Philip had asked you questions which are not actually answered.

    How does a Session can influence L2 Cache ? Session.lock, Session.flush, and all other methods are related to L1 only NOT L2.

    Since L1 cant be disabled at all you should not mention L1 operations of Session when you mention ‘When not to use Cache’

  8. Apparently some versions of hibernate have a nasty issue with the L2 Cache..

    http://opensource.atlassian.com/projects/hibernate/browse/HHH-3383

  9. This blog describes the issue in detail:

    http://darren.oldag.net/2008/11/hibernate-query-cache-dirty-little_04.html

  10. “The L2 cache also called as query cache is external to hibernate . Hibernate provides a pluggable interface for the L2 cache. This allows a cache to be shared between applications on the same machine or even between multiple applications on multiple machines. The L2 cache will not be able to react to changes made by an external application so you have to be careful not to lose the state of the data. Also a distributed cache creates lot of network traffic and takes up memory as well.”

    I don’t think level 2 cache is the query cache. Hibernate provides query cache separately from level 2 cache.

  11. nice article !!! i am using NCache which provides plug in for NHibernate as L2 cache and so far it is doing a wonderful job.the best thing about NCache is its replicate data over multiple severs so there is no issue of data lose even your cache severs goes down. here is some more details,

    http://www.alachisoft.com/ncache/nhibernate_l2cache_index.html

  12. Well explained. You mentioned here some L2 caches so I’d also like to add another L2 cache in the list thats NCache (in-proc L2 cache). NCache provides Hibernate integration for Second Level Cache without any code change.

Leave a comment