Spring Data Redis in the cloud

Otavio Santana
Developer Relations
20 Sep 2019

Redis is a software project that implements data structure servers. Open source and networked, Redis stores keys with optional durability. It’s also the most popular key-value database, supporting data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, and much more. Redis works with an in-memory dataset. This post will introduce how to use Redis smoothly with Spring Data Redis, which is part of the larger Spring Data family. It provides easy configuration and access to Redis from Spring applications and offers both low-level and high-level abstractions for interacting with the store, freeing the user from concerns about infrastructure.

Redis is usually used for caching data stored in a relational database. In the current sample, it will be treated as a primary database—just for simplification.

Show me the code

The application will run a smooth CRUD of students with Restful, Redis, Spring, and Java 8. The first step is to create the pom.xml where the Java dependencies are, such as Java Reader Config, Spring Boot, and Spring Data Redis.

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>sh.platform.start</groupId>
	<artifactId>spring-boot-maven-redis</artifactId>
	<version>0.0.1</version>

	<properties>
    	<java.version>1.8</java.version>
	</properties>

	<parent>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-parent</artifactId>
    	<version>2.1.5.RELEASE</version>
	</parent>

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

    	<dependency>
        	<groupId>redis.clients</groupId>
        	<artifactId>jedis</artifactId>
        	<version>2.9.0</version>
    	</dependency>
    	<dependency>
        	<groupId>sh.platform</groupId>
        	<artifactId>config</artifactId>
        	<version>2.2.2</version>
    	</dependency>
	</dependencies>

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

	<repositories>
    	<repository>
        	<id>oss.sonatype.org-snapshot</id>
        	<url>http://oss.sonatype.org/content/repositories/snapshots</url>
    	</repository>
	</repositories>
</project>

Let’s start by easily configuring the bean definitions by using the Java Config Reader:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import sh.platform.config.Config;
import sh.platform.config.MySQL;
import sh.platform.config.Redis;
import sh.platform.config.RedisSpring;

import javax.sql.DataSource;

@Configuration
public class JedisConfig {

	@Bean
	public JedisConnectionFactory getDataSource() {
    	Config config = new Config();
    	RedisSpring redis = config.getCredential("redis", RedisSpring::new);
    	return redis.get();
	}
}

The entity here isn’t complicated; the Student entity only has three attributes: id, name, and year. To allow validation, we’re using bean validation. To be clear, we’re talking about the key-value database. Therefore, we can find the entity from the id attribute.

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

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;

@RedisHash("Student")
public class Student implements Serializable {

	@Id
	@NotBlank
	private String id;

	@NotBlank
	private String name;

	@Min(2018)
	private int year;

//getter and setter
}

The Spring Repository feature is still valid in a key-value database; it allows easy integration between Java and the database.

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends PagingAndSortingRepository<Student, String> {
}

After creating the model and the repository, the last step in the Java application is the controller with REST. The @RequestMapping annotation provides “routing” information. It tells Spring that any HTTP request with the / path should be mapped to the home method. The @RestController annotation tells Spring to render the resulting string directly back to the caller.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;


@RestController
@RequestMapping("students")
public class StudentController {

	@Autowired
	private StudentRepository repository;

	@PostMapping
	@ResponseStatus(code = HttpStatus.CREATED)
	public String save(@RequestBody @Valid Student student) {
    	repository.save(student);
    	return "Saved- " + student.getName();
	}

	@GetMapping(value = "/{id}", produces = "application/json")
	public Student get(@PathVariable("id") String id) {
    	return repository.findById(id).orElseThrow(() -> new RuntimeException("Not found"));
	}

	@GetMapping(produces = "application/json")
	public Iterable<Student> get() {
    	return repository.findAll();
	}


	@PutMapping(value = "/{id}", produces = "application/json")
	public Student update(@PathVariable("id") String id, @RequestBody @Valid Student student) {
    	repository.save(student);
    	return student;
	}

	@DeleteMapping(value = "/{id}", produces = "application/json")
	public Student delete(@PathVariable("id") String id) {
    	Student student = repository.findById(id).orElseThrow(() -> new RuntimeException("Not found"));
    	repository.deleteById(id);
    	return student;
	}
}

The final part of our application is a startup. It’s a standard method that follows the Java convention for an application entry point. SpringApplication bootstraps the app, starting Spring on a Tomcat web server.

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

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

}

Platform.sh structure

The Platform.sh structure is still the same as in the first Java post, and it won’t impact your application in any way. Indeed, Platform.sh is a PaaS that facilitates integration where, through infrastructure by code, you, as a user, can demand services that your application/business needs.

Once we’re using Redis, we’ll update the services file to require Redis services and the application file to receive a credential to connect to the most popular key-value NoSQL database.

The .services.yaml file:

# The services of the project.
#
# Each service listed will be deployed
# to power your Platform.sh project.

redis:
  type: redis:5.0
  size: S

The .platform.app.yaml file:

# This file describes an application. You can have multiple applications
# in the same project.
#
# See https://docs.platform.sh/user_guide/reference/platform-app-yaml.html

# The name of this app. Must be unique within a project.
name: app

# The runtime the application uses.
type: "java:8"

disk: 1024

# The hooks executed at various points in the lifecycle of the application.
hooks:
  build: mvn clean install


# The relationships of the application with services or other applications.
#
# The left-hand side is the name of the relationship as it will be exposed
# to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand
# side is in the form `<service name>:<endpoint name>`.
relationships:
  redis: 'redis:redis'

# The configuration of app when it is exposed to the web.
web:
  commands:
    	start:  java -jar target/spring-boot-maven-redis.jar --server.port=$PORT

The application is now ready, so it’s time to move it to the cloud with Platform.sh using the following steps:

  • Create a new free trial account.
  • Sign up as a new user and set a password, or login using a current GitHub, Bitbucket, or Google account. If you use a third-party login, you’ll be able to set a password for your Platform.sh account later.
  • Select the region of the world where your site should live.
  • Select the blank template.

After this wizard, Platform.sh will provision the whole infrastructure to you, and will offer a Git remote repository. Before access, remember to set the SSH keys. The Platform.sh Git-driven infrastructure means it will automatically manage everything your application needs to push it to the master remote repository. You only need to write your code—including a few YAML files that specify your desired infrastructure—then commit it to Git, and push.

git remote add platform <platform.sh@gitrepository>
git commit -m "Initial project"
git push -u platform master

The code pushed will create the Java application, a Redis instance, and, when it’s done, will return an IP address to the service. Let’s test the application. To test a REST application, an HTTP client is OK.

curl -X POST -k -i 'https://<service.ip>/students' --data '{"id": "poliana","name": "Poliana Santana","year": 2019}'
curl -X POST -k -i 'https://<service.ip>/students' --data '{"id": "otavio","name": "Otavio Santana","year": 2020}'
curl -X POST -k -i 'https://<service.ip>/students' --data '{"id": "ada","name": "Ada Santana","year": 2021}'
curl https://<service.ip>/students
#result output here

In this post, we created an integration with Redis, the most famous key-value NoSQL database. And we’ve seen how natural it is to work with both Redid and Spring Data. Redis is a NoSQL database in-memory, which means writer/reader operations are faster.