Caches

General

The purpose of a cache is to reduce access to the database.

There are two kinds of caches in a Hibernate application. The Hibernate session is the first one, the second one can be used optionally.

The Hibernate session is a transactional cache, i.e. it shows the changes a user has already flushed within an open transaction. As a session belongs to a unique user, theses changes are not propagated to other sessions. I remind you that a session is not thread-safe, do not share it across threads!

The second level cache is shared across a Java virtual machine instance or, if you use clustering, it can even be shared across multiple servers across a network. All your sessions share this cache. The second level cache normally contains only committed data. (except for transaction caches, where you can configure the isolation level)

A cache does not reference your objects, so changes to the objects will not destroy the cache. A cache resembles more a Map having a combination of id and class as key and an array as value, where the array holds all the field values.

images/c_cache_session.jpg
Key Field 1 Field 2 Field 3

de.laliluna.Developer#680

Peter

123

12.22

de.laliluna.Developer#674

Karl

1234

77.55

This is of course a simplification. A cache does also handle information about the creation time, last access time, change time and some very effective search algorithms.

Aspects to consider

A cache holds objects to prevent database queries. A cache is not aware of changes which bypass Hibernate for example queries issued by JDBC or from other non Java applications.

Situations where a cache is useful
  • The same data is queried repeatedly
  • Amount of data is relative small as compared to the available memory
  • Queries are slow and the database is on the network
  • All applications use Hibernate and the same cache

Choosing the cache mode

There are four cache modes available.

  • read-write
  • read-only
  • nonstrict-read-write
  • transactional

Readonly
By far the fasted cache is read-only but if you specify this in a class you cannot save or update any entries. You may consider using special mappings only to display data in combination with a readonly cache. This would push the performance to the limit.
Read-write
This is probably the most commonly used setting. As indicated by the Hibernate reference this cache cannot be used if you use the transaction isolation level serializable, which is probably used only in very rare cases.
Nonstrict-read-write
This cache mode does not guaranty strict transaction isolation. An update to the same dataset happens rarely by two concurrent threads at the same moment. This is true when your application uses optimistic locking with short transactions and is not heavily stressed by simultaneous transactions to the same data. When a transaction takes 50 milli-seconds you must have two threads accessing the same data at the same moment.
Transactional
Transactional cache modes are fully supported by the cache and you can configure the isolation level of the cache. Most caches do not support this behaviour.

Standard cache

The standard cache is filled when you load any data with methods like

  • session.load
  • session.get
  • session.createQuery(…).list()
  • …..

The cache is used when Hibernate has to create an instance from a given id. This is done when you use for example

  • session.load
  • session.get
  • traversing a relation which is not yet initialised

You can configure the size of the cache and when items should expire. Expiring is useful when you update data with other applications from time to time but you can accept a delay during which the user sees stale data.

You do not have to set a timeout when your application is the only one accessing the database.

You can evict objects from the cache using the session factory (not the session itself). It provides methods like evict and others to deal with data in the cache.

Using the cache

Configure the cache in your hibernate.cfg.xml or in the hibernate.properties.

<property name="cache.provider_class">
   org.hibernate.cache.EhCacheProvider
</property>

Add a configuration file for your selected cache. You can find more detailed information about each cache in the following chapters. Here is a short example.

<cache name="myCache"
    maxElementsInMemory="6000"
    eternal="true"
    timeToIdleSeconds="0"
    timeToLiveSeconds="0"
    overflowToDisk="true"
/>

Add the cache tag to your class and to all relations you want to cache. You can specify a cache region to use a special configuration. For example I used the myCache configuration but you can also leave out the region definition in order to use the default configuration.

<class name="Developer" table="tdeveloper">
 <cache usage="read-only"  region="myCache" />
 <list name="computers" cascade="all">
   <cache usage="read-only"  region="myCache" />
   <key column="developer_id" not-null="true"></key>
   <list-index column="listindex"></list-index>
   <one-to-many class="Computer" />
 </list>

That’s it.

Query cache

A query cache contains the object ids found by a query. The query and the parameters are used as key. As a consequence a query cache is only useful when you have queries with the same parameters issued regularly. When a table is updated, all cached queries touching the table are evicted from the cache as they are considered to be outdated.

To sum up, a query cache is useful when you have a lot of identical selects compared to the number of updates. Typical applications are content management systems, Internet forums with a lot of read access etc.

To use the query cache you must do at least the following:

In the hibernate.properties or hibernate.cfg.xml add the property

  <property name="hibernate.cache.use_query_cache">true</property>

In your query set CacheAble to true

List result = session.createQuery(
   "from Computer c where c.name like ? order by c.name desc, c.id asc")
   .setString(0,param)
   .setCacheable(true).list();

You can also specify a special cache region used for your query using setCacheRegion

List result = session.createQuery(
   "from Computer c where c.name like ? order by c.name desc, c.id asc")
   .setString(0,param)
   .setCacheable(true).setCacheRegion(“myQueryCache”).list();

That’s it.

As opposed to the table in the Hibernate reference all of the following cache providers support query caching.

Test all caches

We provided an example application named TestCache. It will output access times to the data.

In order to use the test, generate test data first. Adapt the hibernate.cfg.xml to suit your database and uncomment the setupTest method in the class TestMain to generate random data entries.

Have a look at the class TestMain to comment or uncomment the tests you want to run and to specify which mapping to load. We have mappings for all supported cache modes. A cache provider does not support every mode.

You can configure the number of threads for running two kinds of tests. The first uses session.get to randomly select entries. The other test queries data with random parameters. Both tests log how the access times are changing by the time when your cache is filled. Run a no cache test to compare the results.

In the hibernate.cfg.xml you can configure the cache provider. Configuration files for any of the following caches can be found in the project as well. Read in the following descriptions to learn more about needed libraries.

EH Cache

EH cache is the standard cache for Hibernate. It can be used as a distributed cache or as cache for JSP content. Detailed information can be found on the EH Cache website at

http://ehcache.sourceforge.net/documentation/

The configuration is done in the ehcache.xml file:

<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <diskStore path="java.io.tmpdir"/>

    <cache name="myCache"
        maxElementsInMemory="6000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        />

    <cache name="net.sf.hibernate.cache.StandardQueryCache"
        maxElementsInMemory="20"
        eternal="false"
        timeToLiveSeconds="240"
        overflowToDisk="false"/>

  <cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
        maxElementsInMemory="5000"
        eternal="true"
        overflowToDisk="false"/>

    <defaultCache
        maxElementsInMemory="7000"
        eternal="false"
        timeToIdleSeconds="180"
        timeToLiveSeconds="180"
        overflowToDisk="false"
        />
</ehcache>

Cache provider in the hibernate.cfg.xml:

<property name="cache.provider_class">
   org.hibernate.cache.EhCacheProvider
</property>

Needed libraries:

  • eh-cache.jar

Supported cache modes:

  • read-write
  • read-only
  • nonstrict-read-write

OS Cache

OS Cache can also be used as distributed cache or as cache for JSP content. Clustering using JMS and jgroups is supported. Detailed information can be found at http://www.opensymphony.com/oscache/.

Cache provider:

<property name="cache.provider_class">
   org.hibernate.cache.OSCacheProvider
</property>

Needed libraries:

  • oscache.jar

Supported cache modes:

  • read-write
  • read-only
  • nonstrict-read-write

Swarmcache

The swarm cache can be used as distributed cash as well. It uses jgroups for the clustering. Further information can be found at http://swarmcache.sourceforge.net/.

Cache provider:

<property name="cache.provider_class">
   org.hibernate.cache.SwarmCacheProvider
</property>

Needed libraries:

  • swarmcache.jar
  • jgroups-all library which you must download from the swarmcache website.

Supported cache modes:

  • nonstrict-read-write

JBoss Treecache

JBoss is the only transactional cache in our list. It supports clustering as well. Further information can be found at http://www.jboss.org/products/jbosscache

You may consider using the treecache if you use Hibernate on JBoss.

Cache provider:

<property name="cache.provider_class">
   org.hibernate.cache.TreeCacheProvider
</property>

Needed libraries to run in a java application:

  • jboss-cache.jar
  • jboss-system.jar
  • jboss-jmx.jar
  • concurrent.jar
  • jboss-common.jar
  • jgroup library deliverd by Hibernate

For a web application deployed on a JBoss application server you will probably not need any of these libraries as they are already contained in a typical JBoss installation.

Supported cache modes:

  • transactional

Bypass a cache

When you use LockMode.Read the cache is bypassed and the values are read directly from the database. You can specify the LockMode when you use session.lock or session.load.