8 min read

(For more resources related on Spring, see here.)

Designing a Redis data model

The most important rules of designing a Redis data model are: Redis does not support ad hoc queries and it does not support relations in the same way than relational databases. Thus, designing a Redis data model is a total different ballgame than designing the data model of a relational database. The basic guidelines of a Redis data model design are given as follows:

  • Instead of simply modeling the information stored in our data model, we have to also think how we want to search information from it. This often leads to a situation where we have to duplicate data in order to fulfill the requirements given to us. Don’t be afraid to do this.
  • We should not concentrate on normalizing our data model. Instead, we should combine the data that we need to handle as an unit into an aggregate.
  • Since Redis does not support relations, we have to design and implement these relations by using the supported data structures. This means that we have to maintain these relations manually when they are changed. Because this might require a lot of effort and code, it could be wise to simply duplicate the information instead of using relations.
  • It is always wise to spend a moment to verify that we are using the correct tool for the job.

NoSQL Distilled, by Martin Fowler contains explanations of different NoSQL databases and their use cases, and can be found at http://martinfowler.com/books/nosql.html.

Redis supports multiple data structures. However, one question remained unanswered: which data structure should we use for our data? This question is addressed in the following table:

Data type

Description

String

A string is good choice for storing information that is already converted to a textual form. For instance, if we want to store HTML, JSON, or XML, a string should be our weapon of choice.

List

A list is a good choice if we will access it only near the start or end. This means that we should use it for representing queues or stacks.

Set

We should use a set if we need to get the size of a collection or check if a certain item belongs to it. Also, if we want to represent relations, a set is a good choice (for example, “who are John’s friends?”).

Sorted set

Sorted sets should be used in the same situations as sets when the ordering of items is important to us.

Hash

A hash is a perfect data structure for representing complex objects.

Key components

Spring Data Redis provides certain components that are the cornerstones of each application that uses it. This section provides a brief introduction to the components that we will later use to implement our example applications.

Atomic counters

Atomic counters are for Redis what sequences are for relational databases. Atomic counters guarantee that the value received by a client is unique. This makes these counters a perfect tool for creating unique IDs to our data that is stored in Redis. At the moment, Spring Data Redis offers two atomic counters: RedisAtomicInteger and RedisAtomicLong . These classes provide atomic counter operations for integers and longs.

RedisTemplate

The RedisTemplate<K,V> class is the central component of Spring Data Redis. It provides methods that we can use to communicate with a Redis instance. This class requires that two type parameters are given during its instantiation: the type of used Redis key and the type of the Redis value.

Operations

The RedisTemplate class provides two kinds of operations that we can use to store, fetch, and remove data from our Redis instance:

  1. Operations that require that the key and the value are given every time an operation is performed. These operations are handy when we have to execute a single operation by using a key and a value.
  2. Operations that are bound to a specific key that is given only once. We should use this approach when we have to perform multiple operations by using the same key.

The methods that require that a key and value is given every time an operation is performed are described in following list:

  • HashOperations<K,HK,HV> opsForHash(): This method returns the operations that are performed on hashes
  • ListOperations<K,V> opsForList(): This method returns the operations performed on lists
  • SetOperations<K,V> opsForSet(): This method returns the operations performed on sets
  • ValueOperations<K,V> opsForValue(): This method returns the operations performed on simple values
  • ZSetOperations<K,HK,HV> opsForZSet(): This method returns the operations performed on sorted sets

The methods of the RedisTemplate class that allow us to execute multiple operations by using the same key are described in following list:

  • BoundHashOperarations<K,HK,HV> boundHashOps(K key): This method returns hash operations that are bound to the key given as a parameter
  • BoundListOperations<K,V> boundListOps(K key): This method returns list operations bound to the key given as a parameter
  • BoundSetOperations<K,V> boundSetOps(K key):: This method returns set operations, which are bound to the given key
  • BoundValueOperations<K,V> boundValueOps(K key): This method returns operations performed to simple values that are bound to the given key
  • BoundZSetOperations<K,V> boundZSetOps(K key): This method returns operations performed on sorted sets that are bound to the key that is given as a parameter

The differences between these operations become clear to us when we start building our example applications.

Serializers

Because the data is stored in Redis as bytes, we need a method for converting our data to bytes and vice versa. Spring Data Redis provides an interface called RedisSerializer<T>, which is used in the serialization process. This interface has one type parameter that describes the type of the serialized object. Spring Data Redis provides several implementations of this interface. These implementations are described in the following table:

Serializer

Description

GenericToStringSerializer<T>

Serializes strings to bytes and vice versa. Uses the Spring ConversionService to transform objects to strings and vice versa.

JacksonJsonRedisSerializer<T>

Converts objects to JSON and vice versa.

JdkSerializationRedisSerializer

Provides Java based serialization to objects.

OxmSerializer

Uses the Object/XML mapping support of Spring Framework 3.

StringRedisSerializer 

Converts strings to bytes and vice versa.

We can customize the serialization process of the RedisTemplate class by using the described serializers. The RedisTemplate class provides flexible configuration options that can be used to set the serializers that are used to serialize value keys, values, hash keys, hash values, and string values.

The default serializer of the RedisTemplate class is JdkSerializationRedisSerializer. However, the string serializer is an exception to this rule. StringRedisSerializer is the serializer that is by default used to serialize string values.

Implementing a CRUD application

This section describes two different ways for implementing a CRUD application that is used to manage contact information. First, we will learn how we can implement a CRUD application by using the default serializer of the RedisTemplate class. Second, we will learn how we can use value serializers and implement a CRUD application that stores our data in JSON format.

Both of these applications will also share the same domain model. This domain model consists of two classes: Contact and Address.

  • We removed the JPA specific annotations from them
  • We use these classes in our web layer as form objects and they no longer have any other methods than getters and setters

The domain model is not the only thing that is shared by these examples. They also share the interface that declares the service methods for the Contact class. The source code of the ContactService interface is given as follows:

public interface ContactService {
public Contact add(Contact added);
public Contact deleteById(Long id) throws NotFoundException;
public List&lt;Contact&gt; findAll();
public Contact findById(Long id) throws NotFoundException;
public Contact update(Contact updated) throws NotFoundException;
}

Both of these applications will communicate with the used Redis instance by using the Jedis connector.

Regardless of the user’s approach, we can implement a CRUD application with Spring Data Redis by following these steps:

  1. Configure the application context.
  2. Implement the CRUD functions.

Let’s get started and find out how we can implement the CRUD functions for contact information.

Using default serializers

This subsection describes how we can implement a CRUD application by using the default serializers of the RedisTemplate class. This means that StringRedisSerializer is used to serialize string values, and JdkSerializationRedisSerializer serializes other objects.

Configuring the application context

We can configure the application context of our application by making the following changes to the ApplicationContext class:

  1. Configuring the Redis template bean.
  2. Configuring the Redis atomic long bean.

Configuring the Redis template bean

We can configure the Redis template bean by adding a redisTemplate() method to the ApplicationContext class and annotating this method with the @Bean annotation. We can implement this method by following these steps:

  1. Create a new RedisTemplate object.
  2. Set the used connection factory to the created RedisTemplate object.
  3. Return the created object.

The source code of the redisTemplate() method is given as follows:

@Bean
public RedisTemplate redisTemplate() {
RedisTemplate&lt;String, String&gt; redis = new RedisTemplate&lt;String,
String&gt;();
redis.setConnectionFactory(redisConnectionFactory());
return redis;
}

Configuring the Redis atomic long bean

We start the configuration of the Redis atomic long bean by adding a method called redisAtomicLong() to the ApplicationContext class and annotating the method with the @Bean annotation. Our next task is to implement this method by following these steps:

  1. Create a new RedisAtomicLong object. Pass the name of the used Redis counter and the Redis connection factory as constructor parameters.
  2. Return the created object.

The source code of the redisAtomicLong() method is given as follows:

@Bean
public RedisAtomicLong redisAtomicLong() {
return new RedisAtomicLong("contact", redisConnectionFactory());
}

If we need to create IDs for instances of different classes, we can use the same Redis counter. Thus, we have to configure only one Redis atomic long bean.

LEAVE A REPLY

Please enter your comment!
Please enter your name here