EhCache: Embedded Caching Solution
Caching is one of the most effective ways to improve application performance. By keeping frequently accessed data close to your application logic, you avoid expensive round-trips to databases or remote services. EhCache is a mature, widely-used, open-source caching library for Java that makes embedded caching straightforward.
In this post, we'll explore what EhCache offers, how to configure it, and best practices for using it in production.
What Is EhCache?
EhCache is an in-process (embedded) caching library that runs within the same JVM as your application. Unlike distributed caches such as Redis or Memcached, EhCache stores data locally, which means near-instant access times without network overhead.
Key characteristics:
- Embedded and lightweight — no separate server process required.
- Tiered storage — supports heap, off-heap, and disk-based storage.
- JSR-107 (JCache) compliant — integrates with the standard Java caching API.
- Spring integration — works seamlessly with Spring's caching abstraction.
Adding EhCache to Your Project
For EhCache 3.x with Maven, add the following dependency:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>
If you want JCache (JSR-107) support:
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.1</version>
</dependency>
Programmatic Configuration
EhCache 3 offers a clean, type-safe builder API for configuring caches in code:
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.*;
import org.ehcache.config.units.*;
public class CacheExample {
public static void main(String[] args) {
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("productCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1000, EntryUnit.ENTRIES)
.offheap(10, MemoryUnit.MB)))
.build(true);
Cache<Long, String> productCache =
cacheManager.getCache("productCache", Long.class, String.class);
productCache.put(1L, "Wireless Mouse");
String value = productCache.get(1L);
System.out.println("Cached value: " + value);
cacheManager.close();
}
}
Understanding Tiered Storage
One of EhCache's strengths is its tiered storage model. You can combine multiple tiers, and EhCache automatically moves data between them based on usage.
| Tier | Location | Speed | Capacity |
|---|---|---|---|
| Heap | JVM heap | Fastest | Limited by heap size |
| Off-heap | Native memory | Fast | Larger, no GC pressure |
| Disk | Local disk | Slower | Very large, persistent |
A common configuration uses heap for hot data and off-heap to hold a larger working set without increasing garbage collection pressure.
Setting Expiration Policies
Stale data can cause bugs, so expiration is essential. EhCache supports time-to-live (TTL) and time-to-idle (TTI) policies:
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class,
ResourcePoolsBuilder.heap(1000))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(
Duration.ofMinutes(10)))
.build();
- Time-to-live — entry expires a fixed time after creation.
- Time-to-idle — entry expires after a period of no access.
XML Configuration
If you prefer externalized configuration, EhCache supports XML. Create an ehcache.xml file:
<config xmlns='http://www.ehcache.org/v3'>
<cache alias="productCache">
<key-type>java.lang.Long</key-type>
<value-type>java.lang.String</value-type>
<expiry>
<ttl unit="minutes">10</ttl>
</expiry>
<resources>
<heap unit="entries">1000</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
</config>
Load it like this:
URL myUrl = getClass().getResource("/ehcache.xml");
CacheManager cacheManager =
CacheManagerBuilder.newCacheManager(new XmlConfiguration(myUrl));
cacheManager.init();
Integrating with Spring
EhCache works well with Spring's @Cacheable abstraction through JCache. Enable caching and let annotations handle the rest:
@Configuration
@EnableCaching
public class CacheConfig {
// JCacheManagerFactoryBean or JCacheCacheManager wiring here
}
@Service
public class ProductService {
@Cacheable(value = "productCache", key = "#id")
public Product findProduct(Long id) {
// Expensive database lookup — cached after first call
return productRepository.findById(id);
}
}
Best Practices
- Size your caches deliberately. Overly large heap caches increase GC pauses. Prefer off-heap for large datasets.
- Always set expiration.
Top comments (0)