Tag Archives: java

ehcache and hibernate: cache of collections

Last post I wrote about activating the hibernate second-level cache. Now, let’s see how to activate the second-level cache for collections.

In this example I’ll use a Customer with Orders. Please, take a look in the representation of the relationship between them, below:

Implementing the classes with Java will look like this:

package com.rodrigow;
class Order {
    int number;
}
package com.rodrigow;
import java.util.Set;
class Customer {
    String name;
    Set<Order> orders;
}

Now, the hibernate mapping:

<hibernate-mapping>
  <class name="com.rodrigow.Order" table="ORDER">
    <cache usage="read-only"/>
    <id name="number" type="int" />
  </class>
</hibernate-mapping>
<hibernate-mapping>
  <class name="com.rodrigow.Customer" table="CUSTOMER">
    <cache usage="read-only"/>
    <id name="name" type="java.lang.String" />
    <set name="orders" lazy="false">
      <cache usage="read-only" />
      <key column="number" />
      <one-to-many class="com.rodrigow.Order" />
    </set>
  </class>
</hibernate-mapping>

There is a configuration, in bold, that tells hibernate to look in the cache for objects of this type.

EHCache configuration (ehcache.xml) will became something like below:

<ehcache>
  <cache
    name="com.rodrigow.Customer"
    maxElementsInMemory="200"
    eternal="true"
    overflowToDisk="true"
  />
  <cache
    name="com.rodrigow.Customer.orders"
    maxElementsInMemory="200"
    eternal="true"
    overflowToDisk="true"
  />
</ehcache>

In bold we tell how it will find the objects. In order to find the client’s orders, it is needed to add an entry with the same name we gave in the class Client, i.e. orders.

Now everything is ready to go. No hits in the database!!! But wait… looking the hibernate logs I’m finding hits on the database for every single Order from a Client. But, should it be in the cache? What? Cache not found for the Order? AHHH!?!?!, Well, the documentation from EHCache it’s not that good, so, let’s dive into source code.

EHCache holds a cache version of the collection but it isn’t enough for hibernate to hydrate the objects. By the time it try to materialize the objects, hibernate look for each cache version of each Order and it does not find it, therefore hibernate ends up querying database for each Order from a Client.

Thus, we’ll need to add one more entry on ehcache.xml:

<ehcache>
  ...
  <cache
    name="com.rodrigow.Order"
    maxElementsInMemory="200"
    eternal="true"
    overflowToDisk="true"
  />
</ehcache>

Now you can check hibernate logs and actually see the cache hits for each order on the customer!

hibernate: activating the second-level cache

Hibernate has two different cache levels: the first-level and the second-level cache. The first one is always activated. The second-level cache is up to you.

So, first-level cache, it’s related to session objects, i.e, objects on the managed state. These objects are results from a query, an object after inserted or updated. Again, first-level is activated by default.

In the other hand, second-level cache, when activated, it’s a in memory copy from the database. Hibernate still using the first-level cache, but, instead of querying directly the database it uses this second-level cache. Faster and do not overload the database.

The second-level it’s just a interface on Hibernate. You’ll need to use an implementation of second-level in order to get it working. In java, the most commonly used is the EHCache.

Technical speaking, to activated the EHCache you’ll need to add two properties in the Hibernate configuration:

<property name="hibernate.cache.use_second_level_cache">
   true
</property>
<property name="hibernate.cache.region.factory_class">
   net.sf.ehcache.hibernate.EhCacheRegionFactory
</property>

Note: Depending on the Hibernate version, you’ll need different configuration. Please, checkout the EHCache help page.

Next step will configure Hibernate to make the cache of an object. Directly in the Hibernate configuration, add the cache tag or use the annotation:

<cache usage="read-write|nonstrict-read-write|read-only" />
--
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

The last step is the EHCache configuration: ehcache.xml. We need to create and define an region for our cache. The default would be the class signature:

<ehcache>
  <cache
    name="com.rodrigow.Blog"
    maxElementsInMemory="200"
    eternal="true"
    overflowToDisk="true"
  />
</ehcache

Now is just a matter to turn on the Hibernate and EHCache logs and make sure you see some cache hit. There won’t have any SQL on database.