What strategies exist for using Spring Cache on methods that take an array or collection parameter?
Asked Answered
A

2

9

I want to use Spring's Cache abstraction to annotate methods as @Cacheable. However, some methods are designed to take an array or collection of parameters and return a collection. For example, consider this method to find entites:

public Collection<Entity> getEntities(Collection<Long> ids)

Semantically, I need to cache Entity objects individually (keyed by id), not based on the collection of IDs as a whole. Similar to what this question is asking about.

Simple Spring Memcached supports what I want, via its ReadThroughMultiCache, but I want to use Spring's abstraction in order to support easy changing of the cache store implementation (Guava, Coherence, Hazelcast, etc), not just memcached.

What strategies exist for caching this kind of method using Spring Cache?

Aeri answered 11/11, 2015 at 18:59 Comment(0)
U
9

Spring's Cache Abstraction does not support this behavior out-of-the-box. However, it does not mean it is not possible; it's just a bit more work to support the desired behavior.

I wrote a small example demonstrating how a developer might accomplish this. The example uses Spring's ConcurrentMapCacheManager to demonstrate the customizations. This example will need to be adapted to your desired caching provider (e.g. Hazelcast, Coherence, etc).

In short, you need to override the CacheManager implementation's method for "decorating" the Cache. This varies from implementation to implementation. In the ConcurrentMapCacheManager, the method is createConcurrentMapCache(name:String). In Spring Data GemFire, you would override the getCache(name:String) method to decorate the Cache returned. For Guava, it would be the createGuavaCache(name:String) in the GuavaCacheManager, and so on.

Then your custom, decorated Cache implementation (perhaps/ideally, delegating to the actual Cache impl, from this) would handle caching Collections of keys and corresponding values.

There are few limitations of this approach:

  1. A cache miss is all or nothing; i.e. partial keys cached will be considered a miss if any single key is missing. Spring (OOTB) does not let you simultaneously return cache values and call the method for the diff. That would require some very extensive modifications to the Cache Abstraction that I would not recommend.

  2. My implementation is just an example so I chose not to implement the Cache.putIfAbsent(key, value) operation (here).

  3. While my implementation works, it could be made more robust.

Anyway, I hope it provides some insight in how to handle this situation properly.

The test class is self-contained (uses Spring JavaConfig) and can run without any extra dependencies (beyond Spring, JUnit and the JRE).

Cheers!

Unknow answered 12/11, 2015 at 21:15 Comment(4)
I should also point out that if you know ahead of time that you are not planning to switch caching providers, then you can just implement the Cache operation in terms of the provider. For instance, if Memcached handles this using the ReadThroughMultiCache, then you could just implement the Cache.get(key) operation (and possibly, put?) interms of ReadThroughMultiCache. My solution, however, attempted to be (mostly) caching provider agnostic per your description.Unknow
John, I appreciate the effort and idea, but limitation #1 is a deal-breaker for me. We need to cache intelligently so that items in the key list will NOT be "all-or-nothing." We need to use cached items from the list whenever they're present.Aeri
Right, unfortunately, off the top of my head, I cannot think of a better way to make this more intelligent other than tying yourself (to a certain degree) on on your underlying caching provider. Anyway, the framework is flexible in so much as it gives you an option for extension, to customize the behavior. However, out-of-the-box, this would be very difficult to support, especially consistently across caching providers. Open to suggestions or other ideas on such matters.Unknow
@JohnBlum very usefulPhilharmonic
M
-3

Worked for me. Here's a link to my answer. https://mcmap.net/q/376052/-spring-cache-jsr107-list-collection-argument-as-part-of-the-key

TL:DR

@Cacheable(cacheNames = "test", key = "#p0")
public List<String> getTestFunction(List<String> someIds) {

My example is with String but it also works with Integer and Long, and probably others.

Myrmecophagous answered 2/4, 2020 at 13:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.