Search code examples
javaspringspring-bootcachingspring-data-jpa

Spring boot populating cache used by findById method with findAll method


I have a simple service in a springboot application, one of the methods gets an object from the database by Id(Primary key). I also have another method that returns all the objects in that table.

@Cacheable("stores")
public List<Store> findAllStores() throws InterruptedException {
    Thread.sleep(5000);
    return storeRepository.findAll();
}

@Cacheable("stores")
public Store findById(int storeId) throws InterruptedException {
    Thread.sleep(5000);
    return storeRepository.findById(storeId).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "No store with specified ID exist"));
}

In my tests I called the first method, which populated the cache called "stores" with all the store objects from the table, but when I call the second method to individually find a store by Id, i still get the 5 second wait time? Why is this happening? There are no errors and cache is not a variable so i'm finding it really hard to debug this problem, as I cant see the contents of the cache during debug session.


Solution

  • Any data stored in a cache requires a key for its speedy retrieval. Spring, by default, creates caching keys using the annotated method's signature as key.

    So here you are basically using two different methods and they will be using separate keys which is method signature. So if you call the same method again only then the result will be returned from cache instead of getting data from DB. I hope you get that. You can check below link

    https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html#:~:text=Annotation%20Type%20Cacheable&text=Annotation%20indicating%20that%20the%20result,invoked%20for%20the%20given%20arguments.

    And your expectation is totally wrong, as told above each cache needs a cacheKey and it stores values against that key, if you execute your second method - first time you get Store from storeId and it's saved in cache with method signature as key which will also have storeId as key, so second time you call the same method it will get it from cache since it already has entry for that key. the first method and second method key's are different so your expectation will be wrong.

    <---->Edit<--->

    You can modify your code like below, specifying the cacheID explicitly.

    @Cacheable("stores",key = "#storeId")
    public Store findById(int storeId) throws InterruptedException {
       
        return storeRepository.findById(storeId).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "No store with specified ID exist"));
    }
    

    Remove the findAllStores() method its not necessary. And use directly cacheManager to store the values. Something like below.

    @Autowired
     CacheManager cacheManager;
    @Autowired
     StoreRepository repo;
    
    public void loadCache() {
      for(Store store:repo.getAll()) {
        cacheManager.getCache("stores").put(store.getId(),store);
      }
    }
    

    You have to call this on startUp. That's it.