How to use Ehcache as Hibernate’s 2nd Level Cache
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
Using JPA’s and Hibernate’s 2nd level cache can improve the performance of your persistence layer. It acts as a session-independent entity store. Hibernate uses it whenever you call the find method on your EntityManager or traverse an association to fetch a cached entity. Instead of executing a database query, Hibernate gets the entity object from the cache. That’s a lot faster than executing a query.
If you provide a query for reading data from your database, for example, a JPQL query, your 2nd level cache will not be used. But you can use Hibernate’s proprietary query cache to cache query results.
Activating the 2nd Level Cache
As I explained in great detail in my Hibernate Performance Tuning Online Training, you activate the 2nd level cache by configuring the shared-cache-mode parameter in your persistence.xml.
<persistence> <persistence-unit name="my-persistence-unit"> <!-- enable selective 2nd level cache --> <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> ... </persistence-unit> </persistence>
I recommend setting it to ENABLE_SELECTIVE and annotating each entity class you want to cache with @Cacheable.
@Entity @Cacheable public class Author { ... }
After you’ve done that, you need to provide a cache implementation. The cache itself is independent of your persistence provider. JPA and Hibernate only define an interface to the 2nd level cache. Because of that, there are various cache implementations available. Ehcache is one of the most popular ones.
A Quick Introduction to Ehcache
Ehcache is a very popular open-source project available under the Apache 2.0 license. It’s a multi-purpose, highly scalable cache implementation for Java applications. Typical use cases are in-process, application-level caches in single-instance or clustered deployments, out-of-process caches, and Hibernate’s 2nd level cache. In this article, I will focus on Ehcache as Hibernate’s 2nd level cache.
Configuring Ehcache as Hibernate’s 2nd Level Cache
The required configuration and dependencies to integrate Ehcache with Hibernate depend on the Ehcache version you want to use in your project. Hibernate provides a proprietary integration for the older Ehcache 2.x releases. The newer Ehcache 3.x releases implement the JCache specification. Hibernate provides a generic integration for all caches that implement that specification.
Let’s take a look at both of them.
Using Ehcache 2.x
Hibernate’s hibernate-ehcache module integrates Ehcache 2.x as the 2nd level cache.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>5.4.15.Final</version> </dependency>
Configuring a Region Factory
After you’ve added the dependency, you need to configure a region factory. You can choose between EhCacheRegionFactory and SingletonEhCacheRegionFactory.
The first one configures a CacheManager for each of Hibernate’s SessionFactory instances. This is recommended when you use multiple Hibernate SessionFactory instances in the same JVM. You can activate it by setting the hibernate.cache.region.factory_class to ehcache.
<property name="hibernate.cache.region.factory_class" value="ehcache"/>
If you want to share the same CacheManager instance among multiple SessionFactory instances, you need to set that parameter to ehcache-singleton. This will activate the SingletonEhCacheRegionFactory.
<property name="hibernate.cache.region.factory_class" value="ehcache-singleton"/>
After you’ve done that, you should also configure your cache. I explain the most important configuration parameters at the end of this article.
Using Ehcache 3.x
Since version 3, Ehcache has been implementing the JCache specification. You can integrate it using Hibernate’s hibernate-jcache module. It provides a generic integration for all JCache compatible cache implementations.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jcache</artifactId> <version>5.4.15.Final</version> </dependency>
In addition to the dependency, you also need to add the hibernate.cache.region.factory_class property to your persistence.xml configuration and set it to jcache.
<property name="hibernate.cache.region.factory_class" value="jcache"/>
Hibernate then uses the default JCache provider to create the default CacheManager. It also uses a default configuration to create the caches. If you want to provide a configuration, which you should, you need to specify the CacheManager and a path to the cache configuration in your persistence.xml file.
<property name="hibernate.javax.cache.provider" value="org.ehcache.jsr107.EhcacheCachingProvider"/> <property name="hibernate.javax.cache.uri" value="file:/META-INF/ehcache.xml"/>
Configuring Your Caches
By default, Hibernate stores each entity class an individual region of the 2nd level cache. It uses the fully qualified class name as the region name. Ehcache maps each region to a separate cache. You can configure each one of them in the ehcache.xml file.
<ehcache> <cache name="com.thorben.janssen.Author" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600"></cache> </ehcache>
When you use Ehcache, you need to configure the maximum size of your cache. You can do that by setting the maxEntriesLocalHeap or the maxBytesLocalHeap configuration attribute. In the previous example, maxEntriesLocalHeap is set at 1000. So, the cache segment com.thorben.janssen.Author will accept up to 1000 Author entity objects.
Depending on your configuration, cache elements can expire. The timeToLiveSeconds attribute defines the maximum time an object stays in the cache. The timetoIdelSeconds configuration attribute specifies the time after which an unused object gets removed. Based on the cache configuration in the previous example, an Author entity expires if it hasn’t been accessed for 300 seconds or after it stays in the cache for 600 seconds.
When the cache exceeds its configured size, Ehcache removes expired elements. If that doesn’t clear up enough space, it evicts objects from the cache. By default, it removes the least recently used elements. You can change that policy using the memoryStoreEvictionPolicy configuration attribute.
Providing a Default Configuration
If you don’t provide a configuration for each region, the region factory will log a warning and create the region itself. You can prevent the warning message by setting the hibernate.cache.ehcache.missing_cache_strategy configuration property for Ehcache 2.x or hibernate.javax.cache.missing_cache_strategy for Ehcache 3.x. You can choose one of the following:
- fail, to throw an Exception if a cache isn’t configured,
- create-warn (default), to log a warning and to create a new cache, or
- create, to create a new cache without logging a warning.
If you decide to not configure your caches explicitly, you should provide a defaultCache configuration. It will be used for each of the dynamically created caches.
<ehcache> <defaultCache maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600"> </defaultCache> <cache name="com.thorben.janssen.Author" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600"></cache> </ehcache>
Conclusion
Ehcache is a popular and highly scalable cache. Hibernate provides two integrations to support Ehcache as its 2nd level cache implementation.
These integrations require almost no configuration. You only need to add the dependency and configure the region factory class to start using Ehcache based on a default configuration. But it’s best to provide your configuration and specify the size of your cache and if elements can expire. If you adjust these configuration parameters to your application, Ehcache will act as a very efficient 2nd level cache for Hibernate.
Hi Francis,
please check if there is a file called ehcache.xml in the \META-INF folder in your classpath.
Regards,
Thorben
Hi,
in my persistence.xml i set:
<property name="hibernate.javax.cache.uri" value="file:undefinedMETA-INFundefinedehcache.xml"undefined>
but I get a stacktrace:
…..
Caused by: java.io.FileNotFoundException: \META-INF\ehcache.xml
….
What's the problem here?