Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching on methods in JpaRepository (Spring Data)

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!

like image 804
xiaoli Avatar asked Apr 23 '18 03:04

xiaoli


People also ask

Does JPA Repository cache?

It caches entities that are explicitly declared to be cacheable. Upon retrieval of a cacheable entity, the JPA runtime first looks for this entity in the persistence context, then in the entity cache, and finally in the database.


1 Answers

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

like image 148
Mehraj Malik Avatar answered Oct 16 '22 16:10

Mehraj Malik