I am using Spring Cache, where I pass in a collection of keys, and the return is a list of entities. I would like to have the caching framework understand that each element
In fact, it is possible, even with Spring's Caching Abstraction, but not out-of-the-box (OOTB). Essentially, you must customize Spring's caching infrastructure (Explained further below)
By default, Spring's caching infrastructure uses the entire @Cacheable
method parameter arguments as the cache "key", as explained here. Of course you can also customize the key resolution using either a SpEL Expression or with a custom KeyGenerator
implementation, as explained here.
Still, that does not break up the collection or array of parameter arguments along with the @Cacheable
method's return value into individual cache entries (i.e. key/value pairs based on the array/collection or Map).
For that, you need a custom implementation of Spring's CacheManager
(dependent on your caching strategy/provider) and Cache
interfaces.
NOTE: Ironically, this will be the 3rd time I have answered nearly the same question, first here, then here and now here, :-). Anyway...
I have updated/cleaned up my example (a bit) for this posting.
Notice that my example extends and customizes the ConcurrentMapCacheManager provided in the Spring Framework itself.
Theoretically, you could extend/customize any CacheManager
implementation, like Redis's in Spring Data Redis, here (source), or Pivotal GemFire's CacheManager
in Spring Data GemFire, here (source). The open source version of Pivotal GemFire is Apache Geode, which has a corresponding Spring Data Geode project, (source for CacheManager in Spring Data Geode, which is basically identical to SD GemFire). Of course, you can apply this technique to other caching providers... Hazelcast, Ehcache, etc.
However, the real guts of the work is handled by the custom implementation (or mores specifically, the base class) of Spring's Cache interface.
Anyway, hopefully from my example, you will be able to figure out what you need to do in your application to satisfy your application's caching requirements.
Additionally, you can apply the same approach to handling Maps
, but I will leave that as an exercise for you, ;-).
Hope this helps!
Cheers, John
With @CachePut
and a helper method you can achieve it very simply like this :
public List<Country> getAllByCode(List<String>codes) {
return countryDao.findAllInCodes(codes);
}
public void preloadCache(List<String>codes) {
List<Country> allCountries = getAllByCode(codes);
for (Country country : allCountries) {
cacheCountry(country);
}
}
@CachePut
public Country cacheCountry(Country country) {
return country;
}
Note
This will only add values to the cache, but never remove old values. You can easily do cache eviction before you add new values
Option 2
There is a proposal to make it work like this :
@CollectionCacheable
public List<Country> getAllByCode(List<String>codes) {
See :
If you are impatient take the code from GitHub and integrate locally