Les Hazlewood

Where Les is More…

Consistent Cache Configuration: Spring, Hibernate, EhCache, Shiro, et. al.

Filed under: Software, Java, General — Les at 9:55 pm on Thursday, December 6, 2007

Have you ever wanted to use caching in your Spring/Hibernate application beyond just supporting Hibernate’s 2nd-level cache?

If you know what a Hibernate 2nd-level cache is, you really know how huge its performance benefits are. Wouldn’t it be useful to utilize caching for other things in your application? Even if you don’t explicitly use a caching API in your app, you still might already be using caching without knowing it - other open source frameworks in addition to Hibernate utilize caching internally for the same performance benefits.

This article explains how to achieve the following:

1) I know I’m going to utilize caching for Hibernate’s 2nd-level cache, but I want to enable that same caching support to other libraries and frameworks that might need it. It would also nice to have if I ever need to use it in my own application code directly.

2) My application is Spring-configured, so I want to configure that caching support in Spring like everything else and then have all the frameworks/libraries, including Hibernate, to use these Spring-configured cache beans.

Unfortunately Hibernate makes this a pain to do.

Hibernate uses something called a CacheProvider to support its 2nd-level cache. A Hibernate SessionFactory uses a single CacheProvider to maintain/manage the individual Cache instances used by the 2nd-level cache.

Now here’s the problem: This CacheProvider is instantiated by Hibernate based on text configuration properties specified to the SessionFactory. When the SessionFactory starts up, it reads these properties, finds the one called hibernate.cache.provider_class which specifies a fully-qualified class name of the CacheProvider implementation, and creates a new instance of that class via reflection. It then uses that instance from then on to manage the Cache instances it needs.

For existing spring configs, here is a simplified example (note the 2nd hibernate property):

<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <!-- hbm.xml files listed... --> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect hibernate.cache.provider_class = net.sf.ehcache.hibernate.EhCacheProvider </value> </property> <!-- Other properties as necessary --> </bean>

Why is this a problem? Because you can’t easily configure your CacheProvider instance yourself and then just tell the SessionFactory to use it. The SessionFactory interface and its default implementation do not have setter methods that would allow you to do so. This means you can’t use Dependency-Injection to set the CacheProvider instance on the Hibernate SessionFactory in Spring or any other DI microcontainer.

This is a problem for me too. The scenario that prompted this article happens to me quite a bit - I use Hibernate plus other libraries that benefit from caching. My own open-source project, Apache Shiro, can use caching to greatly speed up security checks. When I use Hibernate and Shiro in the same application, I want both frameworks to use the same underlying cache infrastructure. And although Shiro supports DI for its cache support, Hibernate does not, which means I have to jump through hoops to get Hibernate to play nice.

Where does Ehcache come in to this? Well, Ehcache is one of the more well-known and stable open-source caching solution for enterprise applications. It is stable and has a long history, it is extremely efficient with goodies like smart multi-threading under the hood, and now with Ehcache 1.3+, supports multicast distributed caching in a cluster - a huge benefit in enterprise apps. Basically, for open-source options, it is one of the best you can get. Cheers to Greg Luck for creating and supporting this great library (Another wonderful Open Source alternative is TerraCotta, take a look at that too).

So, in this particular example, I’m using EhCache but I also want to use it not just for Hibernate, but for Shiro and everything else that could use a Cache. And until now, I’ve been using Ehcache as Hibernate’s cache provider using the aforementioned text property. Hibernate would create an instance of EhCacheProvider and use it internally. Basically this EhCacheProvider implementation is a wrapper around EhCache’s own API of a CacheManager.

The CacheManager is really what I want to configure in Spring. Typically it is good practice and much easier to specify a single CacheManager for an application, which uses a single ehcache.xml file for all configuration. Then you can use this one ehcache.xml file for your Hibernate cache configuration, as well as for any other library that would use EhCache under the hood.

So, since you can’t inject a CacheManager into Hibernate’s SessionFactory, how do you do this in Spring?

Well, Since Hibernate instantiates the CacheProvider itself, and we can’t inject anything, our only option left is via static memory. Blech, I know - I don’t like statics any more than you do, but don’t worry - it is _only_ used as a mechanism to make Hibernate play nice and its never used anywhere else in your config or application code.

*Note that static memory will only allow a single CacheManager to be used across the entire VM, so if you need more than one Hibernate SessionFactory, each with its own separate cache configs, you’ll need to do more trickery (maybe 2 static fields? one per SessionFactory? You’ll know more about your app than I do, so I’ll leave how to do that to you. But you can use this the following technique and expand on it for your environment).

Let’s get to the code, its only one class. You’ll create your own CacheProvider implementation to statically reference a CacheManager instance. The CacheManager instance will be configured in Spring, and we’ll get to that in a bit. Here’s the CacheProvider implementation:

package com.leshazlewood.hibernate; import net.sf.ehcache.CacheManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.cache.Cache; import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheProvider; import org.hibernate.cache.Timestamper; import java.util.Properties; /** * @author Les Hazlewood */ public class ExternalEhCacheProvider implements CacheProvider { protected transient final Log log = LogFactory.getLog(getClass()); private static CacheManager cacheManager = null; /** * This is the method that is called by an external framework (e.g. Spring) to set the * constructed CacheManager for all instances of this class. Therefore, when * Hibernate instantiates this class, the previously statically injected CacheManager * will be used for all hibernate calls to build caches. * @param cacheManager the CacheManager instance to use for a HibernateSession factory using * this class as its cache.provider_class. */ public static void setCacheManager(CacheManager cacheManager) { ExternalEhCacheProvider.cacheManager = cacheManager; } public Cache buildCache(String name, Properties properties) throws CacheException { try { net.sf.ehcache.Ehcache cache = cacheManager.getEhcache(name); if (cache == null) { if ( log.isWarnEnabled() ) { log.warn( "Unable to find EHCache configuration for cache named [" + name + "]. Using defaults."); } cacheManager.addCache( name ); cache = cacheManager.getEhcache(name); if (log.isDebugEnabled()) { log.debug("Started EHCache region '" + name + "'"); } } return new net.sf.ehcache.hibernate.EhCache(cache); } catch (net.sf.ehcache.CacheException e) { throw new CacheException(e); } } public long nextTimestamp() { return Timestamper.next(); } public void start(Properties properties) throws CacheException { //ignored, CacheManager lifecycle handled by the IoC container } public void stop() { //ignored, CacheManager lifecycle handled by the IoC container } public boolean isMinimalPutsEnabledByDefault() { return false; } }

Ok, now that you have this class, what do you do with it? Well the first thing is to change your hibernate.cache.provider_class value to now be com.leshazlewood.hibernate.ExternalEhCacheProvider in your Hibernate SessionFactory bean definition. Then you want to define your CacheManager instance as a bean in your Spring config. Next you want to tell this CacheProvider implementation to use that instance when Hibernate needs to use caching functions.

We do that via a cool little Spring trick - by using the MethodInvokingFactoryBean. Basically this bean will call a static method at application startup with specified method arguments. Its a handy little tool that we can use to statically call the setter method that makes the CacheProvider work:

<bean id="ehcacheCacheManager" class="net.sf.ehcache.CacheManager" destroy-method="shutdown"> <!-- If you want to specify a custom ehcache.xml file (recommended in general but required if defining more than one cacheManager beans), uncomment the following line to point to your configuration. You can use Spring's Resource support to define the value. If you don't uncomment, EhCache's internal defaults will be used. <constructor-arg type="java.net.URL" value="classpath:/ehcache.xml"/> --> </bean> <bean id="cacheProviderCacheManagerInjector" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="com.leshazlewood.hibernate.ExternalEhCacheProvider.setCacheManager"/> <property name="arguments"> <list> <ref bean="ehcacheCacheManager"/> </list> </property> </bean>

So the above spring config says: At application startup, create the CacheManager instance named ‘ehcacheCacheManager’ and then inject it into the ExternalEhCacheProvider via that class’s ’setCacheManager’ static method.

But are we finished?

Well, not quite. We have one piece of cleanup work to do.

The experienced Spring folks will recognize that there can possibly be a race condition with our Hibernate SessionFactory and CacheManager/CacheProvider definitions. Yes, our Spring-configured CacheManager instance will be created and injected into the ExternalEhCacheProvider class correctly, and our Hibernate SessionFactory bean instance will be created correctly. But, what if the Hibernate SessionFactory tries to use a ExternalEhCacheProvider instance before it is ready to be used? (i.e. before the static CacheManager property has been injected?)

To prevent this race condition and guarantee that the ExternalEhCacheProvider’s internal CacheManager instance is set so it is available when the Hibernate SessionFactory needs it, we use the spring bean ‘depends-on’ attribute in our SessionFactory definition:

<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" depends-on="cacheProviderCacheManagerInjector"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <!-- hbm.xml files listed... --> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect hibernate.cache.provider_class = com.leshazlewood.hibernate.ExternalEhCacheProvider </value> </property> <!-- Other properties as necessary --> </bean>

So, although our Hibernate SessionFactory bean doesn’t actually use the CacheManager as a property - otherwise this whole article would be pointless ;) - we force it to wait until the cacheProviderCacheManagerInjector bean has done its work before we allow it to initialize.

We make the ‘depends-on’ attribute point to this injector (MethodInvokingFactoryBean) instance specifically (as opposed to the ehcacheCacheManager bean) because we care that it is not only created, but also injected too.

There. Now we’re done. :)

Now when the Hibernate SessionFactory bean starts up and creates its hibernate.cache.provider_class instance, we can rest assured that it will access our Spring-configured CacheManager.

And, you’ll be able to use that same Spring-configured ‘ehcacheCacheManager’ bean and inject it into Shiro, or any other framework that supports simple Dependency Injection. Because this bean is an application singleton and uses a single config file, now all of your caching config across the entire application is easily manged from the same location - very convenient.

Have fun!

17 Comments »

Comment by Vitaly

March 30, 2008 @ 4:52 pm

Very useful. Thanks. I am working on a project where I need some access to Hibernate’s ehcache. So I used your technique and now I can invalidate required objects in cache and still leave all cache untouched with eternal set to true.

Comment by Les

March 31, 2008 @ 12:41 pm

Hi Vitaly,

Glad to help!

Best of luck,

Les

Comment by Andreas Sahlbach

April 30, 2008 @ 8:15 am

Very useful indeed. I am going to use this to setup our distributed cache for hibernate entities and for our higher level distributed objects. Thanks for your time writing this down!

Comment by kittu

May 16, 2008 @ 8:58 am

Hi Vitaly,

I want to know the details of invalidating the objects.. I am facing problems in reading the cache itself. I can see the values in eclipse debug mode but when I do the get method on the cache it is not returning null value. Please help me out.

Thanks,
Kittu.

Comment by kittu

May 16, 2008 @ 8:59 am

>>it is not returning null value
Please read it as

it is returning null value

Kittu.

Comment by Anonymous Coward

May 21, 2008 @ 5:50 pm

Nice hack, but you don’t have to make the CacheManager static. Use org.springframework.cache.ehcache.EhCacheManagerFactoryBean to set up a cache manager. Inject it to your custom CacheProvider, then inject the cacheProvider to org.springframework.orm.hibernate3.LocalSessionFactoryBean using the cacheProvider properties.

Comment by Vincent

July 4, 2008 @ 5:19 am

I’m using spring and hibernate with ehcache.
The cache is working properly when I look at the hybernate queries log trace. The problem is that I allways get a different instance of the cache object when I’d like to get the same instance.
Is there any way to do it?
Thanks
Vincent

Comment by Vincent

July 4, 2008 @ 5:21 am

spelling mistake: I mean the same instance of the cacheD object.

Comment by Les

July 4, 2008 @ 9:23 am

@Anonymous Coward,

This is true - a Spring-configured Hibernate SessionFactory will now accept an injected CacheProvider - but this functionality was only introduced recently in Spring 2.5.1. So the solution is valid for versions prior to that.

But also, my solution is still a good one if you want to use a consistent caching infrastructure across your application - you wouldn’t want to inject a Hibernate CacheProvider into components that don’t use the Hibernate API.

It is nice to have a wrapper CacheManager that is in your package structure that can be used everywhere - in Hibernate DAOs and other managers/services, without coupling everything to the Hibernate API.

Comment by Les

July 4, 2008 @ 9:28 am

@Vincent

If you’re talking about the data that Hibernate caches in the 2nd-level cache, then no, there is no way to guarantee the same instance every time.

The reason is that Hibernate doesn’t store the objects themselves in the 2nd-level cache - it stores it in what they call a ‘dehydrated’ form - basically an array of data, or more accurately, a tuple.

For example, let’s say you had a User class with two properties, username and password. If you tell Hibernate to cache Users, it doesn’t cache the actually User instance. It creates a tuple (i.e. array of just the properties - username and password). When the User is requested from the 2nd-level cache, Hibernate uses reflection to create a new User instance and then reflectively calls setUsername and then setPassword.

So that’s why you won’t get the same User instance across Sessions. You will get the same instance back in the same Session though, because once it is ‘hydrated’ from the 2nd-level cache, hibernate puts it in the transient (transactionally bound) 1st-level cache and uses it for the remainder of the transaction.

Comment by Xinsheng Chen

February 2, 2009 @ 6:55 pm

Hi, Les,
I have used your code on my project. Thanks!
I would ask a quesion here.
You said, “Since Hibernate instantiates the CacheProvider itself, and we can’t inject anything, our only option left is via static memory.” Is it possible for this static declaration to cause memory leak? I have this concern because in ExternalEhCacheProvider class we have declared:
private static CacheManager cacheManager = null;
which is static; while in
net.sf.ehcache.CacheManager
we have a field:
static java.util.List ALL_CACHE_MANAGERS
The documentation says that “CacheManagers should remove themselves from this list during shut down.” Is this ensured?

Comment by named

May 15, 2009 @ 3:17 am

thank u very much for giving useful information about spring and hibernate cache.
Provide more articles like this.

Comment by named

May 15, 2009 @ 3:27 am

XHTML: You can use these tags:

Comment by Faisal Basra

October 1, 2009 @ 10:38 pm

Thanks very much…. I was trouble around to use caching in spring hibernate based environment.

Good stuff…. 5 stars.

Pingback by EHCache and Spring Integration reference « happypig

October 19, 2009 @ 7:49 pm

[…] Consistent Cache Configuration: Spring, Hibernate, EhCache, JSecurity, et. al. http://www.leshazlewood.com/?p=37 Configuring EhCache with Spring […]

Comment by Chandrasekhar Reddy

October 29, 2009 @ 6:04 am

Thank a lot very much for giving such a useful information about EHCache integration with Spring and Hibernate.

Comment by cometta

December 2, 2009 @ 10:08 pm

i getting error ExternalEhCacheProvider.cacheManager is null?

my hibernatesessionfactory configuration is like yours

com.package.ExternalEhCacheProvider

cacheManger is not injected into ExternalEhCacheProvider? what i did wrong?

Comment by cometta

February 3, 2010 @ 4:40 am

why not use org.hibernate.cache.EhCacheProvider
directly ?

Comment by Les

February 3, 2010 @ 9:17 am

@Cometta

Did you read the article? I explain exactly why it is not possible to do this.

RSS feed for comments on this post. TrackBack URI

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Powered by WP Hashcash