Author: Brian Sam-Bodden
To configure a
RedisTemplate and learn how to access different operation bundles to read and write data to Redis from a Spring REST controller.
In this lesson, you will learn:
- How to use the
@RedisHashannotation to map domain objects.
- How to use
@Idto provide primary keys to your mapped objects.
- How to use
@Referenceto create referential associations between objects.
- How to create and use Redis Repositories to store domain objects.
If you get stuck:
- The progress made in this lesson is available on the redi2read github repository at https://github.com/redis-developer/redi2read/tree/course/milestone-3
In many applications, whether web applications or RESTful web services, one of the first parts of the domain to be implemented is the user/roles sub-domain. In Redi2Read, Users have one or more Roles, a typical one-to-many association.
In this section, we will embark on the creation of the Redi2Read domain models. We will use Lombok to simplify our POJOs,
and using the Spring Data Redis
@RedisHash and other Spring Data annotations, we will configure our models to be persisted in Redis.
Let’s start by creating the simplest of our domain’s classes, the Role class under
src/main/java/com/redislabs/edu/redi2read/models. Let’s name the file
with the following contents:
We are starting with a class annotated using Lombok’s
@Data annotation, which adds
@Setter, and a
@RequiredArgsContructor, giving us a fully formed Java POJO.
For the Spring Data Repositories to know how to map an instance of Role to a Redis Hash, we need to annotate the class with the @RedisHash annotation.
@RedisHash can take as parameters a timeToLive of type Long, and a String value, which will override the default Redis key prefix (by default, the key prefix is the fully qualified name of the class plus a colon).
Within the class, most of the usual Spring Data annotations are supported. For Role, lets annotate the id field with the @Id annotation. Spring Data Redis will auto-generate and suitable id for the annotated type.
The User model will be used in a registration/signup method. To allow for server-side validation to be performed, we need to add a dependency to the Maven POM for the spring-boot-starter-validation library.
Now we can create the User class with the following contents: package com.redislabs.edu.redi2read.models;
This class is a little more involved, so let’s break it down:
- We have another POJO (
@Data) whose instances can be persisted as Redis Hashes (
- The class is annotated to only add fields to equals/hashcode/toString that are explicitly annotations with
- Once again, we have an auto-generated String Redis Hash key using
- We create a (secondary) index on the email field by annotating the property with
@Indexed. We will learn more about secondary indexes in the next lesson.
javax.validation.constraintsannotations are used to denote fields as being of type
@NotNull-able, and restricting their
- The passwordConfirm field, which will hold the traditional “password confirmation”, is marked as
@RedisHashdoes not attempt to serialize it to the database
- For the
User’s roles, we have a
Roleobjects marked as
@Referenceswhich will cause them to be stored as the id of a given role in the Redis Hash backing a
- Finally at the bottom, we add a utility method to add
Roles to the
User’s Role Set.
Now that we have two models properly annotated, we need associated repositories to perform data operations on said models. A Spring Data Repository is similar to the DAO (Data Access Object) pattern in that they both abstract operations against the underlying data storage. The difference is that repositories further abstract the underlying storage mechanism by focusing on the management of a collection of objects, while DAOs are more SQL/Table centric.
src/main/java/com/redislabs/edu/redi2read/repositories let's create the RoleRepository interface as follows:
Our repository extends
CrudRepository for the
Role class with key of type
String, it provides basic CRUD and finder operations.
Spring Data Redis repositories are highly performant; they avoid using reflection and byte-code generation and instead use programmatic
JDK proxies instances using Spring’s ProxyFactory API. See https://bit.ly/2PshxEI
Let’s test the
RoleRepository by using a
CommandLineRunner implementation. A Spring
CommandLineRunner is an interface that tells the Spring container
that the run method needs to be executed upon startup.
A Spring Boot application can have many
CommandLineRunners; to control the order of their execution, we can further annotate them with the
Create the directory
src/main/java/com/redislabs/edu/redi2read/boot and then add the
CreateRoles class will run on each server start-up or on each live reload (since we are using Spring DevTools).
@Order annotation takes a numeric value indicating the order of execution.
To test the command line runner we have a
System.out.println in the run method that we can watch fly by on the console.
Now that we know the
CreateRoles component runs, let's complete it to work with the
We begin by injecting an instance of
RoleRepository using the
@Autowired annotation. Since we don’t want the roles to be created on each restart of the server,
our logic will only execute when the
RoleRepository has no
If there are no
Roles, then we create the “admin” and “customer” roles using the Lombok builder. Then we save them to Redis using the
RoleRepository save method.
To properly log a message we’ll use the
@Slf4j (Simple Logging Facade for Java) annotation provided by Lombok, which creates a logger instance called log,
with the customary log level logging methods.
On server start we should now see, once, the output of our database seeding.
Let’s use the Redis CLI to explore how the
Roles were stored, let’s use the KEYS command passing the Role fully qualified class name and a wildcard. Resulting in:
The first two values are
Hashes, actual instances of the
Role class. The string after the
: is the primary key of the individual Role. Let’s inspect one of those hashes:
Using the TYPE command returns, as expected that the value under the key is a Redis Hash. We use the HGETALL to “Get All” values in the Hash. The
_class is a metadata field which demarks the class of the object stored in the Hash.
Now let’s inspect the third value in the KEYS list:
The Redis Set under the mapped class name is used to keep the primary keys maintained for a given class.