Spring Boot + Spring Data Redis (Remote Directory Server) with HashOperations + Rest API + Example

  • In this tutorial, we will set up the connection with NO-SQL in-memory Remote Database Redis and will be exposing the Rest API for the below operations to the end-user in Spring Boot using @RestController. For Example:
1. /save - insert record in Redis database.
2. /findall - retrieve records from Redis database.
3. /find?id={id} - select a particular record from Redis database.
4. /uppercase?id={id} - update record in Redis to Uppercase.
5. /delete?id={id} - delete a record from Redis database.

High-Level Application Architecture

Here is the high-level application’s architecture with Redis integration.

High Level Application Architecture

Why Redis?

  • Redis is a great choice for implementing a highly available in-memory Cache – “a query that used to take an hour can run in seconds on the cache.”

Redis Role in a Microservices Architecture

We can use Redis in-memory cache/database along with Microservices Architecture as below.

1. Redis as Configuration Server
2. Redis as a Message Broker
3. Redis as a Primary Database in case of multi-db usecases
  • Caching – Redis is a great choice for implementing a highly available in-memory cache to decrease data access latency, increase throughput, and ease the load off your relational or NoSQL database and application
  • Chat, messaging, and queues – Redis supports Pub/Sub with pattern matching and a variety of data structures such as lists, sorted sets, and hashes.
  • Session store – Redis as an in-memory cache data store with high availability and persistence is a popular choice among application developers to store and manage session data for internet-scale applications.
  • Real-time analytics – Redis can be used with streaming solutions such as Apache Kafka and Amazon Kinesis as an in-memory data store to ingest, process, and analyze real-time data with sub-millisecond latency. Redis is an ideal choice for real-time analytics use cases such as social media analytics, ad targeting, personalization, and IoT.

Redis architecture

  • Redis architecture contains two main processes: Redis client and Redis Server.
  • Redis client and server can be in the same computer or in two different computers. Redis server is responsible for storing data in memory.

Redis Advantages

Database Management systems store everything in second storage which makes read and write operations very slow. But Redis stores everything in primary memory (RAM) in form of key and value pairs which is very fast in reading and write of data. Also, In Redis, the key has to be a string but the value can be a string, list, set, sorted set, or hash.

Redis Limitations

Primary memory is limited (much lesser size and more expensive than secondary) therefore Redis cannot store large files or binary data. It can only store those small textual information which needs to be accessed, modified, and inserted at a very fast rate. If we try to write more data than available memory then we will receive errors.

Redis does not provide any mechanism for datastore backup and recovery. Therefore if there is any hard disk crash or any other kind of disaster then all data will be lost. You need to use some third-party server backup and recovery software to workaround it.

If you are using Redis in a replicated environment then there is no need for backup.

How to install Redis on Windows

Follow the steps below:

Step 1: Download .zip or .msi file from Github
Go to https://github.com/microsoftarchive/redis/releases to download .zip or .msi file.
Note: The .zip file will may require you to add the environment variables manually while the .msi file is an installer and will add it automatically.

hashoperations redis spring boot

Technology Stack

Java 8
Maven 3.5.0
Spring Boot 2.5.2
Eclipse
Spring Data Redis 2.5.2
Spring Boot Rest API

Spring Data: Application Setup

Spring Boot: Maven File (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>spring-boot-data-redis</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-boot-data-redis</name>
	<description>Demo project for Spring Data - Redis</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.2</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

spring-boot-data-redis: Application Configurations (application.yaml)

Application configurations file will be looked like as below.

#Application Configurations
server:
 port: 8080
# Applicationn context name
 servlet:
  contextPath: /springbootdataredis

Spring Boot: @SpringBootApplication

package com.the.basic.tech.info.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringDataRedisApplication {

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

Spring Boot: Redis Configurations (RedisConfig.java)

package com.the.basic.tech.info.redis.config;

import java.time.Duration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;

@Configuration
@ComponentScan("com.the.basic.tech.info.redis")
public class RedisConfig {

	@Bean
	JedisConnectionFactory jedisConnectionFactory() {

		RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
		redisStandaloneConfiguration.setHostName("localhost");
		redisStandaloneConfiguration.setPort(6379);
		redisStandaloneConfiguration.setDatabase(0);
		// Note: Uncomment the below line if any password has been set
		// redisStandaloneConfiguration.setPassword(RedisPassword.of("password"));

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

		JedisConnectionFactory jedisConFactory = new JedisConnectionFactory(redisStandaloneConfiguration,
				jedisClientConfiguration.build());

		return jedisConFactory;
	}

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

Spring Boot: @RestController (WebController.java)

Here in this RestController we have exposed/implemented five Rest Interfaces for different operation in Redis.

package com.the.basic.tech.info.redis.controller;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.the.basic.tech.info.redis.model.Customer;
import com.the.basic.tech.info.redis.repo.CustomerRepository;

@RestController
public class WebController {

	@Autowired
	private CustomerRepository customerRepository;

	@RequestMapping("/save")
	public String save() {
		// save a single Customer
		customerRepository.save(new Customer(1, "Jack", "Smith"));
		customerRepository.save(new Customer(2, "Diego", "Peter"));
		customerRepository.save(new Customer(3, "Peter", "John"));
		customerRepository.save(new Customer(4, "Jason", "Bob"));
		customerRepository.save(new Customer(5, "Merry", "kill"));

		return "Save Operation Executed Successfully. Please call findall API.";
	}

	@RequestMapping("/findall")
	public String findAll() {
		String result = "";
		Map<Long, Customer> customers = customerRepository.findAll();

		for (Customer customer : customers.values()) {
			result += customer.toString() + "<br>";
		}

		return result;
	}

	@RequestMapping("/find")
	public String findById(@RequestParam("id") Long id) {
		String result = "";
		result = customerRepository.find(id).toString();
		return result;
	}

	@RequestMapping(value = "/uppercase")
	public String postCustomer(@RequestParam("id") Long id) {
		Customer customer = customerRepository.find(id);
		customer.setFirstName(customer.getFirstName().toUpperCase());
		customer.setLastName(customer.getLastName().toUpperCase());

		customerRepository.update(customer);

		return "Uppercase Operation Executed Successfully. Please call findall API.";
	}

	@RequestMapping("/delete")
	public String deleteById(@RequestParam("id") Long id) {
		customerRepository.delete(id);

		return "Delete Operation Executed Successfully. Please call findall API.";
	}
}

Spring Boot: Model Layer (Customer.java)

package com.the.basic.tech.info.redis.model;

import java.io.Serializable;

public class Customer implements Serializable {

	private static final long serialVersionUID = 1L;

	private long id;
	private String firstName;
	private String lastName;

	protected Customer() {
	}

	public long getId() {
		return id;
	}

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

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Customer(long id, String firstName, String lastName) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
	}

	@Override
	public String toString() {
		return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
	}
}

Spring Boot: Redis Repo Implementation Layer

CustomerRepository interface declaration

package com.the.basic.tech.info.redis.repo;

import java.util.Map;

import com.the.basic.tech.info.redis.model.Customer;

public interface CustomerRepository {

	void save(Customer customer);
	Customer find(Long id);
	Map<Long, Customer> findAll();
	void update(Customer customer);
	void delete(Long id);
}

CustomerRepositoryImpl Java Class with HashOperations as below

package com.the.basic.tech.info.redis.repo;

import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import com.the.basic.tech.info.redis.model.Customer;

@Repository
public class CustomerRepositoryImpl implements CustomerRepository {

	private static final String KEY = "Customer";

	private RedisTemplate<String, Object> redisTemplate;
	private HashOperations<String, Long, Customer> hashOperations;

	@Autowired
	public CustomerRepositoryImpl(RedisTemplate<String, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	@PostConstruct
	private void init() {
		hashOperations = redisTemplate.opsForHash();
	}

	@Override
	public void save(Customer customer) {
		hashOperations.put(KEY, customer.getId(), customer);
	}

	@Override
	public Customer find(Long id) {
		return hashOperations.get(KEY, id);
	}

	@Override
	public Map<Long, Customer> findAll() {
		return hashOperations.entries(KEY);
	}

	@Override
	public void update(Customer customer) {
		hashOperations.put(KEY, customer.getId(), customer);
	}

	@Override
	public void delete(Long id) {
		hashOperations.delete(KEY, id);
	}

}

Application (microservice) Deploy, Run & Test

Run spring-boot-data-redis: mvn spring-boot:run

D:\development\spring-boot-data-redis>mvn spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-boot-data-redis 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.5.2:run (default-cli) > test-compile @ spring-boot-data-redis >>>
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ spring-boot-data-redis ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ spring-boot-data-redis ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ spring-boot-data-redis ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory D:\development\spring-boot-data-redis\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ spring-boot-data-redis ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.5.2:run (default-cli) < test-compile @ spring-boot-data-redis <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.5.2:run (default-cli) @ spring-boot-data-redis ---
[INFO] Attaching agents: []

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-29 21:40:00.034  INFO 24120 --- [           main] c.t.b.t.i.r.SpringDataRedisApplication   : Starting SpringDataRedisApplication using Java 1.8.0_212 on LOCALHOST with PID 24120 (D:\development\spring-boot-data-redis\target\classes started by 172025 in D:\development\spring-boot-data-redis)
2021-06-29 21:40:00.049  INFO 24120 --- [           main] c.t.b.t.i.r.SpringDataRedisApplication   : No active profile set, falling back to default profiles: default
2021-06-29 21:40:01.145  INFO 24120 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-06-29 21:40:01.149  INFO 24120 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-06-29 21:40:01.206  INFO 24120 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 28 ms. Found 0 Redis repository interfaces.
2021-06-29 21:40:02.776  INFO 24120 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-06-29 21:40:02.789  INFO 24120 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-06-29 21:40:02.791  INFO 24120 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-06-29 21:40:02.896  INFO 24120 --- [           main] o.a.c.c.C.[.[.[/springbootdataredis]     : Initializing Spring embedded WebApplicationContext
2021-06-29 21:40:02.897  INFO 24120 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2737 ms
2021-06-29 21:40:03.506  INFO 24120 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '/springbootdataredis'
2021-06-29 21:40:03.525  INFO 24120 --- [           main] c.t.b.t.i.r.SpringDataRedisApplication   : Started SpringDataRedisApplication in 4.45 seconds (JVM running for 5.485)
2021-06-29 21:40:07.281  INFO 24120 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/springbootdataredis]     : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-06-29 21:40:07.282  INFO 24120 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-06-29 21:40:07.289  INFO 24120 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 3 ms

Save Employees Data in Redis – Rest API (http://localhost:8080/springbootdataredis/save)

Fetch Employees Data from Redis – Rest API (http://localhost:8080/springbootdataredis/findall)

Fetch Employee Data from Redis for a specific {id} – Rest API (http://localhost:8080/springbootdataredis/find?id=3)

Update Employee Data in Redis for a specific {id} – Rest API (http://localhost:8080/springbootdataredis/uppercase?id=3)

Now, Fetch Employees Data from Redis – Rest API (http://localhost:8080/springbootdataredis/findall)

Note: You can see updated record in Uppercase for id=3 as below

Delete Employee Data from Redis for a specific {id} – Rest API (http://localhost:8080/springbootdataredis/delete?id=3)

Record will be removed/deleted from Redis Cache.

Now, Fetch Employees Data from Redis – Rest API (http://localhost:8080/springbootdataredis/findall)

Download Source Code (Attached)

Keep learning. Have a great Day 🙂