|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Data Redis 4.1.1! |
Working with Objects through RedisTemplate
Most users are likely to use RedisTemplate and its corresponding package, org.springframework.data.redis.core or its reactive variant ReactiveRedisTemplate.
The template is, in fact, the central class of the Redis module, due to its rich feature set.
The template offers a high-level abstraction for Redis interactions.
While [Reactive]RedisConnection offers low-level methods that accept and return binary values (byte arrays), the template takes care of serialization and connection management, freeing the user from dealing with such details.
The RedisTemplate class implements the RedisOperations interface and its reactive variant ReactiveRedisTemplate implements ReactiveRedisOperations.
To use Redis JSON, use RedisJsonTemplate.
You can use Redis JSON (being an additional module) through a separate entry point featuring a fluent API that is different from the existing RedisTemplate approach, therefore, make sure to familiarize yourself with its specifics described in Working with Redis JSON.
The preferred way to reference operations on a [Reactive]RedisTemplate instance is through the
[Reactive]RedisOperations interface.
|
Moreover, the template provides operations views (following the grouping from the Redis command reference) that offer rich, generified interfaces for working against a certain type or certain key (through the KeyBound interfaces) as described in the following table:
Operational views
-
Imperative
-
Reactive
| Interface | Description |
|---|---|
Key Type Operations |
|
Redis geospatial operations, such as |
|
Redis hash operations |
|
Redis HyperLogLog operations, such as |
|
Redis list operations |
|
Redis set operations |
|
Redis string (or value) operations |
|
Redis zset (or sorted set) operations |
|
Redis JSON operations |
|
Key Bound Operations |
|
Redis key bound geospatial operations |
|
Redis hash key bound operations |
|
Redis key bound operations |
|
Redis list key bound operations |
|
Redis set key bound operations |
|
Redis string (or value) key bound operations |
|
Redis zset (or sorted set) key bound operations |
|
| Interface | Description |
|---|---|
Key Type Operations |
|
Redis geospatial operations such as |
|
Redis hash operations |
|
Redis HyperLogLog operations such as ( |
|
Redis list operations |
|
Redis set operations |
|
Redis string (or value) operations |
|
Redis zset (or sorted set) operations |
|
Once configured, the template is thread-safe and can be reused across multiple instances.
RedisTemplate uses a Java-based serializer for most of its operations.
This means that any object written or read by the template is serialized and deserialized through Java.
You can change the serialization mechanism on the template, and the Redis module offers several implementations, which are available in the org.springframework.data.redis.serializer package.
See Serializers for more information.
You can also set any of the serializers to null and use RedisTemplate with raw byte arrays by setting the enableDefaultSerializer property to false.
Note that the template requires all keys to be non-null.
However, values can be null as long as the underlying serializer accepts them.
Read the Javadoc of each serializer for more information.
For cases where you need a certain template view, declare the view as a dependency and inject the template.
The container automatically performs the conversion, eliminating the opsFor[X] calls, as shown in the following example:
-
Java Imperative
-
Java Reactive
-
XML
@Configuration
class MyConfig {
@Bean
LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
}
@Configuration
class MyConfig {
@Bean
LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
ReactiveRedisTemplate<String, String> ReactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="redisConnectionFactory"/>
...
</beans>
[Reactive]RedisTemplate-
Imperative
-
Reactive
public class Example {
// inject the actual operations
@Autowired
private RedisOperations<String, String> operations;
// inject the template as ListOperations
@Resource(name="redisTemplate")
private ListOperations<String, String> listOps;
public void addLink(String userId, URL url) {
listOps.leftPush(userId, url.toExternalForm());
}
}
public class Example {
// inject the actual template
@Autowired
private ReactiveRedisOperations<String, String> operations;
public Mono<Long> addLink(String userId, URL url) {
return operations.opsForList().leftPush(userId, url.toExternalForm());
}
}
Working with Redis JSON
RedisJsonTemplate provides a fluent API for storing JSON documents and accessing their values through Redis JSON commands.
It converts between JSON and Java objects and lets you select values within a document through JSONPath expressions.
Storing and Reading Documents
You can create a RedisJsonTemplate from a RedisConnectionFactory.
The following example stores a person with two addresses and reads the document back as a Java object:
record Address(String city) {}
record Person(String firstName, String lastName, List<Address> addresses) {}
RedisJsonTemplate<String> template = RedisJsonTemplate.create(connectionFactory);
template.set("user", new Person("John", "Doe",
List.of(new Address("London"), new Address("Paris"))));
Person person = template.get("user").as(Person.class);
The get method returns a JsonOperations.JsonResult.
Use its as method to convert the result to the required Java type.
Reading Values by Path
To read a value within a document, use value(key) with a JSONPath expression:
String firstName = template.value("user").path("$.firstName").get()
.as(String.class); // "John"
Redis returns JSONPath matches in an array, including when the path selects a single value or the entire document ($).
The as method unwraps this match array and converts the selected value, which can itself be an object, an array, or a scalar.
It returns null if there are no matches and throws a SerializationException if there is more than one match.
For paths that can select several values, use matches() to obtain a JsonOperations.JsonResults.
Its as method converts each match and returns a list.
The following example reads the city from each address:
List<String> cities = template.value("user").path("$.addresses[*].city").get()
.matches().as(String.class); // ["London", "Paris"]
You can use matches() for any number of matches, including zero.
Wildcards, recursive descent, slices, unions, and filters can select more than one value.
|
Calling
Unlike |
Reading Several Paths
The paths method reads several paths from one document in a single JSON.GET call.
It returns a JsonOperations.JsonPathResult, which you can convert to an object containing the selected properties:
record Name(String firstName, String lastName) {}
Name name = template.paths("user", "firstName", "lastName").as(Name.class);
For property paths, such as firstName or lastName, the as method maps an object keyed by the requested property paths.
In the preceding example, this object is {"firstName":"John","lastName":"Doe"}.
Each path’s match array is unwrapped before conversion.
A path with no matches contributes a JSON null value while a path with more than one match causes a SerializationException.
You can also use JSONPath expressions, in which case the object is keyed by those expressions.
To read the results individually, use path(String) with the same path you requested.
This also lets you read paths with multiple matches:
JsonPathResult result = template.paths("user", "$.firstName", "$.addresses[*].city");
String firstName = result.path("$.firstName").as(String.class);
List<String> cities = result.path("$.addresses[*].city").matches().as(String.class);
The template requires at least one path and rejects duplicate paths or a mixture of property paths and JSONPath expressions with an IllegalArgumentException.
It also throws an IllegalArgumentException if Redis omits a requested path from the reply, as can happen with unsupported JSONPath expressions.
Absent Values and JSON null
JsonResult.as(…) returns null when the key does not exist, the path has no matches, or the selected value is JSON null.
Use exists(), matches(), and isNull() to distinguish these cases:
| Case | exists() |
matches() |
isNull() |
as(…) |
|---|---|---|---|---|
Missing key |
|
empty |
|
|
Path matched nothing |
|
empty |
|
|
Path matched JSON |
|
1 element |
|
|
One non-null match |
|
1 element |
|
the converted value |
Accessing Raw JSON
Use JsonResult.asBytes() or JsonResult.asString() to access the JSON reply without converting it to a Java object or unwrapping the match array.
Both methods return null if the key does not exist.
For example, reading $.firstName with asString() returns ["John"], including the match array.
You can access individual matches as JSON through matches().asBytes() or matches().asString().
See the Javadoc for details on preserving the original JSON representation with custom serializers.
JsonPathResult also provides asBytes() and asString() methods.
These return the raw JSON.GET reply, including the match arrays and the JSONPath keys sent to Redis, rather than the object used for conversion through as(…).
For a single requested path, the reply contains only the match array.
String-focused Convenience Classes
Since it is quite common for the keys and values stored in Redis to be java.lang.String, the Redis modules provides two extensions to RedisConnection and RedisTemplate, respectively the StringRedisConnection (and its DefaultStringRedisConnection implementation) and StringRedisTemplate as a convenient one-stop solution for intensive String operations.
In addition to being bound to String keys, the template and the connection use the StringRedisSerializer underneath, which means the stored keys and values are human-readable (assuming the same encoding is used both in Redis and your code).
The following listings show an example:
-
Java Imperative
-
Java Reactive
-
XML
@Configuration
class RedisConfiguration {
@Bean
LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@Configuration
class RedisConfiguration {
@Bean
LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
ReactiveStringRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory factory) {
return new ReactiveStringRedisTemplate<>(factory);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="redisConnectionFactory"/>
</beans>
-
Imperative
-
Reactive
public class Example {
@Autowired
private StringRedisTemplate redisTemplate;
public void addLink(String userId, URL url) {
redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
}
}
public class Example {
@Autowired
private ReactiveStringRedisTemplate redisTemplate;
public Mono<Long> addLink(String userId, URL url) {
return redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
}
}
As with the other Spring templates, RedisTemplate and StringRedisTemplate let you talk directly to Redis through the RedisCallback interface.
This feature gives complete control to you, as it talks directly to the RedisConnection.
Note that the callback receives an instance of StringRedisConnection when a StringRedisTemplate is used.
The following example shows how to use the RedisCallback interface:
public void useCallback() {
redisOperations.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
Long size = connection.dbSize();
// Can cast to StringRedisConnection if using a StringRedisTemplate
((StringRedisConnection)connection).set("key", "value");
}
});
}
Serializers
From the framework perspective, the data stored in Redis is only bytes. While Redis itself supports various types, for the most part, these refer to the way the data is stored rather than what it represents. It is up to the user to decide whether the information gets translated into strings or any other objects.
In Spring Data, the conversion between the user (custom) types and raw data (and vice-versa) is handled by Spring Data Redis in the org.springframework.data.redis.serializer package.
This package contains two types of serializers that, as the name implies, take care of the serialization process:
-
Two-way serializers based on
RedisSerializer. -
Element readers and writers that use
RedisElementReaderandRedisElementWriter.
The main difference between these variants is that RedisSerializer primarily serializes to byte[] while readers and writers use ByteBuffer.
Multiple implementations are available (including two that have been already mentioned in this documentation):
-
JdkSerializationRedisSerializer, which is used by default forRedisCacheandRedisTemplate. -
the
StringRedisSerializer.
However, one can use OxmSerializer for Object/XML mapping through Spring OXM support or JacksonJsonRedisSerializer or GenericJacksonJsonRedisSerializer for storing data in JSON format.
Do note that the storage format is not limited only to values. It can be used for keys, values, or hashes without any restrictions.
|
By default, If you are concerned about security vulnerabilities due to Java serialization, consider the general-purpose serialization filter mechanism at the core JVM level: |