Spring Caching Simple (@Cacheable) Example Class Level Part 2

In this post under Spring Caching, I will show with example how “@Cacheable” at class level affects the functioning of the class method.

In previous post I have already explained how to enable and use caching at class level but with a class having just one single method.

Below is the “Calculator” class that I used in the previous post

Calculator

package caching.package2;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
@Cacheable(cacheNames = "calculate_region")
public class Calculator {
public int calculate() {
System.out.println("Calculating");
int sum = 0;
for(int i = 1; i < 1000000; i++) {
sum = sum + i;
}
return sum;
}
}

In the above code, cache region name is declared at class level.

So if we change the above “Calculator” class as below

Calculator

package caching.package3;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
@Cacheable(cacheNames = "calculate_region")
public class Calculator {
public int add() {
System.out.println("Calculating");
return 1 + 2;
}
public int substract() {
System.out.println("Calculating");
return 2 - 1;
}
public int multiply() {
System.out.println("Calculating");
return 1 * 2;
}
}

If you note we have added three methods “add”, “substract”, and “multiply”. Now these 3 method don’t have there own cache region.

As a result, they will use the same cache region declared at class level “calculate_region”.

So when we run the code again with the below main class.

Main class

package caching.package3;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@EnableCaching
@Configuration
@ComponentScan(basePackages = "caching.package3")
public class Example3 {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Example3.class);
Calculator calculator = annotationConfigApplicationContext.getBean(Calculator.class);
System.out.println("-------------------------------------");
System.out.println("add call started iteration 1");
System.out.println(calculator.add());
System.out.println("add call finished iteration 1");
System.out.println("-------------------------------------");
System.out.println("add call started iteration 2");
System.out.println(calculator.add());
System.out.println("add call finished iteration 2");
System.out.println("-------------------------------------");
System.out.println("substract call started iteration 1");
System.out.println(calculator.substract());
System.out.println("substract call finished iteration 1");
System.out.println("-------------------------------------");
System.out.println("substract call started iteration 2");
System.out.println(calculator.substract());
System.out.println("substract call finished iteration 2");
System.out.println("-------------------------------------");
System.out.println("multiply call started iteration 1");
System.out.println(calculator.multiply());
System.out.println("multiply call finished iteration 1");
System.out.println("-------------------------------------");
System.out.println("multiply call started iteration 2");
System.out.println(calculator.multiply());
System.out.println("multiply call finished iteration 2");
System.out.println("-------------------------------------");
}
}

The output will be as shown below

Output

-------------------------------------
add call started iteration 1
Calculating
3
add call finished iteration 1
-------------------------------------
add call started iteration 2
3
add call finished iteration 2
-------------------------------------
substract call started iteration 1
3
substract call finished iteration 1
-------------------------------------
substract call started iteration 2
3
substract call finished iteration 2
-------------------------------------
multiply call started iteration 1
3
multiply call finished iteration 1
-------------------------------------
multiply call started iteration 2
3
multiply call finished iteration 2
-------------------------------------

The output will always print 3 which is the result of “add” method.

In the first call, the “add” method is called and then afterwards it returns 3 from the cache regardless of whether you are calling “substract” or “multiply”.

Which is wrong in this case.

So to fix this we have to modify the “Calculator” class as shown below

Calculator

package caching.package3;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
@Cacheable(cacheNames = "calculate_region")
public class Calculator {
@Cacheable(cacheNames = "add_region")
public int add() {
System.out.println("Calculating");
return 1 + 2;
}
@Cacheable(cacheNames = "substract_region")
public int substract() {
System.out.println("Calculating");
return 2 - 1;
}
public int multiply() {
System.out.println("Calculating");
return 1 * 2;
}
}

As you can see in the above code, we have repeatedly applied “@Cacheable” annotation at method level with different cache names for “add” and “substract” method in addition to “@Cacheable” annotation at class level.

Now the “add” method will use the cache region named “add_region” and “substract” method will use the cache region named “substract_region” but “multiply” will still use cache region named “calculate_region”.

Now when we run the code the output will be as shown below

Output

-------------------------------------
add call started iteration 1
Calculating
3
add call finished iteration 1
-------------------------------------
add call started iteration 2
3
add call finished iteration 2
-------------------------------------
substract call started iteration 1
Calculating
1
substract call finished iteration 1
-------------------------------------
substract call started iteration 2
1
substract call finished iteration 2
-------------------------------------
multiply call started iteration 1
Calculating
2
multiply call finished iteration 1
-------------------------------------
multiply call started iteration 2
2
multiply call finished iteration 2
-------------------------------------

So when applying “@Cacheable” annotation at class level with a cache region, please remember the cache region will be reused by all the class methods.

And if you don’t want that, apply at method level the “@Cacheable” annotation with different cache region name.

Leave a comment