Caching on methods in JpaRepository (Spring Data)

14,835

I am assuming that you have required configuration already set up and the stack trace you have posted is the problem. So let's dig it.

There are two problems I see:

  1. java.lang.IllegalStateException: No cache could be resolved, At least one cache should be provided per cache operation.

    Resolution: Whenever you want to cache the data or evict the data you MUST provide the name of the cache, which I don't see provided in your code.

    @Cacheable's cacheNames or value should be defined in order to get the cache working.

    Example : @Cacheable(value = "usersCache")

  2. The proper cache key

    Because cache works on key-value pair, you should provide a proper cache key. If you don't provide the cache key then, by default, a default key generation strategy that creates a SimpleKey that consists of all the parameters with which the method was called.

Suggestion: You should provide the cache key manually.

Example :

@Cacheable(value = "usersCache", key = "#username")
User findByUsername(String username);

Note: Make sure username is unique because cache key must be unique.

You can read more Spring cache annotations: some tips & tricks

Share:
14,835
xiaoli
Author by

xiaoli

Updated on June 18, 2022

Comments

  • xiaoli
    xiaoli almost 2 years

    Tools: Spring-Boot : 1.5.9.RELEASE Spring-Data-JPA : 1.11.9.RELEASE

    Issue: Currently I have a repository that extended from JpaRepository. In order to avoid frequent DB access, I want to cache some of the CRUD methods in the JpaRepository. I tried a few ways from what I can find with Mr.Google but non of them working except one.

    EDITED 1. Solution mentioned in this link is workable. However, there is a bad practice (redundancy to me) at here. Imagine if I have 50 repositories extending the JpaRepository, this means that I have to override the save method in 50 repositories.

         public interface UserRepository extends CrudRepository<User, Long> {
            @Override
            @CacheEvict("user")
            <S extends User> S save(S entity);
    
            @Cacheable("user")
            User findByUsername(String username);
         }
    

    EDITED 2. Extend the JpaRepository interface. I saw something that might works at link2.

    In the link, it mentioned 3 different ways to caching the JpaRepository methods. the 1st method is same as what I mentioned in #1. However, I want something similar to 2nd/3rd method so that I no need to keep repeating overriding the CRUD methods in all repositories.

    Below is some sample code that I have written.

        @NoRepositoryBean        
        public interface BaseRepository<T, ID extends Serializable> extends 
        JpaRepository<T, ID> {
    
            @CacheEvict
            <S extends User> S save(S entity);
    
            @Cacheble
            T findOne(ID id);
        }
    
        @Repository
        @CacheConfig("user")
        public interface UserRepository extends BaseRepository<User, Integer> {
            // when I calling findOne/save method from UserRepository, it should 
            // caching the methods based on the CacheConfig name defined in the 
            // child class.
        }
    

    However, it seems like the code (above) ain't working as I getting below exception. I understand the issue mainly happened because there is no name being assigned to the cacheable annotation in the BaseRepository. But I would need to cache the CRUD methods in the BaseRepository that extend from JpaRepository.

    java.lang.IllegalStateException: No cache could be resolved for 'Builder[public abstract java.util.List com.sdsap.app.repository.BaseRepository.findAll()] caches=[] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'' using resolver 'org.springframework.cache.interceptor.SimpleCacheResolver@30a9fd0'. At least one cache should be provided per cache operation.

    I have been asking Mr.Google for few days and yet can't find any suitable solution. I hope someone can help me at here. Sorry if my question isn't clear or missing something as this is my first time posting at here. Thanks!

  • xiaoli
    xiaoli about 6 years
    Thanks for the reply. I already have all the configuration setup and the application is running fine. I'm just left with the caching issue. I understand that a cacheable annotation must be provided with a name. However, currently I have a baserepository that extends jparepository. I can't put a specific name to the cacheable in baserepository since it will be use by all repositories as a base.
  • xiaoli
    xiaoli about 6 years
    Thanks for the reply. I haven't use CacheResult before. That's interesting. But my main issue is mainly how to cache the basic CRUD methods in a baserepository that extends from jparepository.
  • Shanu Gupta
    Shanu Gupta about 6 years
    you could implement the crud methods you want and just call super.method in the implementation? And then put the @CacheResult over them.
  • xiaoli
    xiaoli about 6 years
    I did implement the crud method before. However, when I try to implement them in a BaseRepositoryImpl, the cacheable still can't work (same error - No cache could be resolved). I haven't try with the CacheResult yet but I will give it a try. Thanks for the help!!!
  • Bob 1174
    Bob 1174 almost 4 years
    This answer is completely irrelevant to the question asked. The question basically is how would the spring CrudRepository implementation gets the name of the cache from @CacheConfig annotation defined in UserRepository?