Caching in Spring Boot RESTful Service: Part 1

Caching in Spring Boot RESTful Service: Part 1

0 Comments

Why Use Caching?

When data stored in some database is requested simultaneously from a large user base, the system can get overwhelmed. This happens because for each request the application has to retrieve the data from the database. As the number of simultaneous requests keeps on increasing, the performance of the system degrades increasing latency. You can address this problem using caching.

In this first part of the series on caching, I will explain how to cache frequently retrieved data in a Spring Boot RESTful API.

The Sample Application

I have a bare minimum Spring Boot REST API that enables users to add products and retrieve all products at one go. As it is apparent, I will set up caching for the operation of retrieving all products. I want the application to return all products from the cache instead of querying the database for each request. To enable caching add the following dependency to your pom.xml file.

Here is the caching dependency in the pom.xml file.

<dependency>;
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

The next step is to enable caching in the application by adding the @EnableCaching class-level annotation.

@EnableCaching
@SpringBootApplication
public class RestServiceForProductApplication {

       public static void main(String[] args) {
                SpringApplication.run(RestServiceForProductApplication.class, args);
       }
}

Implement Caching for Product Retrieval

The application has a ProductServiceImpl class where we will enable caching. The code that implements caching is this:

ProductServiceImpl.java

@CacheConfig(cacheNames = "product")
@Service

public class ProductServiceImpl implements ProductService {

      private ProductRepository productRepository;

      public ProductServiceImpl() {
      }


      @Autowired
      public void setProductRepository(ProductRepository productRepository) {
             this.productRepository = productRepository;
      }


     @Autowired
     public ProductServiceImpl(ProductRepository productRepository) {
             this.productRepository = productRepository;
     }
}

In the preceding code, the class is marked with the @CacheConfig(cacheNames = "product")  annotation.

It is a class-level annotation that provides common cache-related settings. It tells the string where to store the cache for the class.  In the example provided above, “product” is the name of the cache.

Now, let’s add a service method addProduct() for adding products to the database.

@Caching(evict = {@CacheEvict(value = "allproductcache", allEntries = true),
        @CacheEvict(value = "productcache", key = "#product.id")
        })
@Override
public Product addProduct(Product product) {
    return productRepository.save(product);
}

In the preceding code, @Caching annotation is required when we need both @CachePut  and  @CacheEvictat the same time. In other words,  when we want to use multiple annotations of the same type we use this annotation. When you want to remove or evict cache of previously loaded master data you will have to use @CacheEvict  . If you want to remove all entries of the cache then you need to use allEntries = true.

Finally, let’s implement the service method to retrieve all products.

@Cacheable(value = "allproductcache")
@Override
public List<Product> getAllProducts() {

    System.out.println("Data is retrieved from database ");
    return (List<Product>) productRepository.findAll();
}

@Cacheableis a method level annotation. It defines a cache for a method’s return value. You can also add a cache name by using the value attribute. You can also specify a unique key to identify values in the cache.

Now that our implementation is ready let’s test the caching functionality.

Test for Caching

To test the application, I am using Spring Test with JUnit 5 and Mockito.

The test code is this:

@ExtendWith(MockitoExtension.class)
@SpringBootTest
public class ProductServiceTest {
    @Mock
    private ProductRepository productRepository;
    @Autowired
    @InjectMocks
    private ProductServiceImpl productService;
    private Product product1;
    private Product product2;
   

    @BeforeEach
    public void setUp() {
        
        product1 = new Product(1, "Bread", 20);
        product2 = new Product(2, "jam", 140);
        
        }

    @AfterEach
    public void tearDown() {
        product1 = product2 = null;
        
    }

    @Test
    void givenCallToGetAllUsersThenShouldReturnListOfAllProduct() {
        productService.addProduct(product1);
        productService.addProduct(product2);

        productService.getAllProducts();
        productService.getAllProducts();
        productService.getAllProducts();
        productService.getAllProducts();

        
        verify(productRepository, times(1)).findAll();
    }

}

In the test code, we are mocking the product repository. The test case adds to products and makes four calls to retrieve all the products. Without caching this would have involved four calls to the database. But let’s verify that only a single call happens instead of four calls.

This is done in the verify method on Line 38.

Now, let’s run the test.

The output of the test is this.

output for test case

As you can see the test passes.  Because of caching only a single call got made to the repository.

Summary

In this post, you can see that whenever products are added to the database instead of querying the database for retrieving products for each incoming request,  cache comes into play.

Now, consider a scenario where you need to delete a product. The cache has to reflect the change as well. Else, the deleted product will still be present in the cache and returned to users. The same thing will happen when a product gets updated. I will be discussing how to manage such scenarios in Part 2 of this series on caching.

You can find the source code of this post on Github.

For in-depth knowledge of the Spring Framework check my Udemy Best Seller Course Spring Framework 5: Beginner to Guru

About SFG Contributor

Staff writer account for Spring Framework Guru

    You May Also Like

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.