How do you configure Ehcache as a second-level cache provider in Hibernate or JPA?
Ehcache can be seamlessly integrated with Hibernate or JPA as a second-level cache provider to significantly improve application performance by reducing the number of database calls. The second-level cache stores entity data after it has been loaded from the database, making it available for subsequent queries without hitting the database again. This guide outlines the steps to configure Ehcache.
1. Add Dependencies
First, you need to add the necessary dependencies to your project's build file. This typically includes the Hibernate core, the Hibernate-Ehcache integration library, and potentially a JAXB API dependency for JDK 9+ environments with JPA.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.x.x.Final</version> <!-- Use your Hibernate version -->
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>6.x.x.Final</version> <!-- Must match hibernate-core version -->
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version> <!-- Required for JPA/Hibernate with JDK 9+ -->
</dependency>
2. Configure Persistence Unit (persistence.xml)
In your persistence.xml file, located in the META-INF directory, you need to specify Ehcache as the second-level cache provider and enable caching. You'll also define the caching region factory and, optionally, the default cache concurrency strategy.
<persistence-unit name="myPU">
<properties>
<!-- Enable second-level cache -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<!-- Specify Ehcache as the cache provider (for Hibernate 6+) -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jcache.JCacheRegionFactory"/>
<!-- For older Hibernate versions (e.g., Hibernate 5.x) use: -->
<!-- <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/> -->
<!-- Optional: set default cache mode (e.g., READ_WRITE, NONSTRICT_READ_WRITE, READ_ONLY) -->
<property name="hibernate.cache.default_cache_concurrency_strategy" value="READ_WRITE"/>
<!-- Optional: Enable query cache (stores results of queries, not entities) -->
<!-- <property name="hibernate.cache.use_query_cache" value="true"/> -->
<!-- Optional: Specify ehcache.xml location if not in classpath root -->
<!-- <property name="hibernate.cache.provider_configuration_file_resource_path" value="/config/my-custom-ehcache.xml"/> -->
</properties>
</persistence-unit>
3. Configure Ehcache (ehcache.xml)
Ehcache requires its own configuration file, typically named ehcache.xml, placed in your classpath (e.g., src/main/resources). This file defines cache managers, default cache settings, and specific cache regions for entities or collections. For JCache (JSR-107) compliance, you might use ehcache.xml or ehcache-jcache.xml depending on your version and setup.
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<!-- Default cache settings -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
overflowToDisk="false"
diskPersistent="false"
statistics="true" />
<!-- Define a specific cache for an entity -->
<cache name="com.example.entity.MyEntity"
maxEntriesLocalHeap="5000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="3600"
overflowToDisk="false"
diskPersistent="false"
statistics="true" />
<!-- Define a specific cache for a collection (entity.collectionPropertyName) -->
<cache name="com.example.entity.MyEntity.relatedEntities"
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="900"
timeToLiveSeconds="1800"
overflowToDisk="false"
diskPersistent="false"
statistics="true" />
</ehcache>
4. Enable Caching on Entities
Finally, you need to annotate the entities and their collections that you wish to cache. JPA's @Cacheable annotation marks an entity for caching, and Hibernate's @Cache provides more fine-grained control over cache region and concurrency strategy, allowing you to override the default settings.
import jakarta.persistence.Cacheable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Column;
import jakarta.persistence.OneToMany;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import java.io.Serializable;
import java.util.List;
@Entity
@Cacheable // JPA annotation to enable caching
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "com.example.entity.MyEntity") // Hibernate specific
public class MyEntity implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
@OneToMany(mappedBy = "myEntity")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // Cache collections
private List<RelatedEntity> relatedEntities;
// Getters, Setters, Constructors...
}
5. Considerations and Best Practices
- Cache Concurrency Strategy: Choose an appropriate strategy (
READ_ONLY,NONSTRICT_READ_WRITE,READ_WRITE,TRANSACTIONAL) based on your data's mutability and consistency requirements.READ_ONLYis fastest but for immutable data,READ_WRITEoffers strong consistency for mutable data. - Cache Regions: Define explicit cache regions in
ehcache.xmlfor better organization and fine-tuning of eviction policies and TTL for different entities or collections. This allows more granular control than relying solely on the default cache. - Eviction Policies: Configure
maxEntriesLocalHeap,timeToIdleSeconds, andtimeToLiveSecondscarefully to manage memory usage and data freshness. In a distributed setup, consideroverflowToDiskor a Terracotta server. - Query Cache: The second-level cache caches entities, while the query cache caches the results of queries. If you frequently run the same queries, enable the query cache (
hibernate.cache.use_query_cache=true) and usequery.setCacheable(true)on your JPA/Hibernate queries. - Serialization: Ensure all cached entities are
Serializableif you are using a distributed cache, or if Ehcache is configured to store objects to disk, as Ehcache might need to serialize them. - Cache Invalidation: Understand how cache invalidation works with your chosen concurrency strategy to prevent stale data issues, especially in clustered environments where multiple application instances might be modifying the database.
- Monitoring: Utilize Ehcache's built-in statistics (enable
statistics="true"inehcache.xml) and JMX integration to monitor cache performance and identify potential bottlenecks or misconfigurations.