How update/remove an item already cached within a collection of items
I think something like this schould work... Actually it's only a variation if "Stéphane Nicoll" answer ofcourse, but it may be useful for someone. I write it right here and haven't check it in IDE, but something similar works in my Project.
Override CacheResolver:
@Cacheable(value="products", key="#root.target.PRODUCTS", cacheResolver = "customCacheResolver")
Implement your own cache resolver, which search "inside" you cached items and do the work in there
public class CustomCacheResolver implements CacheResolver{ private static final String CACHE_NAME = "products"; @Autowired(required = true) private CacheManager cacheManager; @SuppressWarnings("unchecked") @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> cacheOperationInvocationContext) { // 1. Take key from query and create new simple key SimpleKey newKey; if (cacheOperationInvocationContext.getArgs().length != null) { //optional newKey = new SimpleKey(args); //It's the key of cached object, which your "@Cachable" search for } else { //Schould never be... DEFAULT work with cache if something wrong with arguments return new ArrayList<>(Arrays.asList(cacheManager.getCache(CACHE_NAME))); } // 2. Take cache EhCacheCache ehCache = (EhCacheCache)cacheManager.getCache(CACHE_NAME); //this one we bringing back Ehcache cache = (Ehcache)ehCache.getNativeCache(); //and with this we working // 3. Modify existing Cache if we have it if (cache.getKeys().contains(newKey) && YouWantToModifyIt) { Element element = cache.get(key); if (element != null && !((List<Products>)element.getObjectValue()).isEmpty()) { List<Products> productsList = (List<Products>)element.getObjectValue(); // ---**--- Modify your "productsList" here as you want. You may now Change single element in this list. ehCache.put(key, anfragenList); //this method NOT adds cache, but OVERWRITE existing // 4. Maybe "Create" new cache with this key if we don't have it } else { ehCache.put(newKey, YOUR_ELEMENTS); } return new ArrayList<>(Arrays.asList(ehCache)); //Bring all back - our "new" or "modified" cache is in there now... }
Read more about CRUD of EhCache: EhCache code samples
Hope it helps. And sorry for my English:(
Caching the collection using the caching abstraction is a duplicate of what the underlying caching system is doing. And because this is a duplicate, it turns out that you have to resort to some kind of duplications in your own code in one way or the other (the duplicate key for the set is the obvious representation of that). And because there is duplication, you have to sync state somehow
If you really need to access to the whole set and individual elements, then you should probably use a shortcut for the easiest leg. First, you should make sure your cache contains all elements which is not something that is obvious. Far from it actually. Considering you have that:
//EhCacheCache cache = (EhCacheCache) cacheManager.getCache("products");
@Override
public Set<Product> findAll() {
Ehcache nativeCache = cache.getNativeCache();
Map<Object, Element> elements = nativeCache.getAll(nativeCache.getKeys());
Set<Product> result = new HashSet<Product>();
for (Element element : elements.values()) {
result.add((Product) element.getObjectValue());
}
return Collections.unmodifiableSet(result);
}
The elements
result is actually a lazy loaded map so a call to values()
may throw an exception. You may want to loop over the keys or something.
You have to remember that the caching abstraction eases the access to the underlying caching infrastructure and in no way it replaces it: if you had to use the API directly, this is probably what you would have to do in some sort.
Now, we can keep the conversion on SPR-12036 if you believe we can improve the caching abstraction in that area. Thanks!