Spring Boot Redis Cache example

In this tutorial, I will show you how to add Redis Cache into your Spring Boot Application (CRUD example) using spring-boot-starter-data-redis and Lettuce Connector.

You can also apply this tutorial on following Projects:
Spring Boot 3 Rest API example
Spring Boot Rest API with H2
Spring Boot Rest API with MySQL
Spring Boot Rest API with PostgreSQL
Spring Boot Rest API with MongoDB
Spring Boot Rest API with SQL Server
Spring Boot Rest API with Cassandra
Spring Boot Rest API with Oracle

More Practice:
Spring @CacheEvict example
Spring Boot WebFlux Rest API example
Spring Boot Security and JWT tutorial with example
Spring Boot @ControllerAdvice & @ExceptionHandler example
@RestControllerAdvice example in Spring Boot
Spring Boot Unit Test for Rest Controller
– Documentation: Spring Boot Swagger 3 example
Spring Boot + GraphQL example


Why Redis Cache?

When a service experiences a high volume of requests and the requested data remains static, a caching solution can significantly reduce response times and lighten the load on the service.

Cache is a type of auxiliary memory that is specifically designed to hold data and instructions that are frequently accessed by applications or websites. Its fast and easy accessibility makes it a valuable tool in helping these applications and websites work faster.

There are several types of caching, including:

  • Memory Cache: Primary Cache L1, Secondary Cache L2, Main Memory L3 Cache
  • Web Cache: Site Cache, Browser Cache, Micro Cache, Server Cache
  • Application/Software Cache
  • Data Caching
  • Application/Output Cache
  • Distributed Caching

For more details, please visit: What is a Cache? 6 Types You Need to Know

We will build the Spring Boot Redis Cache example for Data Caching purpose.

Spring Boot Redis Cache example Overview

Assume that we’ve already had a Spring Boot Application that exposes Rest APIs for a Tutorial application:

Methods Urls Actions
POST /api/tutorials create new Tutorial
GET /api/tutorials retrieve all Tutorials
GET /api/tutorials/:id retrieve a Tutorial by :id
PUT /api/tutorials/:id update a Tutorial by :id
DELETE /api/tutorials/:id delete a Tutorial by :id
DELETE /api/tutorials delete all Tutorials
GET /api/tutorials/published find all published Tutorials
GET /api/tutorials?title=[keyword] find all Tutorials which title contains keyword

You can find how to implement this Rest API server in one of following tutorials (with Github):
Spring Boot 3 Rest API example
Spring Boot Rest API with H2
Spring Boot Rest API with MySQL
Spring Boot Rest API with PostgreSQL
Spring Boot Rest API with MongoDB
Spring Boot Rest API with SQL Server
Spring Boot Rest API with Cassandra
Spring Boot Rest API with Oracle

We will add Redis Cache for the Spring Boot CRUD example above by storing the query result (GET requests) with Redis.
That means, the response for following queries will be cached:

Methods Urls Actions
GET /api/tutorials retrieve all Tutorials
GET /api/tutorials/:id retrieve a Tutorial by :id
GET /api/tutorials/published find all published Tutorials
GET /api/tutorials?title=[keyword] find all Tutorials which title contains keyword

What we need is to add config package and a service for caching abstraction.

spring-boot-redis-cache-example-project

Install Redis

Follow this instruction for setup Redis on your Machine.

Import Redis into Spring Boot

To use Redis Cache in your Maven project, you need to add the spring-boot-starter-data-redis dependency to your project’s pom.xml file:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

To use Redis Cache in your Gradle project, you need to add the spring-boot-starter-data-redis dependency to your project’s build.gradle file:

implementation 'org.springframework.boot:spring-boot-starter-data-redis:[version]'

This will support Java Redis client to interact with Redis server. In this tutorial, we use Lettuce Connector along with Spring Data.

Configure Redis connection

We configure the credentials to connect to the Redis instance from the Spring Boot app in application.properties.

redis.host=localhost
redis.port=6379

Then we continue to configure our Spring Boot Redis Lettuce by creating a new Lettuce connection factory.

config/RedisConfig.java

import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

@Configuration
public class RedisConfig {
  @Value("${redis.host}")
  private String redisHost;

  @Value("${redis.port}")
  private int redisPort;

  @Bean
  public LettuceConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort);

    return new LettuceConnectionFactory(configuration);
  }
}

RedisCacheManager in Spring Boot

With the org.springframework.data.redis.cache package, Spring Redis offers a built-in implementation for the Spring cache abstraction. The beauty of the caching abstraction is that it allows for the use of multiple caching solutions with minimal changes to your codebase.

It’s important to note that the caching service is merely an abstraction and not a cache implementation, meaning that it still requires an actual storage solution to store the cached data. This concept is realized through the use of the org.springframework.cache.Cache and org.springframework.cache.CacheManager interfaces.

Add RedisCacheManager to above configuration to use Redis as a backing implementation.

config/RedisConfig.java

import org.springframework.data.redis.cache.RedisCacheManager;

@Configuration
public class RedisConfig {

  // ...

  @Bean
  public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    return RedisCacheManager.create(connectionFactory);
  }
}

Create Service for Redis Cache abstraction

To enable caching for specific methods, we will use @Cacheable annotation, which designates methods as cacheable. This means that the result of the method is stored in the cache, so that subsequent invocations with the same arguments can return the cached value without invoking the method itself. The annotation simply requires the name of the cache associated with the method, as shown in the following example.

service/TutorialService.java

@Service
public class TutorialService {
  @Autowired
  TutorialRepository tutorialRepository;

  @Cacheable("tutorials")
  public List<Tutorial> findAll() {
    doLongRunningTask();

    return tutorialRepository.findAll();
  }

  @Cacheable("tutorials")
  public List<Tutorial> findByTitleContaining(String title) {
    doLongRunningTask();

    return tutorialRepository.findByTitleContaining(title);
  }

  @Cacheable("tutorial")
  public Optional<Tutorial> findById(long id) {
    doLongRunningTask();

    return tutorialRepository.findById(id);
  }

  @Cacheable("published_tutorials")
  public List<Tutorial> findByPublished(boolean isPublished) {
    doLongRunningTask();

    return tutorialRepository.findByPublished(isPublished);
  }

  // other methods...

  private void doLongRunningTask() {
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

The method is annotated with @Cacheable("tutorials") or @Cacheable(value="tutorials"), which tells Spring to cache the result of this method in a cache named “tutorials”. The value (alias for cacheNames) is name of the cache used to store the results of method invocations.

When the findAll() method is called, it is associated with the 'tutorials' cache name, and the cache is checked to see if the method has already been executed, preventing it from being executed again. While typically only a single cache is specified, the annotation enables multiple names to be supplied, allowing for the use of multiple caches. In this situation, each of the caches is evaluated before the method is executed, and if any of them contains the desired value, it is returned.

Enable Caching Annotations

To enable caching annotations, we use the annotation @EnableCaching annotation:

service/TutorialService.java

@Service
@EnableCaching
public class TutorialService {
  
}

Customize Redis Cache Configuration

RedisCacheManager behavior can be customized with RedisCacheManagerBuilder.

The RedisCache created via RedisCacheManager is governed by the RedisCacheConfiguration, which enables you to specify key expiration times (TTL), RedisSerializer implementations to convert between the binary storage format and other formats. The example below illustrates how this can be achieved:

config/RedisConfig.java

@Configuration
public class RedisConfig {

  // ...

  @Bean
  public RedisCacheManager cacheManager() {
    RedisCacheConfiguration cacheConfig = myDefaultCacheConfig(Duration.ofMinutes(10)).disableCachingNullValues();

    return RedisCacheManager.builder(redisConnectionFactory())
        .cacheDefaults(cacheConfig)
        .withCacheConfiguration("tutorials", myDefaultCacheConfig(Duration.ofMinutes(5)))
        .withCacheConfiguration("tutorial", myDefaultCacheConfig(Duration.ofMinutes(1)))
        .build();
  }

  private RedisCacheConfiguration myDefaultCacheConfig(Duration duration) {
    return RedisCacheConfiguration
        .defaultCacheConfig()
        .entryTtl(duration)
        .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
  }
}

Run and Check

– Run Redis first.
– Run Spring Boot application with Maven command: mvn spring-boot:run.

Create some Tutorials – POST http://localhost:8080/api/tutorials.

spring-boot-redis-cache-example-create

This is the Database table after that:

spring-boot-redis-cache-example-database

Let’s check our Spring Boot Redis Cache example.

In the first time we run GET http://localhost:8080/api/tutorials, you can see that it takes more than 3 seconds.

spring-boot-redis-cache-example-1

We call the method again, Spring will check if the result is already present in the cache, and return it from the cache instead of executing the method again. Now the response time is only 16 milliseconds.

spring-boot-redis-cache-example-hit-1

We continue to make another query: search by title:
GET http://localhost:8080/api/tutorials?title=edi.

spring-boot-redis-cache-example-2

And the second request causes the cache hit.

spring-boot-redis-cache-example-hit-2

It’s similar for the next example when we retrieve a specific Tutorial object.
GET http://localhost:8080/api/tutorials/3.

spring-boot-redis-cache-example-3

Cache HIT for newer request:

spring-boot-redis-cache-example-hit-3

If you continue to query other Tutorial objects (id=1 and id=5 for example). Redis Cache will store them.

Check the Redis Cache using Redis-CLI with KEYS * command:

spring-boot-redis-cache-example

Use GET [key] command for checking the value:

spring-boot-redis-cache-example-cachenames

Conclusion

Today you’ve known the reason why we need Caching, then added Redis Cache into our Spring Boot application for CRUD example using spring-boot-starter-data-redis, we also configured Redis Lettuce connector for Spring Boot project.

In the tutorial, we use @Cacheable annotation to tell Spring to cache the result of a method in a cache.

You also need to know way to evict cache with the tutorial:
Spring @CacheEvict example

Source Code

The complete source code for this tutorial is on Github.

You can also apply the code easily on following Projects:
Spring Boot 3 Rest API example
Spring Boot Rest API with H2
Spring Boot Rest API with MySQL
Spring Boot Rest API with PostgreSQL
Spring Boot Rest API with MongoDB
Spring Boot Rest API with SQL Server
Spring Boot Rest API with Cassandra
Spring Boot Rest API with Oracle

Further Reading

Fullstack CRUD App:
Spring Boot Thymeleaf example
Vue + Spring Boot example
Angular 8 + Spring Boot example
Angular 10 + Spring Boot example
Angular 11 + Spring Boot example
Angular 12 + Spring Boot example
Angular 13 + Spring Boot example
Angular 14 + Spring Boot example
Angular 15 + Spring Boot example
Angular 16 + Spring Boot example
React + Spring Boot example

More Practice:
Spring Boot WebFlux Rest API example
Secure Spring Boot with Spring Security & JWT Authentication
Spring Boot Rest XML example – Web service with XML Response
Spring Boot + GraphQL example
Spring Boot File upload example
Spring Boot Pagination and Sorting example
– Documentation: Spring Boot Swagger 3 example