I need a simple cache for storing tuples in memory with a certain time-to-live. I couldn't find a way to do that on EHcache website, which contains mostly complex usage scenarios. Can anyone help me out?
PS I don't use Spring.
CacheManager cacheManager = initCacheManager();
CacheConfiguration cacheConfiguration = new CacheConfiguration().name("myCache")
.maxEntriesLocalHeap(100)
.timeToLiveSeconds(20);
cacheManager.addCache(new Cache(cacheConfiguration));
<cache name="myCache"
maxEntriesLocalHeap="100"
timeToLiveSeconds="20"/>
Element
Ehcache 2.x allows your to override expiry settings per Element
:
Element element = new Element("key", "value");
element.setTimeToLive(10);
cache.put(element);
CacheManager cacheManager = initCacheManager();
CacheConfigurationBuilder<Long, String> configuration =
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder
.heap(100))
.withExpiry(Expirations.timeToLiveExpiration(new Duration(20, TimeUnit.SECONDS)));
cacheManager.createCache("myCache", configuration);
In Ehcache 3, the builder is immutable and can be safely shared to create multiple caches from a similar configuration. And the code will be more compact if you use static imports, which I did not do here to ease pasting this snippet in an IDE.
<cache alias="myCache">
<expiry>
<ttl unit="seconds">20</ttl>
</expiry>
<heap>100</heap>
</cache>
Expiry
in Ehcache 3.x, Expiry
is an interface which users can implement:
public interface Expiry<K, V> {
Duration getExpiryForCreation(K key, V value);
Duration getExpiryForAccess(K key, ValueSupplier<? extends V> value);
Duration getExpiryForUpdate(K key, ValueSupplier<? extends V> oldValue, V newValue);
}
time-to-live matches the getExpiryForCreation
invocation, which will receive the key and value of the mapping, allowing to implement different expirations depending on the mapping itself.
This solution was tested as working fine on Ehcache 3.8.1, but the code is for sure valid for any Ehcache 3.x
First create a class to keep a generic, in fact any object you are able pass to cache:
import java.io.Serializable;
import java.time.Duration;
public class EhcacheValue<T> implements Serializable {
private static final long serialVersionUID = 1L;
private T object;
private long timeToLive;
public EhcacheValue(T theObject, Duration theDuration) {
object = theObject;
timeToLive = theDuration.getSeconds();
}
public Duration getTimeToLiveDuration() {
return Duration.ofSeconds(timeToLive);
}
public T getObject() {
return object;
}
public void setObject(T theObject) {
object = theObject;
}
public long getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(long theTimeToLive) {
timeToLive = theTimeToLive;
}
}
Then create a custom Expiry class implementing the Ehcache 3.x interface ExpiryPolicy :
import java.time.Duration;
import java.util.function.Supplier;
import org.ehcache.expiry.ExpiryPolicy;
public class CustomExpiryPolicy<K, V extends EhcacheValue<?>> implements ExpiryPolicy<K, V> {
public CustomExpiryPolicy() {
}
@Override
public Duration getExpiryForCreation(K theKey, V theValue) {
return theValue.getTimeToLiveDuration();
}
@Override
public Duration getExpiryForAccess(K theKey, Supplier<? extends V> theValue) {
return null;
}
@Override
public Duration getExpiryForUpdate(K theKey, Supplier<? extends V> theOldValue, V theNewValue) {
return theNewValue.getTimeToLiveDuration();
}
}
Then you have the normal code, but with a few important lines on the way :
public class TestEhCache {
private static final String CACHE_TIER_HEAP = "OnHeap";
public static void main(String[] args) {
StatisticsService statisticsService = new DefaultStatisticsService();
myCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.using(statisticsService)
.build();
//we init it here, not with boolean in build()
myCacheManager.init();
// Beware, the items are not expiring automatically, there are more mechanism in place for checking the expiration
// For instance the expiration is checked at get time
// for more details see the documentation from the version 2.8
// here: https://www.ehcache.org/documentation/2.8/apis/cache-event-listeners.html
// Unfortunately for the version 3.x doesn't exist a detailed depiction, but in main the procedure is the same
//
// Ehcache 2.8 documentation:
// Elements are checked for expiry in Ehcache at the following times:
//
// When a get request is made
// When an element is spooled to the diskStore in accordance with a MemoryStore eviction policy
// In the DiskStore when the expiry thread runs, which by default is
// net.sf.ehcache.Cache#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS
CustomExpiryPolicy<String,EhcacheValue<?>> expiryPolicy = new CustomExpiryPolicy<String,EhcacheValue<?>>();
ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2000L, EntryUnit.ENTRIES).build();
@SuppressWarnings("unchecked")
Class<EhcacheValue<?>> myEhcacheValue = (Class<EhcacheValue<?>>)(Class<?>)EhcacheValue.class;
CacheConfiguration<String,EhcacheValue<?>> cacheConfiguration = CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, myEhcacheValue, resourcePools)
.withExpiry(expiryPolicy)
//.withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics, it seems is not needed
.build();
myCache = myCacheManager.createCache("myCache", cacheConfiguration);
myCacheStatistics = statisticsService.getCacheStatistics("myCache");
long cacheEntriesNr = myCacheStatistics.getTierStatistics().get(CACHE_TIER_HEAP).getMappings();
//nb element in heap tier
long sizeEntriesByStatistics = myCacheStatistics.getTierStatistics().get("OnHeap").getMappings();
//size of the tier in memory, when you set memory limits, not in this case
//long sizeBytesByStatistics = myCacheStatistics.getTierStatistics().get("OnHeap").getOccupiedByteSize();
long getsCnt = myCacheStatistics.getCacheGets();
long putsCnt = myCacheStatistics.getCachePuts();
long removalsCnt = myCacheStatistics.getCacheRemovals();
long missesCnt = myCacheStatistics.getCacheMisses();
long evictionsCnt = myCacheStatistics.getCacheEvictions();
long crtExpiredCnt = myCacheStatistics.getCacheExpirations();
System.out.println("getsCnt = "+getsCnt);
System.out.println("putsCnt = "+putsCnt);
System.out.println("removalsCnt = "+removalsCnt);
System.out.println("missesCnt = "+missesCnt);
System.out.println("evictionsCnt = "+evictionsCnt);
System.out.println("expiredCnt = "+expiredCnt);
System.out.println("hitPercentage = "+hitPercentage);
System.out.println("missPercentage = "+missPercentage);
System.out.println("Computed number of items in cache = "+(putsCnt-removalsCnt-expiredCnt-evictionsCnt));
}
@SuppressWarnings("unchecked")
// we are aware about the unchecked cast - it is a desired behavior.
// Cache can store objects of various types, so compile time type safety cannot be achieved easily.
// We'll get runtime ClassCastException if cache is used incorrectly.
public static <T> T get(String key) {
EhcacheValue<T> ehcacheValue = (EhcacheValue<T>)myCache.get(key);
return (T) (ehcacheValue!=null?ehcacheValue.getObject():null);
}
// we are aware about the unchecked cast - it is a desired behavior.
// Cache can store objects of various types, so compile time type safety cannot be achieved easily.
// We'll get runtime ClassCastException if cache is used incorrectly.
public static <T extends Object> T put(String key, T value, int timeToLiveInSeconds) {
if (key == null) {
throw new AssertionError("Key must not be null!");
}
if (value != null) {
EhcacheValue<T> ehcacheValue = new EhcacheValue<T>(value, Duration.of(60, ChronoUnit.SECONDS));
myCache.put(key, ehcacheValue);
return value;
} else {
return null;
}
}
}
As for EhCache version 3.3.1, groupId:org.ehcache,artifactId:ehcache
, the following works!
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("AllCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,String.class,
ResourcePoolsBuilder.heap(100))
.withExpiry(Expirations.timeToLiveExpiration(new Duration(86400, TimeUnit.SECONDS)))
.build()).build(true);
Cache<String, String> allCache = cacheManager.getCache("AllCache", String.class, String.class);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.