Home > Net >  How should I implement caching using Spring Boot for crud operations
How should I implement caching using Spring Boot for crud operations

Time:03-05

I am new to implementing spring cache and is having trouble choosing the right way to use caching. below are some of the methods in user service as an example :

  @NotNull
  @Cacheable("usersCache")
  Optional<User> getUser(@NotNull Long id, @NotNull CompanyDetails companyDetails);
  
  @NotNull
  @Cacheable("usersCache")
  Pageable<User> getUsers(@NotNull CompanyDetails companyDetails, @NotNull Long offset, @NotNull Long limit);

  @NotNull
  @Cacheable("usersCache")
  List<User> getUsers(@NotNull CompanyDetails companyDetails);
  
  @NotNull
  @Cacheable("usersCache")
  Optional<User> getUser(@NotNull String key, @NotNull CompanyDetails companyDetails);
  
  
  @NotNull
  Optional<UserDetails> createUser(@NotNull final UserRequest request, @NotNull CompanyDetails companyDetails);
  
  @NotNull
  Optional<User> updateUser(@NotNull final UserRequest request, @NotNull CompanyDetails companyDetails);
  
  @NotNull
  Optional<User> deleteUser(@NotNull final Long id, @NotNull CompanyDetails companyDetails); 

Since keys are generated based on the parameters values by default, the getUser(@NotNull String key) and getUser(@NotNull Long id) will have different entries in cache for the same user. This is one of the hurdles I am facing as this means I can have many duplicate entries due to this.

The second concern is that I have two getUsers methods to fetch users, one with pagination and the other without. These two method will have their own entry in the cache and this means that there will be a lot of duplicates and to make things worse, for each different offset and limit combinations, there will be an entry in cache, which can create a lot of duplicates.

Now there are create, update and delete methods. Given the methods update, delete and getUser methods will have id as a parameter value directly or as a field in UserRequest object, shouldn't I be using this id as a part of the key along with other parameters.

Also, please consider the below case :

@NotNull
  Optional<UserDetails> createUser(@NotNull final UserRequest request, @NotNull CompanyDetails companyDetails);

Here the user is added and whenever a new user is added, should I flush the entire usersCache or should I wait for TTL to clear the cache?

Is there an effective approach to follow given a lot of duplicates are added in cache for each method because they have different parameters. Should I use the default key generation by spring or should I follow any particular approach in specifying the keys?

CodePudding user response:

You are mixing the same cache with different key types. I would suggest using a key generator, like

@Cacheable(cacheNames = "mycache", key = "#object.id")
public Object myMethod( Object object )

Otherwise, it will not work because the equals-method can never return true for different types, like CompanyDetails and SimpleKey.

CodePudding user response:

As already mentioned above, you can not use same cacheName for different return type functions.

You can define custom cache key to resolve your problem.

I would suggest something like :

  @NotNull
  @Cacheable(cacheNames = {"usersCache"}, key = "{#id}")
  Optional<User> getUser(@NotNull Long id, @NotNull CompanyDetails companyDetails);

  @NotNull
  @Cacheable(cacheNames = {"usersCache"}, key = "{#key}")
  Optional<User> getUser(@NotNull String key, @NotNull CompanyDetails companyDetails);

  @NotNull
  @Cacheable(cacheNames = {"pageableUsersCache"}, key = "{#companyDetails.id,#offset,#limit}")
  Pageable<User> getUsers(@NotNull CompanyDetails companyDetails, @NotNull Long offset, @NotNull Long limit);

  @NotNull
  @Cacheable(cacheNames = {"userListCache"}, key = "{#companyDetails.id}")
  List<User> getUsers(@NotNull CompanyDetails companyDetails);

  @NotNull
  Optional<UserDetails> createUser(@NotNull final UserRequest request,      @NotNull CompanyDetails companyDetails);
  
 /**
   * If you want to cache user when it is created then you need to change the signature of your function to 
   * return Optional<User>
   */
  @NotNull
  @CachePut(cacheNames = {"usersCache"}, key = "{#request.id}")
  Optional<User> createUser(@NotNull final UserRequest request, @NotNull CompanyDetails companyDetails);

  @NotNull
  // CachePut will update the cache for defined key and you don't have to manage eviction
  @CachePut(cacheNames = {"usersCache"}, key = "{#request.id}")
  @CacheEvict(cacheNames = {"userListCache"}, key = "{#companyDetails.id}")
  Optional<User> updateUser(@NotNull final UserRequest request, @NotNull CompanyDetails companyDetails);

  @NotNull
  // This should evict both the caches as listed based on the defined key
  @Caching(evict = {
          @CacheEvict(cacheNames = {"usersCache"}, key = "{#id}"),
          @CacheEvict(cacheNames = {"userListCache"}, key = "{#companyDetails.id}")})
  Optional<User> deleteUser(@NotNull final Long id, @NotNull CompanyDetails companyDetails);

For pageableUsersCache it will not be easy to just evict the cache upon user deletion or update as you need to have the knowledge of full key to delete it. Or you can define custom annotation which can delete keys by a pattern like "companyDetails.id*". Otherwise, you can delete all cache entries for pageableUsersCache by using :

  @Caching(evict = {
          @CacheEvict(cacheNames = {"usersCache"}, key = "{#id}"),
          @CacheEvict(cacheNames = {"userListCache"}, key = "{#companyDetails.id}"),
          @CacheEvict(cacheNames = {"pageableUsersCache"}, allEntries = true)})
  Optional<User> deleteUser(@NotNull final Long id, @NotNull CompanyDetails companyDetails);

  • Related