Spring Data Redis Cache – Redis-CLI

In this article, I will provide an example of how to use PostgreSQL as a database and Redis for caching in a Spring Boot project. We will be using Docker Compose to install these tools. First, let’s understand what Redis is, its purpose, and its advantages. After that, we’ll discuss the methods in the provided sample code and their respective functionalities.

What is Redis?

Redis is an open-source, in-memory data storage tool that can function as a database, cache, and message broker. It is known for its high performance, flexibility, and simplicity. Redis is ideal for use cases such as caching, real-time data processing, session management, and sorting. It is especially useful for fast and temporary storage needs.

Redis’ Cache

1.It is a key-value-based database. Each key represents a value.

2.Redis supports simple CRUD (Create, Read, Update, Delete) operations. It does not focus on complex relational queries and has limited query capabilities.

3.Redis stores the data in RAM, so instead of reading from the database each time, if the data is already in the cache, it can be read directly from the cache. This reduces the load on the database.

Points to be considered

1.It is important to determine factors such as what data will be kept in the cache and for how long.

2.When data in the database is updated or changed, it is important to clear the relevant data in the cache. Otherwise, old or incorrect data may remain in the cache.

3.Cache size and content should be kept under control. It is important to prevent unnecessary data accumulation in the cache.

Some tools you can use to view and manage Redis cache

Redis CLI (Command Line Interface):

This command-line tool allows you to send commands to Redis servers, query values in the database, and generally interact with Redis.

RedisInsight:

This tool has a graphical interface used to visually explore, manage, and monitor Redis.

Redis Desktop Manager:

This tool is a desktop application used to manage and visually explore Redis.

We will be using Redis-CLI in our project. Now let’s move on to our project.

Step 1: Create a project using Spring Initializr.

Step 2: To setup the required services, create a docker-compose.yaml file and run it.

version: '3.8'

services:
  db:
    image: postgres
    restart: always
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASS}
      - POSTGRES_USER=postgres
      - POSTGRES_DB=RedisExample
    ports:
      - "5432:5432"
    networks:
      - redis

  redis:
    image: redis:latest
    environment:
      - REDIS_PASSWORD=secret
      - REDIS_DB=1 #default 0 - 16 databases, indexed 0–15.
    ports:
      - "6379:6379"
    networks:
      - redis

networks:
  redis:
    driver: bridge
docker-compose up

Step 3: To display and manage Redis in the next stage, run Redis-CLI with the following command.

docker exec -it <container_name> redis-cli 

container_name = redis-redis-1

127.0.0.1:6379> PING
PONG

If the server is running and the connection is successful, the PING command will return a PONG response.

Step 4: Add the required dependencies to the project.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    //redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.2.2'
    //postgreSql
    implementation 'org.postgresql:postgresql:42.6.0'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.2.2'
}

Use the spring-boot-starter-data-redis dependency for Redis integration with Spring Boot.

Step 5: application.yaml

server:
  port: 9090

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    username: postgres
    password: ${POSTGRES_PASS}
    url: jdbc:postgresql://localhost:5432/RedisExample
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  cache:
    type: redis
    host: localhost
    password: secret
    port: 6379

Step 6: Create a configuration class for Redis.

@Configuration
@EnableCaching
public class RedisConfig {

    @Value("${spring.cache.host}")
    private String hostName;

    @Value("${spring.cache.port}")
    private int port;

    @Value("${spring.cache.password}")
    private String password;

    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory(){
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(hostName,port);
        redisStandaloneConfiguration.setPassword(password);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }
}

The @EnableCaching annotation is used to enable caching in a Spring Boot application.

Jedis and Lettuce are Java-based Redis clients, and both are used to connect to the Redis database, perform queries, and process responses. Lettuce is an asynchronous and performance-oriented Redis client that can be used by Spring to interact with Redis. Lettuce is used in this project.

LettuceConnectionFactory uses a Redis client called Lettuce to communicate with the Redis datastore and handles connection establishment operations.
RedisStandaloneConfiguration is a class in the Spring Data Redis library that represents the connection configuration required to connect to the Redis server.
The RedisStandaloneConfiguration class contains the host, port, and optionally password information of the Redis server. Using this information, a LettuceConnectionFactory can be created and connected to the Redis server with this connection factory.

Step 7: Entity

@Entity
@Table(name = "users")
public class User implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;

    public User() {
    }
    
    //getter and setter

Step 8: Repository

@Repository
public interface IUserRepository extends JpaRepository<User,Long> {
    Optional<User> findByUsername(String username);
}

Step 9: Service

@Service
public class UserService {

    private final IUserRepository userRepository;

    public UserService(IUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public String saveUser(String username) {
        User user = new User();
        user.setUsername(username);
        userRepository.save(user);
        return "registration successful";
    }

    @Cacheable(value = "user", key = "#username")
    public Optional<User> findByUsername(String username) {
        Optional<User> user = userRepository.findByUsername(username);
        if (user.isPresent()) {
            return user;
        }
        throw new RuntimeException("user does not exist");
    }

    @CacheEvict(value = "user" , allEntries = true)
    public String clearCache(){
        return "cache cleared";
    }
}

Spring Cache annotations, such as @Cacheable, @CachePut, and @CacheEvict, provide a way to cache methods and manage the cache.
The @Cacheable annotation is used to store in the cache the calculations or operations that the method performs each time it is called, and to use the value in the cache when it is called again with the same input.

value: This specifies the cache name. In other words, data cached is stored in the cache under this name. Methods that use the same value share the same cache.

key: This specifies the key of the data in the cache. The #username statement creates the cache key using the value of username, one of the method’s parameters.

If the method’s result is already in the cache, it is retrieved from the cache instead of executing the method. If it’s not in the cache, the method is executed, and the result is added to the cache for future calls.

When using the @Cacheable annotation, it’s important to ensure that the data in the cache is up to date and to clear the cache when needed. If the data is updated frequently or needs to be updated every time there is an update, you can use the @CacheEvict or @CachePut annotations.

When we want to update a value in the cache, we can use the @CachePut annotation.
The cache can be cleared using the @CacheEvict annotation, as shown above code example.

Step 10: Controller

@RestController
@RequestMapping("api/v1/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("save/{username}")
    public ResponseEntity<String> saveUser(@PathVariable String username) {
        return ResponseEntity.ok(userService.saveUser(username));
    }

    @GetMapping("find-by-username/{username}")
    public ResponseEntity<Optional<User>> findByUsername(@PathVariable String username) {
        return ResponseEntity.ok(userService.findByUsername(username));
    }

    @GetMapping("clear-cache")
    public ResponseEntity<String> clearCache() {
        return ResponseEntity.ok(userService.clearCache());
    }
}

In the service class, we marked the findByUsername method with the @Cacheable(value = “user”, key = “#username”) annotation.

After registering a user, a database query is executed when making a GET request through Postman to http://localhost:9090/api/v1/users/find-by-username/:username, and the result is cached. When the same request is made again, the data will be retrieved from the cache.

Review all the keys via Redis-CLI.

127.0.0.1:6379> KEYS *
1) "user::mishakp"
2) "user::rbedburys"
3) "user::brizonf"

Get specific key.

127.0.0.1:6379> GET user::brizonf

If we send a request to http://localhost:9090/api/v1/users/clear-cache to clear the cache, the cache will be cleared.

We can also clear the cache by running the FLUSHALL command directly via Redis-CLI.

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> KEYS *
(empty array)

You can review the project’s code by following this link and learn how to use the redis in your projects by following these steps.

References

https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis
https://spring.io/projects/spring-data-redis
https://redis.io/docs/install/install-stack/docker
https://hub.docker.com/_/postgres
https://hub.docker.com/_/redis
https://redis.io/commands
https://redis.io/docs/connect/cli
https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/cache.html
https://docs.docker.com/compose/compose-file/compose-versioning
https://docs.spring.io/spring-data/redis/docs/3.1.6/reference/html/#redis
https://redis.io/resources/tools
https://docs.spring.io/spring-data/redis/reference/redis.html