🏠 ❯ Caching ❯ Spring Cache For Better application performance

Spring Cache For Better application performance

Let’s learn how to implement cache mechanisms in Spring Boot using @Cacheable annotation with an example.

Introduction to Spring Cache abstraction

Caching is a concept that improves response time by storing copies of most frequently used data on a temporary but fast storage. In this article, We will see how to enable caching for a spring boot application with an example.

Imagine that you have a shopping website that has a number of product offerings. In ideal cases, There may be a lot of traffic for new orders/purchases. However, The information about the product details barely change. In this case, it would make sense not to load the price and product information from the database every single time.

Cache support in Spring Boot

First, you need to add @EnableCaching annotation to your Spring Boot application. When added, This annotation creates an in-memory cache implementation where you can store return values of methods.

@EnableCaching
@SpringBootApplication
public class SpringBootCacheExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootCacheExampleApplication.class, args);
    }
}Code language: PHP (php)

At this point, Spring Boot auto configuration will configure an in-memory cache manager.

Service without caching

As you have enabled caching support, You can start marking your methods for caching. For example, The getItem() methods here will take 10 seconds to complete. Because of the sleep, This method will take more time to complete. To do that, add Spring Boot @Cacheable with values as shown below.

@Service
public class ItemService {

    private final ItemRepository itemRepository;

    public ItemService(ItemRepository itemRepository) {
        this.itemRepository = itemRepository;
    }

    public List<Item> getItems() {
        return itemRepository.findAll();
    }

    public Item getItem(Integer id) {
        try {
              Thread.<em>sleep</em>(10000);
        } catch (InterruptedException e) {
               e.printStackTrace();
        }
        return itemRepository.findById(id).orElseThrow(RuntimeException::new);
    }

    public Item createItem(Item item) {
        return itemRepository.save(item);
    }

    public Item updateItem(Integer id, Item request) {
        Item item = getItem(id);
        item.setPrice(request.getPrice());
        item.setProductName(request.getProductName());
        return itemRepository.save(item);
    }

}Code language: PHP (php)

In this case, If we were able to cache the results by id parameter, We can improve the response time.

@Cacheable service

Take a look at the following code. Note that We marked the getItem method as cacheable. When spring boot calls this method, the return value(Item) is cached by the cache manager. With this approach, Spring boot will load the values from cache for the subsequent calls.

@Service
public class ItemService {
    private static final Logger logger = LoggerFactory.getLogger(ItemService.class);
    private final ItemRepository itemRepository;

    public ItemService(ItemRepository itemRepository) {
        this.itemRepository = itemRepository;
    }

    public List<Item> items() {
        return itemRepository.findAll();
    }

    @Cacheable(value = "items", key = "#id")
    public Item getItem(Integer id) {
        try {
              Thread.<em>sleep</em>(10000);
        } catch (InterruptedException e) {
               e.printStackTrace();
        }
        Item item = itemRepository.findById(id).orElseThrow(RuntimeException::new);
        logger.info("Loading data from DB {}", item);
        return item;
    }

    public Item createItem(Item item) {
        return itemRepository.save(item);
    }

    @CacheEvict(value = "items", key = "#id")
    public Item updateItem(Integer id, Item request) {
        Item item = getItem(id);
        item.setPrice(request.getPrice());
        item.setProductName(request.getProductName());
        return itemRepository.save(item);
    }
}Code language: HTML, XML (xml)

Here is the explanation. In the @Cacheable(value = "items", key = "#{id}") annotation, items is the cache name. Every time the getItem method is called, the returned Item object is stored in the items cache. Subsequently, Spring Boot will return the value from cache for future requests. For example, Let’s create load some dummy values and test this out.

Spring Cache in Action

first I have created few records for testing using CommandLineRunner. You don’t have to do this.

idproductNameprice
1Shirt Small28.99
2Pants Large21.99

Here, hitting the API at http://localhost:8080/items/2 for item with id 2 will return a response with 10 second delay.

without spring boot @cacheable annotation
without caching

Further, In the logs you will also see that the application loaded this value from database. This explains that the method was executed and the record was indeed loaded from database.

.ItemService:Loading data from DB Item{id=2,productName='Pants Large',price=21.99}Code language: JavaScript (javascript)

If you try hitting the same API one more time, You will observe that the same log is not printed anymore. This behaviour explains that the control of the method call didn’t pick the value from the database. You can also see the huge reduction in the response time as well.

Spring Cache in action
with @Cacheable in action

Evicting Cached values using @CacheEvict

The problem with this approach is that, If I update the price of 2, the cache will still hold the old value. Right? In order to avoid situations like these, you need to instruct spring Boot when to clear the cached values. In this case, you need to let spring Boot know on each updateItem() method call.

This is where the @CacheEvict annotation comes in to the picture. Notice that both eviction and storing of the cache are happening based on the id of the object that is being cached.

    @CacheEvict(value = "items", key = "#id")
    public Item updateItem(Integer id, Item request) {
        Item item = getItem(id);
        item.setPrice(request.getPrice());
        item.setProductName(request.getProductName());
        return itemRepository.save(item);
    }Code language: PHP (php)

Caching repository methods

@Cacheable Annotation can be applied to @Service,@Controller and even @Repository. As long as you can autowire a component, you can make their methods cacheable. Here is an example of caching applied on Spring Boot JPA repository.

@Repository
public interface ItemRepository extends JpaRepository<Item, Integer> {

    @Cacheable(value = "items",key = "#id")
    Item findById(Integer id);
    
}Code language: PHP (php)

Conclusion

To summarize, We learned how we can implement caching in spring boot with a simple example. Checkout our REDIS based caching for more on this topic.

The project for this tutorial is available in this github repository.

Similar Posts

Leave a Reply

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