1

I am using JSR107 caching with Springboot. I have following method.

@CacheResult(cacheName = "books.byidAndCat")
public List<Book> getAllBooks(@CacheKey final String bookId, @CacheKey final BookCategory bookCat) {

return <<Make API calls and get actual books>>
}

First time it makes actual API calls, and second time it loads cache without issue. I can see the following part of log.

Computed cache key SimpleKey [cb5bf774-24b4-41e5-b45c-2dd377493445,LT] for operation CacheResultOperation[CacheMethodDetails ...

But the problem is I want to load cache without making even first API call, Simply needs to fill the cache like below.

String cacheKey  = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();     
        cacheManager.getCache("books.byidAndCat").put(cacheKey, deviceList);

When I am checking, hashcode of cachekeys are same in both cases, But it is making API calls. If the hashcode is same in both cases, why it is making API calls without considering the cache ?

When debugging spring classes identified that, org.springframework.cache.interceptor.SimpleKeyGenerator is used with the cache key generation even @CacheResult is there. EDIT and enhance the question :

Apart from that if getAllBooks has overloaded methods, and then call this cached method via separate overloaded method, in that case also method caching is not working.

2 Answers2

1

I'm not an expert of JSR107 annotations in the context of Spring. I use the Spring Cache annotations instead.

When using JSR107, the key used is a GeneratedCacheKey. So that's what you should put in your cache. Not the toString() of it. Note that SimpleKeyGenerator isn't returning a GeneratedCacheKey. It returns a SimpleKey which is the key used by Spring when using its own cache annotations instead of JSR-107. For JSR-107, you need a SimpleGeneratedCacheKey.

Then, if you want to preload the cache, just call getAllBooks before needing it.

If you want to preload the cache in some other way, a @javax.cache.annotation.CachePut should do the trick. See its javadoc for an example.

Henri
  • 5,551
  • 1
  • 22
  • 29
  • I agree, it should be generatedCacheKey and we should use the key-generator with the cacheresult annotation. I vote up your answer. But only thing, I want to preload cache without making API calls. If it is API calls, then cache key generation is automatic, and do not want to do much things. – Ruchira Kariyawasam May 26 '18 at 18:38
  • Do we have proper sample of GeneratedCacheKey? This ipmlementation (https://www.javatips.net/api/spring-framework-master/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/KeyGeneratorAdapter.java) is not clear – Ruchira Kariyawasam May 27 '18 at 18:47
  • 1
    If you want to set the cache in some other way, you might want a `@CachePut`. I will update the answer accordingly. – Henri May 27 '18 at 22:20
  • 1
    About the key, you need a `SimpleGeneratedCacheKey` – Henri May 27 '18 at 22:26
  • that cachput would be good approach. It should handle concurrency and when updating, all the other result should wait till update completes. – Ruchira Kariyawasam May 27 '18 at 23:03
  • I think we can use cacheput dummy method, method do not have anything, but just use to put cache only. @Henri, do you see any issues with it ? , If we use sync annotation, it mean all the requests will wait till complete cacheput – Ruchira Kariyawasam May 27 '18 at 23:38
0

As @Henri suggested, we can use the cacheput. But for that we need methods. With the below we can update the cache very similar to the cacheput,

//overloaded method both id and cat is available.

List<Object> bookIdCatCache = new ArrayList<>();
    bookIdCatCache.add(bookId);
    bookIdCatCache.add(deviceCat);
    Object bookIdCatCacheKey  = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));
    cacheManager.getCache("books.byidAndCat").put(bookIdCatCacheKey , bookListWithIdAndCat);

//overloaded method only id is there

List<Object> bookIdCache = new ArrayList<>();
        String nullKey          = null
        bookIdCache.add(bookId);
        bookIdCache.add(nullKey);
        Object bookIdCacheKey  = SimpleKeyGenerator.generateKey(bookIdCache.toArray(new Object[bookIdCache.size()]));
        cacheManager.getCache("books.byidAndCat").put(bookIdCacheKey , bookListWithId);

//Not correct(My previous implementation)

String cacheKey  = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();

//Correct(This is getting from spring)

Object cacheKey  = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));