Getting Started With Spring Data Redis

April 10, 2021 1 Comment Spring Boot Data Redis

1. Introduction

This article covers basic information about Spring Data Redis which allows accessing Redis from Spring applications.

Redis is an in-memory data structure that could be used as a fast database, cache, and message broker. Redis supports several types of data structures such as strings, hashes, lists, sets, bitmaps, geospatial indexes, and streams.

Spring Data provides low-level and high-level abstractions for interacting with Redis. We could use CRUD operations and also complex queries using RedisTemplate.

2. Maven Dependencies

To start using Redis Spring Data we need to add dedicated dependencies into our project.

We can use two different approaches in order to configure Spring Data Redis:

  • first one is to use a common spring-data-redis library with one of Redis client for example jedis:

    <dependencies>
     <dependency>
         <groupId>org.springframework.data</groupId>
         <artifactId>spring-data-redis</artifactId>
         <version>2.4.6</version>
     </dependency>
     <dependency>
         <groupId>redis.clients</groupId>
         <artifactId>jedis</artifactId>
         <version>3.3.0</version>
     </dependency>
    </dependencies>
    
  • the second approach uses Spring Boot Starter dependency for Redis:

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

You could find the latest version of these libraries in our Maven repository:

3. Starting Redis

The easiest way to start a Redis instance is to use an official Docker image:

$ docker run -p 96379:6379 -d redis:6.0 redis-server --requirepass "password"

This command will start secured Redis (you will need to provide a password to connect to it) on port 96379.

4. Redis Configuration

4.1. Spring Boot starter for Redis

When the spring-boot-starter-data-redis is used Spring Boot will automatically configure RedisConnectionFactory we just need to specify a few properties in our application.properties file:

spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=96379
spring.redis.password=password
spring.redis.timeout=60000

Used properties:

  • database - database index used by the connection factory,
  • host - location of Redis,
  • port - the port on which the server is listening,
  • password - password when Redis is secured,
  • timeout - Redis connection timeout.

4.2. Using Java Configuration

In a second approach we will explicitly provide the parameters to connect with Redis:

package com.frontbackend.springboot.config;

import java.time.Duration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
public class RedisConfiguration {

    @Value("${redis.host}")
    private String host;

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

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

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

    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        redisStandaloneConfiguration.setDatabase(database);
        redisStandaloneConfiguration.setPassword(RedisPassword.of(password));

        JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
        jedisClientConfiguration.connectTimeout(Duration.ofSeconds(60));// 60s connection timeout

        return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration.build());
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory());
        return template;
    }
}

This configuration class will be used when spring-data-redis with jedis are used in the project.

5. Spring Data Redis CRUD operations

Let's start with a simple object that will be stored in the Redis database:

package com.frontbackend.springboot.model;

import org.springframework.data.redis.core.RedisHash;

import java.util.Date;

@RedisHash("Session")
public class UserSession {

    private String id;
    private String username;
    private Date loginTime;
    private String browser;

    public UserSession(String id, String username, Date loginTime, String browser) {
        this.id = id;
        this.username = username;
        this.loginTime = loginTime;
        this.browser = browser;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(Date loginTime) {
        this.loginTime = loginTime;
    }

    public String getBrowser() {
        return browser;
    }

    public void setBrowser(String browser) {
        this.browser = browser;
    }

    @Override
    public String toString() {
        return "UserSession{" +
                "id='" + id + '\'' +
                ", username='" + username + '\'' +
                ", loginTime=" + loginTime +
                ", browser='" + browser + '\'' +
                '}';
    }
}

The RedisHash annotations tell Redis to use a hash data structure for our entity object.

Next, we will need a repository to interact with Redis:

package com.frontbackend.springboot.repository;

import org.springframework.data.repository.CrudRepository;

import com.frontbackend.springboot.model.UserSession;
import org.springframework.stereotype.Repository;

@Repository
public interface UserSessionRepository extends CrudRepository<UserSession, String> {
}

5.1. Save new object in Redis

Let's start with saving new UserSession in Redis:

@Test
public void shouldSaveUserSession() {
  UserSession userSession = new UserSession(UUID.randomUUID().toString(), "USERNAME", new Date(), "Chrome");

  UserSession saved = userSessionRepository.save(userSession);

  assertThat(saved).isNotNull();
  assertThat(userSessionRepository.count()).isEqualTo(1);
}

5.2. Get a saved object from Redis using an identifier

We could use findById method to fetch specific object from Redis:

@Test
public void shouldGetSavedUserSession() {
  String id = UUID.randomUUID().toString();
  Date loginTime = new Date();

  UserSession userSession = new UserSession(id, "USERNAME", loginTime, "Chrome");

  userSession.setLoginTime(loginTime);

  userSessionRepository.save(userSession);
  Optional<UserSession> userSessionOptional = userSessionRepository.findById(id);

  assertThat(userSessionOptional).isPresent();

  UserSession saved = userSessionOptional.get();

  assertThat(saved).isNotNull();
  assertThat(saved.getBrowser()).isEqualTo("Chrome");
  assertThat(saved.getUsername()).isEqualTo("USERNAME");
  assertThat(saved.getLoginTime()).isEqualTo(loginTime);
}

5.3. Update existing object

To update object we use the same method that save new instance save(...):

@Test
public void shouldUpdateSavedUserSession() {
   String id = UUID.randomUUID().toString();
   UserSession userSession = new UserSession(id, "USERNAME", new Date(), "Chrome");

   UserSession saved = userSessionRepository.save(userSession);
   saved.setBrowser("IE");

   userSessionRepository.save(saved);

   Optional<UserSession> userSessionOptional = userSessionRepository.findById(id);

   assertThat(userSessionOptional).isPresent();

   UserSession updated = userSessionOptional.get();

   assertThat(updated).isNotNull();
   assertThat(updated.getBrowser()).isEqualTo("IE");
   assertThat(updated.getUsername()).isEqualTo("USERNAME");
}

5.4. Delete existing object

We have several methods to delete objects from Redis:

  • deleteById(...) - delete specified object by the indetifier,
  • deleteAll() - delete all objects related with repository,
  • delete(...) - delete object used as an argument to this method.

Below test will remove specified object by its identifier:

@Test
public void shouldDeleteUserSession() {
  String id = UUID.randomUUID().toString();

  UserSession userSession = new UserSession(id, "USERNAME", new Date(), "Chrome");

  userSessionRepository.save(userSession);
  userSessionRepository.deleteById(id);

  assertThat(userSessionRepository.count()).isEqualTo(0);
}

5.5. Finding objects

We can use findAll() method to retrieve all UserSession objects.

Note that this method returns Iterable interface:

@Test
public void shouldFindAllUserSessionObjects() {
  UserSession userSession1 = new UserSession(UUID.randomUUID().toString(), "USERNAME", new Date(), "Chrome");

  UserSession userSession2 = new UserSession(UUID.randomUUID().toString(), "USERNAME2", new Date(), "IE");

  userSessionRepository.save(userSession1);
  userSessionRepository.save(userSession2);

  Spliterator<UserSession> spliterator = userSessionRepository.findAll().spliterator();

  List<UserSession> all = StreamSupport.stream(spliterator, false).collect(Collectors.toList());

  assertThat(all.size()).isEqualTo(2);
  assertThat(all).extracting("username", "browser").containsOnly(tuple("USERNAME", "Chrome"), tuple("USERNAME2", "IE"));
}

6. Conclusion

In this article, we presented basic information about Spring Data Redis.

As usual, the source code used in this tutorial could be found in a GitHub project.

{{ message }}

{{ 'Comments are closed.' | trans }}