Caching on methods in JpaRepository (Spring Data)
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:
-
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")
-
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 aSimpleKey
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
xiaoli
Updated on June 18, 2022Comments
-
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 about 6 yearsThanks 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 about 6 yearsThanks 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 about 6 yearsyou could implement the
crud
methods you want and just callsuper.method
in the implementation? And then put the@CacheResult
over them. -
xiaoli about 6 yearsI 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 almost 4 yearsThis 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?