Spring Cache - Part 5 - CacheEvict

In the Spring Cache series so far (Part1, Part2, Part3, Part4), we have seen examples where the data is getting added to the cache through the use of @Cacheable and @CachePut. But, what about removing data from cache? Consider a call to delete the record from Service or DB, in which case, we would also want to delete that record from the cache.


We have @CacheEvict to our rescue. When a method is annotated with @CacheEvict, spring removes the data from the cache for the provided key, on the invocation of the method. @CacheEvict supports all the attributes that @Cacheable does. Plus it has a few more.

Let's explore eviction through various examples.

First and foremost, the code we would use for our tests.

ActorService, that provides the persistence for movie actors.

public class ActorService {

 @Cacheable(value = "actors", key = "#name")
 public Actor getActor(String name) {
  //The print that proves the method is executed.
  System.out.println("getting actor " + name);
  //retrieve the actor from DB and return
  return new Actor(name, Gender.MALE);
 }
 
 //other methods 
}

And the code to run the tests.

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.codelooru.cache");
 ActorService service = context.getBean("actorService", ActorService.class);
 
 service.getActors("sean");
 service.getActors("meryl");
 service.getActors("sean");
 service.getActors("meryl");

On the initial run, we would see the following output.
    getting actor sean
    getting actor meryl

CacheEvict with Key


Let's add a removeActor method to the ActorService as shown below. The method is annotated with @CacheEvict with the value set to the cachename, from which the data needs to be evicted.

 @CacheEvict(value = "actors", key = "#name")
 public void removeActor(String name) {
  System.out.println("--removedActor--");
 }

And modify the test code as below and run it

 service.getActor("sean");
 service.getActor("meryl");
 service.getActor("sean");
 service.getActor("meryl");

 service.removeActor("sean");
 service.getActor("sean");
 service.getActor("meryl");

This would result in

getting actor sean
getting actor meryl
--removedActor--
getting actor sean

In this example, we called removeActor with key = "sean", which removed sean from the cache. Therefore, the subsequent call to getActor for sean resulted in the execution of the method, but the call with "meryl" did not.

CacheEvict with conditions


Let's add a condition for eviction

 @CacheEvict(value = "actors", key = "#name", condition="#name == 'meryl'")
 public void removeActor(String name)

Running the test code would result in the following.

 getting actor sean
 getting actor meryl
 --removedActor--

This means that the call to removeActor did not remove the data element from cache, as the condition was checking for the name to match 'meryl'. Let's change the condition to 'sean'

 @CacheEvict(value = "actors", key = "#name", condition="#name == 'sean'") 
 public void removeActor(String name) 

Running the test code would result in the following

getting actor sean
getting actor meryl
--removedActor--
getting actor sean

Now, we see that the removeActor did remove the data element for 'sean' from the cache.

unless works in a similar way, except that its expression evaluated after the execution of the method; thereby allowing you to use the result of the method in the expression. See Part3 for more details on how unless works.

CacheEvict with allEntries


Let's modify removeActor as shown below.

 @CacheEvict(value = "actors", allEntries = true) 
 public void removeActor(String name)

Running the test code results in the following output.

getting actor sean 
getting actor meryl 
--removedActor-- 
getting actor sean 
getting actor meryl 

As you see, setting the allEntries attribute to true, removed all the data elements from the "actors" cache. Therefore, the subsequent calls with both sean and meryl result in the execution of the methods.

Final Notes



  • Either key or allEntries attribute should be present for cache eviction.
  • Do not use both @Cacheable and @CacheEvict on a method. It does not solve any purpose.
  • @CacheEvict does not support retention based eviction e.g. evict LeastRecentlyUsed, LeastFrequentlyUsed, etc. For such policies, you will have to rely on 3rd party cache providers like ehCache.