© 2011-2019 The original authors.
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. |
- Preface
- Introduction
- Reference Documentation
- 5. Redis support
- 5.1. Redis Requirements
- 5.2. Redis Support High-level View
- 5.3. Connecting to Redis
- 5.4. Redis Sentinel Support
- 5.5. Working with Objects through RedisTemplate
- 5.6. String-focused Convenience Classes
- 5.7. Serializers
- 5.8. Hash mapping
- 5.9. Redis Messaging (Pub/Sub)
- 5.10. Redis Transactions
- 5.11. Pipelining
- 5.12. Redis Scripting
- 5.13. Support Classes
- 6. Redis Cluster
- 7. Redis Repositories
- 5. Redis support
- Appendixes
Preface
1. New Features
This section briefly covers items that are new and noteworthy in the latest releases.
1.1. New in Spring Data Redis 1.8
-
Upgrade to Jedis 2.9.
-
Upgrade to
Lettuce
4.2 (Note: Lettuce 4.2 requires Java 8). -
Support for Redis GEO commands.
-
Support for Geospatial Indexes using Spring Data Repository abstractions (see Geospatial Index).
-
MappingRedisConverter
-basedHashMapper
implementation (see Hash mapping). -
Support for
PartialUpdate
in repositories (see Persisting Partial Updates). -
SSL support for connections to Redis cluster.
-
Support for
client name
throughConnectionFactory
when using Jedis.
1.2. New in Spring Data Redis 1.7
-
Support for RedisCluster.
-
Support for Spring Data Repository abstractions (see Redis Repositories).
1.3. New in Spring Data Redis 1.6
-
The
Lettuce
Redis driver switched from wg/lettuce to mp911de/lettuce. -
Support for
ZRANGEBYLEX
. -
Enhanced range operations for
ZSET
, including+inf
/-inf
. -
Performance improvements in
RedisCache
, now releasing connections earlier. -
Generic Jackson2
RedisSerializer
making use of Jackson’s polymorphic deserialization.
1.4. New in Spring Data Redis 1.5
-
Add support for Redis HyperLogLog commands:
PFADD
,PFCOUNT
, andPFMERGE
. -
Configurable
JavaType
lookup for Jackson-basedRedisSerializers
. -
PropertySource
-based configuration for connecting to Redis Sentinel (see: Redis Sentinel Support).
Introduction
This document is the reference guide for Spring Data Redis (SDR) Support. It explains Key-Value module concepts and semantics and the syntax for various stores namespaces.
For an introduction to key-value stores, Spring, or Spring Data examples, see Getting Started. This documentation refers only to Spring Data Redis Support and assumes the user is familiar with key-value storage and Spring concepts.
2. Why Spring Data Redis?
The Spring Framework is the leading full-stack Java/JEE application framework. It provides a lightweight container and a non-invasive programming model enabled by the use of dependency injection, AOP, and portable service abstractions.
NoSQL storage systems provide an alternative to classical RDBMS for horizontal scalability and speed. In terms of implementation, key-value stores represent one of the largest (and oldest) members in the NoSQL space.
The Spring Data Redis (SDR) framework makes it easy to write Spring applications that use the Redis key-value store by eliminating the redundant tasks and boilerplate code required for interacting with the store through Spring’s excellent infrastructure support.
3. Requirements
Spring Data Redis 2.x binaries require JDK level 8.0 and above and Spring Framework 4.3.25.RELEASE and above.
In terms of key-value stores, Redis 2.6.x or higher is required. Spring Data Redis is currently tested against the latest 4.0 release.
4. Getting Started
This section provides an easy-to-follow guide for getting started with the Spring Data Redis module.
4.1. First Steps
As explained in Why Spring Data Redis?, Spring Data Redis (SDR) provides integration between the Spring framework and the Redis key-value store. Consequently, you should become acquainted with both of these frameworks. Throughout the SDR documentation, each section provides links to relevant resources. However, you should become familiar with these topics before reading this guide.
4.1.1. Learning Spring
Spring Data uses Spring framework’s core functionality, such as the IoC container, resource abstract, and the AOP infrastructure. While it is not important to know the Spring APIs, understanding the concepts behind them is important. At a minimum, the idea behind IoC should be familiar. That being said, the more knowledge you have about the Spring, the faster you can pick up Spring Data Redis. In addition to the Spring Framework’s comprehensive documentation, there are a lot of articles, blog entries, and books on the matter. The Spring Guides home page offer a good place to start. In general, this should be the starting point for developers wanting to try Spring Data Redis.
4.1.2. Learning NoSQL and Key Value Stores
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms, and patterns (to make things worse, even the term itself has multiple meanings). While some of the principles are common, it is crucial that you be familiar to some degree with the stores supported by SDR. The best way to get acquainted with these solutions is to read their documentation and follow their examples. It usually does not take more then five to ten minutes to go through them and, if you come from an RDMBS-only background, many times these exercises can be eye-openers.
4.1.3. Trying out the Samples
One can find various samples for key-value stores in the dedicated Spring Data example repo, at http://github.com/spring-projects/spring-data-keyvalue-examples. For Spring Data Redis, you should pay particular attention to the retwisj
sample, a Twitter-clone built on top of Redis that can be run locally or be deployed into the cloud. See its documentation, the following blog entry for more information.
4.2. Need Help?
If you encounter issues or you are just looking for advice, use one of the links below:
4.2.1. Community Support
The Spring Data tag on Stack Overflow is a message board for all Spring Data (not just Redis) users to share information and help each other. Note that registration is needed only for posting.
4.2.2. Professional Support
Professional, from-the-source support, with guaranteed response time, is available from Pivotal Software, Inc., the company behind Spring Data and Spring.
4.3. Following Development
For information on the Spring Data source code repository, nightly builds, and snapshot artifacts, see the Spring Data home page.
You can help make Spring Data best serve the needs of the Spring community by interacting with developers on Stack Overflow at either spring-data or spring-data-redis.
If you encounter a bug or want to suggest an improvement (including to this documentation), please create a ticket on the Spring Data issue tracker.
To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community Portal.
Reference Documentation
Document structure
This part of the reference documentation explains the core functionality offered by Spring Data Redis.
Redis support introduces the Redis module feature set.
5. Redis support
One of the key-value stores supported by Spring Data is Redis. To quote the Redis project home page:
Redis is an advanced key-value store. It is similar to memcached but the dataset is not volatile, and values can be strings, exactly like in memcached, but also lists, sets, and ordered sets. All this data types can be manipulated with atomic operations to push/pop elements, add/remove elements, perform server side union, intersection, difference between sets, and so forth. Redis supports different kind of sorting abilities.
Spring Data Redis provides easy configuration and access to Redis from Spring applications. It offers both low-level and high-level abstractions for interacting with the store, freeing the user from infrastructural concerns.
5.1. Redis Requirements
Spring Data Redis requires Redis 2.6 or above and Java SE 6.0 or above . In terms of language bindings (or connectors), Spring Redis integrates with Jedis, JRedis (Deprecated since 1.7), SRP (Deprecated since 1.7) and Lettuce, four popular open-source Java libraries for Redis. If you are aware of any other connector that we should be integrating with please send us feedback.
5.2. Redis Support High-level View
The Redis support provides several components. For most tasks, the high-level abstractions and support services are the best choice. Note that, at any point, you can move between layers. For example, you can get a low-level connection (or even the native library) to communicate directly with Redis.
5.3. Connecting to Redis
One of the first tasks when using Redis and Spring is to connect to the store through the IoC container. To do that, a Java connector (or binding) is required. No matter the library you choose, you need to use only one set of Spring Data Redis APIs (which behaves consistently across all connectors): the org.springframework.data.redis.connection
package and its RedisConnection
and RedisConnectionFactory
interfaces for working with and retrieving active connections to Redis.
5.3.1. RedisConnection and RedisConnectionFactory
RedisConnection
provides the core building block for Redis communication, as it handles the communication with the Redis back end. It also automatically translates the underlying connecting library exceptions to Spring’s consistent DAO exception hierarchy so that you can switch the connectors without any code changes, as the operation semantics remain the same.
For the corner cases where the native library API is required, RedisConnection provides a dedicated method (getNativeConnection ) that returns the raw, underlying object used for communication.
|
Active RedisConnection
objects are created through RedisConnectionFactory
. In addition, the factory acts as PersistenceExceptionTranslator
objects, meaning that, once declared, they let you do transparent exception translation. For example, you can do exception translation through the use of the @Repository
annotation and AOP. For more information, see the dedicated section in the Spring Framework documentation.
Depending on the underlying configuration, the factory can return a new connection or an existing connection (when a pool or shared native connection is used). |
The easiest way to work with a RedisConnectionFactory
is to configure the appropriate connector through the IoC container and inject it into the using class.
Unfortunately, currently, not all connectors support all Redis features. When invoking a method on the Connection API that is unsupported by the underlying library, an UnsupportedOperationException is thrown.
|
5.3.2. Configuring the Jedis Connector
Jedis is one of the connectors supported by the Spring Data Redis module through the org.springframework.data.redis.connection.jedis
package. In its simplest form, the Jedis configuration looks as follow:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Jedis ConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"/>
</beans>
For production use, however, you might want to tweak settings such as the host or password, as shown in the following example:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="server" p:port="6379" />
</beans>
5.3.3. Configuring JRedis connector (Deprecated since 1.7)
JRedis is another popular, open-source connector supported by Spring Data Redis through the org.springframework.data.redis.connection.jredis
package.
A typical JRedis configuration can looks like this:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jredisConnectionFactory" class="org.springframework.data.redis.connection.jredis.JredisConnectionFactory" p:host-name="server" p:port="6379"/>
</beans>
The configuration is quite similar to Jedis, with one notable exception. By default, the JedisConnectionFactory
pools connections. In order to use a connection pool with JRedis, configure the JredisConnectionFactory
with an instance of JredisPool
. For example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jredisConnectionFactory" class="org.springframework.data.redis.connection.jredis.JredisConnectionFactory">
<constructor-arg>
<bean class="org.springframework.data.redis.connection.jredis.DefaultJredisPool">
<constructor-arg value="localhost" />
<constructor-arg value="6379" />
</bean>
</constructor-arg>
</bean>
</beans>
5.3.4. Configuring SRP connector (Deprecated since 1.7)
SRP (an acronym for Sam’s Redis Protocol) is the third open-source connector supported by Spring Data Redis through the org.springframework.data.redis.connection.srp
package.
By now, its configuration is probably easy to guess:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="srpConnectionFactory" class="org.springframework.data.redis.connection.srp.SrpConnectionFactory" p:host-name="server" p:port="6379"/>
</beans>
Needless to say, the configuration is quite similar to that of the other connectors.
5.3.5. Configuring the Lettuce connector
Lettuce is the fourth open-source connector supported by Spring Data Redis through the org.springframework.data.redis.connection.lettuce
package.
Its configuration is probably easy to guess:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="lettuceConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" p:host-name="server" p:port="6379"/>
</beans>
There are also a few Lettuce-specific connection parameters that can be tweaked. By default, all LettuceConnection
s created by the LettuceConnectionFactory
share the same thread-safe native connection for all non-blocking and non-transactional operations. Set shareNativeConnection
to false to use a dedicated connection each time. LettuceConnectionFactory
can also be configured with a LettucePool
to use for pooling blocking and transactional connections, or all connections if shareNativeConnection
is set to false.
5.4. Redis Sentinel Support
For dealing with high-availability Redis, Spring Data Redis has support for Redis Sentinel, using RedisSentinelConfiguration
, as shown in the following example:
/**
* jedis
*/
@Bean
public RedisConnectionFactory jedisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master("mymaster")
.sentinel("127.0.0.1", 26379) .sentinel("127.0.0.1", 26380);
return new JedisConnectionFactory(sentinelConfig);
}
/**
* lettuce
*/
@Bean
public RedisConnectionFactory lettuceConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration().master("mymaster")
.sentinel("127.0.0.1", 26379) .sentinel("127.0.0.1", 26380);
return new LettuceConnectionFactory(sentinelConfig);
}
Configuration Properties
|
Sometimes, direct interaction with one of the Sentinels is required. Using RedisConnectionFactory.getSentinelConnection()
or RedisConnection.getSentinelCommands()
gives you access to the first active Sentinel configured.
5.5. Working with Objects through RedisTemplate
Most users are likely to use RedisTemplate
and its coresponding package, org.springframework.data.redis.core
. 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 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.
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:
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 |
HashOperations |
Redis hash operations |
HyperLogLogOperations |
Redis HyperLogLog operations like (pfadd, pfcount,…) |
GeoOperations |
Redis geospatial operations like |
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 |
BoundHashOperations |
Redis hash key bound operations |
BoundGeoOperations |
Redis key bound geospatial 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:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
...
</beans>
public class Example {
// inject the actual template
@Autowired
private RedisTemplate<String, String> template;
// 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());
}
}
5.6. 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:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
...
</beans>
public class Example {
@Autowired
private StringRedisTemplate redisTemplate;
public void addLink(String userId, URL url) {
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() {
redisTemplate.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");
}
});
}
5.7. 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 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
RedisElementReader
andRedisElementWriter
.
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 forRedisCache
andRedisTemplate
. -
the
StringRedisSerializer
.
However one can use OxmSerializer
for Object/XML mapping through Spring OXM support or Jackson2JsonRedisSerializer
or GenericJackson2JsonRedisSerializer
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, originally developed for JDK 9 but backported to JDK 8, 7, and 6: |
5.8. Hash mapping
Data can be stored by using various data structures within Redis. Jackson2JsonRedisSerializer
can convert objects in JSON format. Ideally, JSON can be stored as a value by using plain keys. You can achieve a more sophisticated mapping of structured objects by using Redis hashes. Spring Data Redis offers various strategies for mapping data to hashes (depending on the use case):
-
Direct mapping, by using
HashOperations
and a serializer -
Using Redis Repositories
-
Using
HashMapper
andHashOperations
5.8.1. Hash Mappers
Hash mappers are converters of map objects to a Map<K, V>
and back. HashMapper
is intended for using with Redis Hashes.
Multiple implementations are available:
-
BeanUtilsHashMapper
using Spring’s BeanUtils. -
ObjectHashMapper
using Object-to-Hash Mapping.
The following example shows one way to implement hash mapping:
public class Person {
String firstname;
String lastname;
// …
}
public class HashMapping {
@Autowired
HashOperations<String, byte[], byte[]> hashOperations;
HashMapper<Object, byte[], byte[]> mapper = new ObjectHashMapper();
public void writeHash(String key, Person person) {
Map<byte[], byte[]> mappedHash = mapper.toHash(person);
hashOperations.putAll(key, mappedHash);
}
public Person loadHash(String key) {
Map<byte[], byte[]> loadedHash = hashOperations.entries("key");
return (Person) mapper.fromHash(loadedHash);
}
}
5.8.2. Jackson2HashMapper
Jackson2HashMapper
provides Redis Hash mapping for domain objects by using FasterXML Jackson.
Jackson2HashMapper
can map top-level properties as Hash field names and, optionally, flatten the structure.
Simple types map to simple values. Complex types (nested objects, collections, maps, and so on) are represented as nested JSON.
Flattening creates individual hash entries for all nested properties and resolves complex types into simple types, as far as possible.
Consider the following class and the data structure it contains:
public class Person {
String firstname;
String lastname;
Address address;
}
public class Address {
String city;
String country;
}
The following table shows how the data in the preceding class would appear in normal mapping:
Hash Field | Value |
---|---|
firstname |
|
lastname |
|
address |
|
The following table shows how the data in the preceding class would appear in flat mapping:
Hash Field | Value |
---|---|
firstname |
|
lastname |
|
address.city |
|
address.country |
|
Flattening requires all property names to not interfere with the JSON path. Using dots or brackets in map keys or as property names is not supported when you use flattening. The resulting hash cannot be mapped back into an Object. |
5.9. Redis Messaging (Pub/Sub)
Spring Data provides dedicated messaging integration for Redis, similar in functionality and naming to the JMS integration in Spring Framework.
Redis messaging can be roughly divided into two areas of functionality:
-
Publication or production of messages
-
Subscription or consumption of messages
This is an example of the pattern often called Publish/Subscribe (Pub/Sub for short). The RedisTemplate
class is used for message production. For asynchronous reception similar to Java EE’s message-driven bean style, Spring Data provides a dedicated message listener container that is used to create Message-Driven POJOs (MDPs) and, for synchronous reception, the RedisConnection
contract.
The org.springframework.data.redis.connection
and org.springframework.data.redis.listener
packages provide the core functionality for Redis messaging.
5.9.1. Publishing (Sending Messages)
To publish a message, you can use, as with the other operations, either the low-level RedisConnection
or the high-level RedisTemplate
. Both entities offer the publish
method, which accepts the message and the destination channel as arguments. While RedisConnection
requires raw data (array of bytes), the RedisTemplate
lets arbitrary objects be passed in as messages, as shown in the following example:
// send message through connection RedisConnection con = ...
byte[] msg = ...
byte[] channel = ...
con.publish(msg, channel); // send message through RedisTemplate
RedisTemplate template = ...
template.convertAndSend("hello!", "world");
5.9.2. Subscribing (Receiving Messages)
On the receiving side, one can subscribe to one or multiple channels either by naming them directly or by using pattern matching. The latter approach is quite useful, as it not only lets multiple subscriptions be created with one command but can also listen on channels not yet created at subscription time (as long as they match the pattern).
At the low-level, RedisConnection
offers the subscribe
and pSubscribe
methods that map the Redis commands for subscribing by channel or by pattern, respectively. Note that multiple channels or patterns can be used as arguments. To change the subscription of a connection or query whether it is listening, RedisConnection
provides the getSubscription
and isSubscribed
methods.
Subscription commands in Spring Data Redis are blocking. That is, calling subscribe on a connection causes the current thread to block as it starts waiting for messages. The thread is released only if the subscription is canceled, which happens when another thread invokes unsubscribe or pUnsubscribe on the same connection. See “Message Listener Containers” (later in this document) for a solution to this problem.
|
As mentioned earlier, once subscribed, a connection starts waiting for messages. Only commands that add new subscriptions, modify existing subscriptions, and cancel existing subscriptions are allowed. Invoking anything other than subscribe
, pSubscribe
, unsubscribe
, or pUnsubscribe
throws an exception.
In order to subscribe to messages, one needs to implement the MessageListener
callback. Each time a new message arrives, the callback gets invoked and the user code gets run by the onMessage
method. The interface gives access not only to the actual message but also to the channel it has been received through and the pattern (if any) used by the subscription to match the channel. This information lets the callee differentiate between various messages not just by content but also examining additional details.
Message Listener Containers
Due to its blocking nature, low-level subscription is not attractive, as it requires connection and thread management for every single listener. To alleviate this problem, Spring Data offers RedisMessageListenerContainer
, which does all the heavy lifting. If you are familiar with EJB and JMS, you should find the concepts familiar, as it is designed to be as close as possible to the support in Spring Framework and its message-driven POJOs (MDPs).
RedisMessageListenerContainer
acts as a message listener container. It is used to receive messages from a Redis channel and drive the MessageListener
instances that are injected into it. The listener container is responsible for all threading of message reception and dispatches into the listener for processing. A message listener container is the intermediary between an MDP and a messaging provider and takes care of registering to receive messages, resource acquisition and release, exception conversion, and the like. This lets you as an application developer write the (possibly complex) business logic associated with receiving a message (and reacting to it) and delegates boilerplate Redis infrastructure concerns to the framework.
Furthermore, to minimize the application footprint, RedisMessageListenerContainer
lets one connection and one thread be shared by multiple listeners even though they do not share a subscription. Thus, no matter how many listeners or channels an application tracks, the runtime cost remains the same throughout its lifetime. Moreover, the container allows runtime configuration changes so that you can add or remove listeners while an application is running without the need for a restart. Additionally, the container uses a lazy subscription approach, using a RedisConnection
only when needed. If all the listeners are unsubscribed, cleanup is automatically performed, and the thread is released.
To help with the asynchronous nature of messages, the container requires a java.util.concurrent.Executor
(or Spring’s TaskExecutor
) for dispatching the messages. Depending on the load, the number of listeners, or the runtime environment, you should change or tweak the executor to better serve your needs. In particular, in managed environments (such as app servers), it is highly recommended to pick a proper TaskExecutor
to take advantage of its runtime.
The MessageListenerAdapter
The MessageListenerAdapter
class is the final component in Spring’s asynchronous messaging support. In a nutshell, it lets you expose almost any class as a MDP (though there are some constraints).
Consider the following interface definition:
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message); void handleMessage(byte[] message);
void handleMessage(Serializable message);
// pass the channel/pattern as well
void handleMessage(Serializable message, String channel);
}
Notice that, although the interface does not extend the MessageListener
interface, it can still be used as a MDP by using the MessageListenerAdapter
class. Notice also how the various message handling methods are strongly typed according to the contents of the various Message
types that they can receive and handle. In addition, the channel or pattern to which a message is sent can be passed in to the method as the second argument of type String
:
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}
Notice how the above implementation of the MessageDelegate
interface (the above DefaultMessageDelegate
class) has no Redis dependencies at all. It truly is a POJO that we make into an MDP with the following configuration:
<?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:redis="http://www.springframework.org/schema/redis"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd">
<!-- the default ConnectionFactory -->
<redis:listener-container>
<!-- the method attribute can be skipped as the default method name is "handleMessage" -->
<redis:listener ref="listener" method="handleMessage" topic="chatroom" />
</redis:listener-container>
<bean id="listener" class="redisexample.DefaultMessageDelegate"/>
...
<beans>
The listener topic can be either a channel (for example, topic="chatroom" ) or a pattern (for example, topic="*room" )
|
The preceding example uses the Redis namespace to declare the message listener container and automatically register the POJOs as listeners. The full blown beans definition follows:
<bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="redisexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListeners">
<map>
<entry key-ref="messageListener">
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="chatroom">
</bean>
</entry>
</map>
</property>
</bean>
Each time a message is received, the adapter automatically and transparently performs translation (using the configured RedisSerializer
) between the low-level format and the required object type. Any exception caused by the method invocation is caught and handled by the container (by default, exceptions get logged).
5.10. Redis Transactions
Redis provides support for transactions through the multi
, exec
, and discard
commands. These operations are available on RedisTemplate
. However, RedisTemplate
is not guaranteed to execute all operations in the transaction with the same connection.
Spring Data Redis provides the SessionCallback
interface for use when multiple operations need to be performed with the same connection
, such as when using Redis transactions. The following example uses the multi
method:
//execute a transaction
List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() {
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForSet().add("key", "value1");
// This will contain the results of all operations in the transaction
return operations.exec();
}
});
System.out.println("Number of items added to set: " + txResults.get(0));
RedisTemplate
uses its value, hash key, and hash value serializers to deserialize all results of exec
before returning. There is an additional exec
method that lets you pass a custom serializer for transaction results.
As of version 1.1, an important change has been made to the exec methods of RedisConnection and RedisTemplate . Previously, these methods returned the results of transactions directly from the connectors. This means that the data types often differed from those returned from the methods of RedisConnection . For example, zAdd returns a boolean indicating whether the element has been added to the sorted set. Most connectors return this value as a long, and Spring Data Redis performs the conversion. Another common difference is that most connectors return a status reply (usually the string, OK ) for operations such as set . These replies are typically discarded by Spring Data Redis. Prior to 1.1, these conversions were not performed on the results of exec . Also, results were not deserialized in RedisTemplate , so they often included raw byte arrays. If this change breaks your application, set convertPipelineAndTxResults to false on your RedisConnectionFactory to disable this behavior.
|
5.10.1. @Transactional Support
By default, transaction Support is disabled and has to be explicitly enabled for each RedisTemplate
in use by setting setEnableTransactionSupport(true)
. Doing so forces binding the current RedisConnection
to the current Thread
that is triggering MULTI
. If the transaction finishes without errors, EXEC
is called. Otherwise DISCARD
is called. Once in MULTI
, RedisConnection
queues write operations. All readonly
operations, such as KEYS
, are piped to a fresh (non-thread-bound) RedisConnection
.
The following example shows how to configure transaction management:
@Configuration
@EnableTransactionManagement (1)
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true); (2)
return template;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// jedis || Lettuce || srp || ...
}
@Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource()); (3)
}
@Bean
public DataSource dataSource() throws SQLException {
// ...
}
}
1 | Configures a Spring Context to enable declarative transaction management. |
2 | Configures RedisTemplate to participate in transactions by binding connections to the current thread. |
3 | Transaction management requires a PlatformTransactionManager . Spring Data Redis does not ship with a PlatformTransactionManager implementation. Assuming your application uses JDBC, Spring Data Redis can participate in transactions by using existing transaction managers. |
The following examples each demonstrate a usage constraint:
// must be performed on thread-bound connection
template.opsForValue().set("thing1", "thing2");
// read operation must be executed on a free (not transaction-aware) connection
template.keys("*");
// returns null as values set within a transaction are not visible
template.opsForValue().get("thing1");
5.11. Pipelining
Redis provides support for pipelining, which involves sending multiple commands to the server without waiting for the replies and then reading the replies in a single step. Pipelining can improve performance when you need to send several commands in a row, such as adding many elements to the same List.
Spring Data Redis provides several RedisTemplate
methods for executing commands in a pipeline. If you do not care about the results of the pipelined operations, you can use the standard execute
method, passing true
for the pipeline
argument. The executePipelined
methods run the provided RedisCallback
or SessionCallback
in a pipeline and return the results, as shown in the following example:
//pop a specified number of items from a queue
List<Object> results = stringRedisTemplate.executePipelined(
new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
for(int i=0; i< batchSize; i++) {
stringRedisConn.rPop("myqueue");
}
return null;
}
});
The preceding example runs a bulk right pop of items from a queue in a pipeline. The results
List
contains all of the popped items. RedisTemplate
uses its value, hash key, and hash value serializers to deserialize all results before returning, so the returned items in the preceding example are Strings. There are additional executePipelined
methods that let you pass a custom serializer for pipelined results.
Note that the value returned from the RedisCallback
is required to be null, as this value is discarded in favor of returning the results of the pipelined commands.
As of version 1.1, an important change has been made to the exec methods of RedisConnection and RedisTemplate . Previously, these methods returned the results of transactions directly from the connectors. This means that the data types often differed from those returned from the methods of RedisConnection . For example, zAdd returns a boolean indicating whether the element has been added to the sorted set. Most connectors return this value as a long, and Spring Data Redis performs the conversion. Another common difference is that most connectors return a status reply (usually the string, OK ) for operations such as set . These replies are typically discarded by Spring Data Redis. Prior to 1.1, these conversions were not performed on the results of exec . Also, results were not deserialized in RedisTemplate , so they often included raw byte arrays. If this change breaks your application, set convertPipelineAndTxResults to false on your RedisConnectionFactory to disable this behavior.
|
5.12. Redis Scripting
Redis versions 2.6 and higher provide support for execution of Lua scripts through the eval and evalsha commands. Spring Data Redis provides a high-level abstraction for script execution that handles serialization and automatically uses the Redis script cache.
Scripts can be run by calling the execute
methods of RedisTemplate
. It uses a configurable ScriptExecutor
to run the provided script. By default, the ScriptExecutor
takes care of serializing the provided keys and arguments and deserializing the script result. This is done through the key and value serializers of the template. There is an additional overload that lets you pass custom serializers for the script arguments and the result.
The default ScriptExecutor
optimizes performance by retrieving the SHA1 of the script and attempting first to run evalsha
, falling back to eval
if the script is not yet present in the Redis script cache.
The following example runs a common “check-and-set” scenario by using a Lua script. This is an ideal use case for a Redis script, as it requires that running a set of commands atomically, and the behavior of one command is influenced by the result of another.
@Bean
public RedisScript<Boolean> script() {
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua")));
redisScript.setResultType(Boolean.class);
}
public class Example {
@Autowired
RedisScript<Boolean> script;
public boolean checkAndSet(String expectedValue, String newValue) {
return redisTemplate.execute(script, Collections.singletonList("key"), expectedValue, newValue);
}
}
-- checkandset.lua
local current = redis.call('GET', KEYS[1])
if current == ARGV[1]
then redis.call('SET', KEYS[1], ARGV[2])
return true
end
return false
The preceding code configures a RedisScript
pointing to a file called checkandset.lua
, which is expected to return a boolean value. The script resultType
should be one of Long
, Boolean
, List
, or a deserialized value type. It can also be null
if the script returns a throw-away status (specifically, OK
).
It is ideal to configure a single instance of DefaultRedisScript in your application context to avoid re-calculation of the script’s SHA1 on every script execution.
|
The checkAndSet
method above then runs the scripts. Scripts can be run within a SessionCallback
as part of a transaction or pipeline. See “Redis Transactions” and “Pipelining” for more information.
The scripting support provided by Spring Data Redis also lets you schedule Redis scripts for periodic execution by using the Spring Task and Scheduler abstractions. See the Spring Framework documentation for more details.
5.13. Support Classes
Package org.springframework.data.redis.support
offers various reusable components that rely on Redis as a backing store. Currently, the package contains various JDK-based interface implementations on top of Redis, such as atomic counters and JDK Collections.
The atomic counters make it easy to wrap Redis key incrementation while the collections allow easy management of Redis keys with minimal storage exposure or API leakage. In particular, the RedisSet
and RedisZSet
interfaces offer easy access to the set operations supported by Redis, such as intersection
and union
. RedisList
implements the List
, Queue
, and Deque
contracts (and their equivalent blocking siblings) on top of Redis, exposing the storage as a FIFO (First-In-First-Out), LIFO (Last-In-First-Out) or capped collection with minimal configuration. The following example shows the configuration for a bean that uses a RedisList
:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="queue" class="org.springframework.data.redis.support.collections.DefaultRedisList">
<constructor-arg ref="redisTemplate"/>
<constructor-arg value="queue-key"/>
</bean>
</beans>
The following example shows a Java configuration example for a Deque
:
public class AnotherExample {
// injected
private Deque<String> queue;
public void addTag(String tag) {
queue.push(tag);
}
}
As shown in the preceding example, the consuming code is decoupled from the actual storage implementation. In fact, there is no indication that Redis is used underneath. This makes moving from development to production environments transparent and highly increases testability (the Redis implementation can be replaced with an in-memory one).
5.13.1. Support for the Spring Cache Abstraction
Spring Redis provides an implementation for Spring cache abstraction through the org.springframework.data.redis.cache
package. To use Redis as a backing implementation, simply add RedisCacheManager
to your configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- turn on declarative caching -->
<cache:annotation-driven />
<!-- declare Redis Cache Manager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:template-ref="redisTemplate"/>
</beans>
By default RedisCacheManager will lazily initialize RedisCache whenever a Cache is requested. This can be changed by predefining a Set of cache names.
|
By default RedisCacheManager will not participate in any ongoing transaction. Use setTransactionAware to enable transaction support.
|
By default RedisCacheManager does not prefix keys for cache regions, which can lead to an unexpected growth of a ZSET used to maintain known keys. It’s highly recommended to enable the usage of prefixes in order to avoid this unexpected growth and potential key clashes using more than one cache region.
|
By default RedisCache will not cache any null values as keys without a value get dropped by Redis itself. However you can explicitly enable null value caching via RedisCacheManager which will store org.springframework.cache.support.NullValue as a placeholder.
|
6. Redis Cluster
Working with Redis Cluster requires Redis Server version 3.0+. See the Cluster Tutorial for more information.
6.1. Enabling Redis Cluster
Cluster support is based on the same building blocks as non-clustered communication. RedisClusterConnection
, an extension to RedisConnection
, handles the communication with the Redis Cluster and translates errors into the Spring DAO exception hierarchy.
RedisClusterConnection
instances are created with the RedisConnectionFactory
, which has to be set up with the associated RedisClusterConfiguration
, as shown in the following example:
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {
/*
* spring.redis.cluster.nodes[0] = 127.0.0.1:7379
* spring.redis.cluster.nodes[1] = 127.0.0.1:7380
* ...
*/
List<String> nodes;
/**
* Get initial collection of known cluster nodes in format {@code host:port}.
*
* @return
*/
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
@Configuration
public class AppConfig {
/**
* Type safe representation of application.properties
*/
@Autowired ClusterConfigurationProperties clusterProperties;
public @Bean RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(
new RedisClusterConfiguration(clusterProperties.getNodes()));
}
}
Configuration Properties
|
The initial configuration points driver libraries to an initial set of cluster nodes. Changes resulting from live cluster reconfiguration are kept only in the native driver and are not written back to the configuration. |
6.2. Working With Redis Cluster Connection
As mentioned earlier, Redis Cluster behaves differently from single-node Redis or even a Sentinel-monitored master-slave environment. This is because the automatic sharding maps a key to one of 16384 slots, which are distributed across the nodes. Therefore, commands that involve more than one key must assert all keys map to the exact same slot to avoid cross-slot execution errors.
A single cluster node serves only a dedicated set of keys. Commands issued against one particular server return results only for those keys served by that server. As a simple example, consider the KEYS
command. When issued to a server in a cluster environment, it returns only the keys served by the node the request is sent to and not necessarily all keys within the cluster. So, to get all keys in a cluster environment, you must read the keys from all the known master nodes.
While redirects for specific keys to the corresponding slot-serving node are handled by the driver libraries, higher-level functions, such as collecting information across nodes or sending commands to all nodes in the cluster, are covered by RedisClusterConnection
. Picking up the keys example from earlier, this means that the keys(pattern)
method picks up every master node in the cluster and simultaneously executes the KEYS
command on every master node while picking up the results and returning the cumulated set of keys. To just request the keys of a single node RedisClusterConnection
provides overloads for those methods (for example, keys(node, pattern)
).
A RedisClusterNode
can be obtained from RedisClusterConnection.clusterGetNodes
or it can be constructed by using either the host and the port or the node Id.
The following example shows a set of commands being run across the cluster:
[email protected]:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460 (1)
7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-10922 (2)
164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-16383 (3)
b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected (4)
RedisClusterConnection connection = connectionFactory.getClusterConnnection();
connection.set("thing1", value); (5)
connection.set("thing2", value); (6)
connection.keys("*"); (7)
connection.keys(NODE_7379, "*"); (8)
connection.keys(NODE_7380, "*"); (9)
connection.keys(NODE_7381, "*"); (10)
connection.keys(NODE_7382, "*"); (11)
1 | Master node serving slots 0 to 5460 replicated to slave at 7382 |
2 | Master node serving slots 5461 to 10922 |
3 | Master node serving slots 10923 to 16383 |
4 | Slave node holding replicants of the master at 7379 |
5 | Request routed to node at 7381 serving slot 12182 |
6 | Request routed to node at 7379 serving slot 5061 |
7 | Request routed to nodes at 7379, 7380, 7381 → [thing1, thing2] |
8 | Request routed to node at 7379 → [thing2] |
9 | Request routed to node at 7380 → [] |
10 | Request routed to node at 7381 → [thing1] |
11 | Request routed to node at 7382 → [thing2] |
When all keys map to the same slot, the native driver library automatically serves cross-slot requests, such as MGET
. However, once this is not the case, RedisClusterConnection
executes multiple parallel GET
commands against the slot-serving nodes and again returns an accumulated result. This is less performant than the single-slot execution and, therefore, should be used with care. If in doubt, consider pinning keys to the same slot by providing a prefix in curly brackets, such as {my-prefix}.thing1
and {my-prefix}.thing2
, which will both map to the same slot number. The following example shows cross-slot request handling:
[email protected]:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460 (1)
7bb...
RedisClusterConnection connection = connectionFactory.getClusterConnnection();
connection.set("thing1", value); // slot: 12182
connection.set("{thing1}.thing2", value); // slot: 12182
connection.set("thing2", value); // slot: 5461
connection.mGet("thing1", "{thing1}.thing2"); (2)
connection.mGet("thing1", "thing2"); (3)
1 | Same Configuration as in the sample before. |
2 | Keys map to same slot → 127.0.0.1:7381 MGET thing1 {thing1}.thing2 |
3 | Keys map to different slots and get split up into single slot ones routed to the according nodes → 127.0.0.1:7379 GET thing2 → 127.0.0.1:7381 GET thing1 |
The preceding examples demonstrate the general strategy followed by Spring Data Redis. Be aware that some operations might require loading huge amounts of data into memory to compute the desired command. Additionally, not all cross-slot requests can safely be ported to multiple single slot requests and error if misused (for example, PFCOUNT ).
|
6.3. Working with RedisTemplate
and ClusterOperations
See the Working with Objects through RedisTemplate section for information about the general purpose, configuration, and usage of RedisTemplate
.
Be careful when setting up RedisTemplate#keySerializer using any of the Json RedisSerializers , as changing JSON structure has immediate influence on hash slot calculation.
|
RedisTemplate
provides access to cluster-specific operations through the ClusterOperations
interface, which can be obtained from RedisTemplate.opsForCluster()
. This lets you explicitly run commands on a single node within the cluster while retaining the serialization and deserialization features configured for the template. It also provides administrative commands (such as CLUSTER MEET
) or more high-level operations (for example, resharding).
The following example shows how to access RedisClusterConnection
with RedisTemplate
:
RedisClusterConnection
with RedisTemplate
ClusterOperations clusterOps = redisTemplate.opsForCluster();
clusterOps.shutdown(NODE_7379); (1)
1 | Shut down node at 7379 and cross fingers there is a slave in place that can take over. |
7. Redis Repositories
Working with Redis Repositories lets you seamlessly convert and store domain objects in Redis Hashes, apply custom mapping strategies, and use secondary indexes.
Redis Repositories require at least Redis Server version 2.8.0 and do not work with transactions. Make sure to use a RedisTemplate with disabled transaction support.
|
7.1. Usage
Spring Data Redis lets you easily implement domain entities, as shown in the following example:
@RedisHash("people")
public class Person {
@Id String id;
String firstname;
String lastname;
Address address;
}
We have a pretty simple domain object here. Note that it has a @RedisHash
annotation on its type and a property named id
that is annotated with org.springframework.data.annotation.Id
. Those two items are responsible for creating the actual key used to persist the hash.
Properties annotated with @Id as well as those named id are considered as the identifier properties. Those with the annotation are favored over others.
|
To now actually have a component responsible for storage and retrieval, we need to define a repository interface, as shown in the following example:
public interface PersonRepository extends CrudRepository<Person, String> {
}
As our repository extends CrudRepository
, it provides basic CRUD and finder operations. The thing we need in between to glue things together is the corresponding Spring configuration, shown in the following example:
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {
@Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
return template;
}
}
Given the preceding setup, we can inject PersonRepository
into our components, as shown in the following example:
@Autowired PersonRepository repo;
public void basicCrudOperations() {
Person rand = new Person("rand", "al'thor");
rand.setAddress(new Address("emond's field", "andor"));
repo.save(rand); (1)
repo.findOne(rand.getId()); (2)
repo.count(); (3)
repo.delete(rand); (4)
}
1 | Generates a new id if the current value is null or reuses an already set id value and stores properties of type Person inside the Redis Hash with a key that has a pattern of keyspace:id — in this case, it might be people:5d67b7e1-8640-4475-beeb-c666fab4c0e5 . |
2 | Uses the provided id to retrieve the object stored at keyspace:id . |
3 | Counts the total number of entities available within the keyspace, people , defined by @RedisHash on Person . |
4 | Removes the key for the given object from Redis. |
7.2. Object-to-Hash Mapping
The Redis Repository support persists Objects to Hashes. This requires an Object-to-Hash conversion which is done by a RedisConverter
. The default implementation uses Converter
for mapping property values to and from Redis native byte[]
.
Given the Person
type from the previous sections, the default mapping looks like the following:
_class = org.example.Person (1)
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand (2)
lastname = al’thor
address.city = emond's field (3)
address.country = andor
1 | The _class attribute is included on the root level as well as on any nested interface or abstract types. |
2 | Simple property values are mapped by path. |
3 | Properties of complex types are mapped by their dot path. |
The following table describes the default mapping rules:
Type | Sample | Mapped Value |
---|---|---|
Simple Type |
String firstname = "rand"; |
firstname = "rand" |
Complex Type |
Address address = new Address("emond’s field"); |
address.city = "emond’s field" |
List |
List<String> nicknames = asList("dragon reborn", "lews therin"); |
nicknames.[0] = "dragon reborn", |
Map |
Map<String, String> atts = asMap({"eye-color", "grey"}, {"… |
atts.[eye-color] = "grey", |
List |
List<Address> addresses = asList(new Address("em… |
addresses.[0].city = "emond’s field", |
Map |
Map<String, Address> addresses = asMap({"home", new Address("em… |
addresses.[home].city = "emond’s field", |
Due to the flat representation structure, Map keys need to be simple types, such as String or Number .
|
Mapping behavior can be customized by registering the corresponding Converter
in RedisCustomConversions
. Those converters can take care of converting from and to a single byte[]
as well as Map<String,byte[]>
. The first one is suitable for (for example) converting a complex type to (for example) a binary JSON representation that still uses the default mappings hash structure. The second option offers full control over the resulting hash.
Writing objects to a Redis hash deletes the content from the hash and re-creates the whole hash, so data that has not been mapped is lost. |
The following example shows two sample byte array converters:
@WritingConverter
public class AddressToBytesConverter implements Converter<Address, byte[]> {
private final Jackson2JsonRedisSerializer<Address> serializer;
public AddressToBytesConverter() {
serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
serializer.setObjectMapper(new ObjectMapper());
}
@Override
public byte[] convert(Address value) {
return serializer.serialize(value);
}
}
@ReadingConverter
public class BytesToAddressConverter implements Converter<byte[], Address> {
private final Jackson2JsonRedisSerializer<Address> serializer;
public BytesToAddressConverter() {
serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
serializer.setObjectMapper(new ObjectMapper());
}
@Override
public Address convert(byte[] value) {
return serializer.deserialize(value);
}
}
Using the preceding byte array Converter
produces output similar to the following:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond's field", country : "andor" }
The following example shows two examples of Map
converters:
@WritingConverter
public class AddressToMapConverter implements Converter<Address, Map<String,byte[]>> {
@Override
public Map<String,byte[]> convert(Address source) {
return singletonMap("ciudad", source.getCity().getBytes());
}
}
@ReadingConverter
public class MapToAddressConverter implements Converter<Address, Map<String, byte[]>> {
@Override
public Address convert(Map<String,byte[]> source) {
return new Address(new String(source.get("ciudad")));
}
}
Using the preceding Map Converter
produces output similar to the following:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond's field"
Custom conversions have no effect on index resolution. Secondary Indexes are still created, even for custom converted types. |
7.2.1. Customizing Type Mapping
If you want to avoid writing the entire Java class name as type information and would rather like to use a key, you can use the @TypeAlias
annotation on the entity class being persisted. If you need to customize the mapping even more, look at the TypeInformationMapper
interface. An instance of that interface can be configured at the DefaultRedisTypeMapper
, which can be configured on MappingRedisConverter
.
The following example shows how to define a type alias for an entity:
@TypeAlias
for an entity@TypeAlias("pers")
class Person {
}
The resulting document contains pers
as the value in a _class
field.
Configuring Custom Type Mapping
The following example demonstrates how to configure a custom RedisTypeMapper
in MappingRedisConverter
:
RedisTypeMapper
via Spring Java Configclass CustomRedisTypeMapper extends DefaultRedisTypeMapper {
//implement custom type mapping here
}
@Configuration
class SampleRedisConfiguration {
@Bean
public MappingRedisConverter redisConverter(RedisMappingContext mappingContext,
RedisCustomConversions customConversions, ReferenceResolver referenceResolver) {
MappingRedisConverter mappingRedisConverter = new MappingRedisConverter(mappingContext, null, referenceResolver,
customTypeMapper());
mappingRedisConverter.setCustomConversions(customConversions);
return mappingRedisConverter;
}
@Bean
public RedisTypeMapper customTypeMapper() {
return new CustomRedisTypeMapper();
}
}
7.3. Keyspaces
Keyspaces define prefixes used to create the actual key for the Redis Hash.
By default, the prefix is set to getClass().getName()
. You can alter this default by setting @RedisHash
on the aggregate root level or by setting up a programmatic configuration. However, the annotated keyspace supersedes any other configuration.
The following example shows how to set the keyspace configuration with the @EnableRedisRepositories
annotation:
@EnableRedisRepositories
@Configuration
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class ApplicationConfig {
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
@Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
return Collections.singleton(new KeyspaceSettings(Person.class, "people"));
}
}
}
The following example shows how to programmatically set the keyspace:
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
@Bean
public RedisMappingContext keyValueMappingContext() {
return new RedisMappingContext(
new MappingConfiguration(
new MyKeyspaceConfiguration(), new IndexConfiguration()));
}
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
@Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
return Collections.singleton(new KeyspaceSettings(Person.class, "people"));
}
}
}
7.4. Secondary Indexes
Secondary indexes are used to enable lookup operations based on native Redis structures. Values are written to the according indexes on every save and are removed when objects are deleted or expire.
7.4.1. Simple Property Index
Given the sample Person
entity shown earlier, we can create an index for firstname
by annotating the property with @Indexed
, as shown in the following example:
@RedisHash("people")
public class Person {
@Id String id;
@Indexed String firstname;
String lastname;
Address address;
}
Indexes are built up for actual property values. Saving two Persons (for example, "rand" and "aviendha") results in setting up indexes similar to the following:
SADD people:firstname:rand e2c7dcee-b8cd-4424-883e-736ce564363e
SADD people:firstname:aviendha a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56
It is also possible to have indexes on nested elements. Assume Address
has a city
property that is annotated with @Indexed
. In that case, once person.address.city
is not null
, we have Sets for each city, as shown in the following example:
SADD people:address.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e
Furthermore, the programmatic setup lets you define indexes on map keys and list properties, as shown in the following example:
@RedisHash("people")
public class Person {
// ... other properties omitted
Map<String,String> attributes; (1)
Map<String Person> relatives; (2)
List<Address> addresses; (3)
}
1 | SADD people:attributes.map-key:map-value e2c7dcee-b8cd-4424-883e-736ce564363e |
2 | SADD people:relatives.map-key.firstname:tam e2c7dcee-b8cd-4424-883e-736ce564363e |
3 | SADD people:addresses.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e |
Indexes cannot be resolved on References. |
As with keyspaces, you can configure indexes without needing to annotate the actual domain type, as shown in the following example:
@Configuration
@EnableRedisRepositories(indexConfiguration = MyIndexConfiguration.class)
public class ApplicationConfig {
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
public static class MyIndexConfiguration extends IndexConfiguration {
@Override
protected Iterable<IndexDefinition> initialConfiguration() {
return Collections.singleton(new SimpleIndexDefinition("people", "firstname"));
}
}
}
Again, as with keyspaces, you can programmatically configure indexes, as shown in the following example:
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
@Bean
public RedisMappingContext keyValueMappingContext() {
return new RedisMappingContext(
new MappingConfiguration(
new KeyspaceConfiguration(), new MyIndexConfiguration()));
}
public static class MyIndexConfiguration extends IndexConfiguration {
@Override
protected Iterable<IndexDefinition> initialConfiguration() {
return Collections.singleton(new SimpleIndexDefinition("people", "firstname"));
}
}
}
7.4.2. Geospatial Index
Assume the Address
type contains a location
property of type Point
that holds the geo coordinates of the particular address. By annotating the property with @GeoIndexed
, Spring Data Redis adds those values by using Redis GEO
commands, as shown in the following example:
@RedisHash("people")
public class Person {
Address address;
// ... other properties omitted
}
public class Address {
@GeoIndexed Point location;
// ... other properties omitted
}
public interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByAddressLocationNear(Point point, Distance distance); (1)
List<Person> findByAddressLocationWithin(Circle circle); (2)
}
Person rand = new Person("rand", "al'thor");
rand.setAddress(new Address(new Point(13.361389D, 38.115556D)));
repository.save(rand); (3)
repository.findByAddressLocationNear(new Point(15D, 37D), new Distance(200)); (4)
1 | Query method declaration on a nested property, using Point and Distance . |
2 | Query method declaration on a nested property, using Circle to search within. |
3 | GEOADD people:address:location 13.361389 38.115556 e2c7dcee-b8cd-4424-883e-736ce564363e |
4 | GEORADIUS people:address:location 15.0 37.0 200.0 km |
In the preceding example the, longitude and latitude values are stored by using GEOADD
that use the object’s id
as the member’s name. The finder methods allow usage of Circle
or Point, Distance
combinations for querying those values.
It is not possible to combine near and within with other criteria.
|
7.5. Time To Live
Objects stored in Redis may be valid only for a certain amount of time. This is especially useful for persisting short-lived objects in Redis without having to remove them manually when they reach their end of life. The expiration time in seconds can be set with @RedisHash(timeToLive=…)
as well as by using KeyspaceSettings
(see Keyspaces).
More flexible expiration times can be set by using the @TimeToLive
annotation on either a numeric property or a method. However, do not apply @TimeToLive
on both a method and a property within the same class. The following example shows the @TimeToLive
annotation on a property and on a method:
public class TimeToLiveOnProperty {
@Id
private String id;
@TimeToLive
private Long expiration;
}
public class TimeToLiveOnMethod {
@Id
private String id;
@TimeToLive
public long getTimeToLive() {
return new Random().nextLong();
}
}
Annotating a property explicitly with @TimeToLive reads back the actual TTL or PTTL value from Redis. -1 indicates that the object has no associated expiration.
|
The repository implementation ensures subscription to Redis keyspace notifications via RedisMessageListenerContainer
.
When the expiration is set to a positive value, the corresponding EXPIRE
command is executed. In addition to persisting the original, a phantom copy is persisted in Redis and set to expire five minutes after the original one. This is done to enable the Repository support to publish RedisKeyExpiredEvent
, holding the expired value in Spring’s ApplicationEventPublisher
whenever a key expires, even though the original values have already been removed. Expiry events are received on all connected applications that use Spring Data Redis repositories.
By default, the key expiry listener is disabled when initializing the application. The startup mode can be adjusted in @EnableRedisRepositories
or RedisKeyValueAdapter
to start the listener with the application or upon the first insert of an entity with a TTL. See EnableKeyspaceEvents
for possible values.
The RedisKeyExpiredEvent
holds a copy of the expired domain object as well as the key.
Delaying or disabling the expiry event listener startup impacts RedisKeyExpiredEvent publishing. A disabled event listener does not publish expiry events. A delayed startup can cause loss of events because of the delayed listener initialization.
|
The keyspace notification message listener alters notify-keyspace-events settings in Redis, if those are not already set. Existing settings are not overridden, so you must set up those settings correctly (or leave them empty). Note that CONFIG is disabled on AWS ElastiCache, and enabling the listener leads to an error.
|
Redis Pub/Sub messages are not persistent. If a key expires while the application is down, the expiry event is not processed, which may lead to secondary indexes containing references to the expired object. |
7.6. Persisting References
Marking properties with @Reference
allows storing a simple key reference instead of copying values into the hash itself.
On loading from Redis, references are resolved automatically and mapped back into the object, as shown in the following example:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
mother = people:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56 (1)
1 | Reference stores the whole key (keyspace:id ) of the referenced object. |
Referenced Objects are not persisted when the referencing object is saved. You must persist changes on referenced objects separately, since only the reference is stored. Indexes set on properties of referenced types are not resolved. |
7.7. Persisting Partial Updates
In some cases, you need not load and rewrite the entire entity just to set a new value within it. A session timestamp for the last active time might be such a scenario where you want to alter one property.
PartialUpdate
lets you define set
and delete
actions on existing objects while taking care of updating potential expiration times of both the entity itself and index structures. The following example shows a partial update:
PartialUpdate<Person> update = new PartialUpdate<Person>("e2c7dcee", Person.class)
.set("firstname", "mat") (1)
.set("address.city", "emond's field") (2)
.del("age"); (3)
template.update(update);
update = new PartialUpdate<Person>("e2c7dcee", Person.class)
.set("address", new Address("caemlyn", "andor")) (4)
.set("attributes", singletonMap("eye-color", "grey")); (5)
template.update(update);
update = new PartialUpdate<Person>("e2c7dcee", Person.class)
.refreshTtl(true); (6)
.set("expiration", 1000);
template.update(update);
1 | Set the simple firstname property to mat . |
2 | Set the simple 'address.city' property to 'emond’s field' without having to pass in the entire object. This does not work when a custom conversion is registered. |
3 | Remove the age property. |
4 | Set complex address property. |
5 | Set a map of values, which removes the previously existing map and replaces the values with the given ones. |
6 | Automatically update the server expiration time when altering Time To Live. |
Updating complex objects as well as map (or other collection) structures requires further interaction with Redis to determine existing values, which means that rewriting the entire entity might be faster. |
7.8. Queries and Query Methods
Query methods allow automatic derivation of simple finder queries from the method name, as shown in the following example:
public interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByFirstname(String firstname);
}
Please make sure properties used in finder methods are set up for indexing. |
Query methods for Redis repositories support only queries for entities and collections of entities with paging. |
Using derived query methods might not always be sufficient to model the queries to execute. RedisCallback
offers more control over the actual matching of index structures or even custom indexes. To do so, provide a RedisCallback
that returns a single or Iterable
set of id
values, as shown in the following example:
String user = //...
List<RedisSession> sessionsByUser = template.find(new RedisCallback<Set<byte[]>>() {
public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
return connection
.sMembers("sessions:securityContext.authentication.principal.username:" + user);
}}, RedisSession.class);
The following table provides an overview of the keywords supported for Redis and what a method containing that keyword essentially translates to:
Keyword | Sample | Redis snippet |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
7.9. Redis Repositories Running on a Cluster
You can use the Redis repository support in a clustered Redis environment. See the “Redis Cluster” section for ConnectionFactory
configuration details. Still, some additional configuration must be done, because the default key distribution spreads entities and secondary indexes through out the whole cluster and its slots.
The following table shows the details of data on a cluster (based on previous examples):
Key | Type | Slot | Node |
---|---|---|---|
people:e2c7dcee-b8cd-4424-883e-736ce564363e |
id for hash |
15171 |
127.0.0.1:7381 |
people:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56 |
id for hash |
7373 |
127.0.0.1:7380 |
people:firstname:rand |
index |
1700 |
127.0.0.1:7379 |
Some commands (such as SINTER
and SUNION
) can only be processed on the server side when all involved keys map to the same slot. Otherwise, computation has to be done on client side. Therefore, it is useful to pin keyspaces to a single slot, which lets make use of Redis server side computation right away. The following table shows what happens when you do (note the change in the slot column and the port value in the node column):
Key | Type | Slot | Node |
---|---|---|---|
{people}:e2c7dcee-b8cd-4424-883e-736ce564363e |
id for hash |
2399 |
127.0.0.1:7379 |
{people}:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56 |
id for hash |
2399 |
127.0.0.1:7379 |
{people}:firstname:rand |
index |
2399 |
127.0.0.1:7379 |
Define and pin keyspaces by using @RedisHash("{yourkeyspace}") to specific slots when you use Redis cluster.
|
7.10. CDI Integration
Instances of the repository interfaces are usually created by a container, for which Spring is the most natural choice when working with Spring Data. Spring offers sophisticated for creating bean instances. Spring Data Redis ships with a custom CDI extension that lets you use the repository abstraction in CDI environments. The extension is part of the JAR, so, to activate it, drop the Spring Data Redis JAR into your classpath.
You can then set up the infrastructure by implementing a CDI Producer for the RedisConnectionFactory
and RedisOperations
, as shown in the following example:
class RedisOperationsProducer {
@Produces
RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("localhost");
jedisConnectionFactory.setPort(6379);
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
void disposeRedisConnectionFactory(@Disposes RedisConnectionFactory redisConnectionFactory) throws Exception {
if (redisConnectionFactory instanceof DisposableBean) {
((DisposableBean) redisConnectionFactory).destroy();
}
}
@Produces
@ApplicationScoped
RedisOperations<byte[], byte[]> redisOperationsProducer(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
}
The necessary setup can vary, depending on your JavaEE environment.
The Spring Data Redis CDI extension picks up all available repositories as CDI beans and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container. Thus, obtaining an instance of a Spring Data repository is a matter of declaring an @Injected
property, as shown in the following example:
class RepositoryClient {
@Inject
PersonRepository repository;
public void businessMethod() {
List<Person> people = repository.findAll();
}
}
A Redis Repository requires RedisKeyValueAdapter
and RedisKeyValueTemplate
instances. These beans are created and managed by the Spring Data CDI extension if no provided beans are found. You can, however, supply your own beans to configure the specific properties of RedisKeyValueAdapter
and RedisKeyValueTemplate
.
7.11. Redis Repositories Anatomy
Redis as a store itself offers a very narrow low-level API leaving higher level functions, such as secondary indexes and query operations, up to the user.
This section provides a more detailed view of commands issued by the repository abstraction for a better understanding of potential performance implications.
Consider the following entity class as the starting point for all operations:
@RedisHash("people")
public class Person {
@Id String id;
@Indexed String firstname;
String lastname;
Address hometown;
}
public class Address {
@GeoIndexed Point location;
}
7.11.1. Insert new
repository.save(new Person("rand", "al'thor"));
HMSET "people:19315449-cda2-4f5c-b696-9cb8018fa1f9" "_class" "Person" "id" "19315449-cda2-4f5c-b696-9cb8018fa1f9" "firstname" "rand" "lastname" "al'thor" (1)
SADD "people" "19315449-cda2-4f5c-b696-9cb8018fa1f9" (2)
SADD "people:firstname:rand" "19315449-cda2-4f5c-b696-9cb8018fa1f9" (3)
SADD "people:19315449-cda2-4f5c-b696-9cb8018fa1f9:idx" "people:firstname:rand" (4)
1 | Save the flattened entry as hash. |
2 | Add the key of the hash written in <1> to the helper index of entities in the same keyspace. |
3 | Add the key of the hash written in <2> to the secondary index of firstnames with the properties value. |
4 | Add the index of <3> to the set of helper structures for entry to keep track of indexes to clean on delete/update. |
7.11.2. Replace existing
repository.save(new Person("e82908cf-e7d3-47c2-9eec-b4e0967ad0c9", "Dragon Reborn", "al'thor"));
DEL "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" (1)
HMSET "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" "_class" "Person" "id" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" "firstname" "Dragon Reborn" "lastname" "al'thor" (2)
SADD "people" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" (3)
SMEMBERS "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9:idx" (4)
TYPE "people:firstname:rand" (5)
SREM "people:firstname:rand" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" (6)
DEL "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9:idx" (7)
SADD "people:firstname:Dragon Reborn" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" (8)
SADD "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9:idx" "people:firstname:Dragon Reborn" (9)
1 | Remove the existing hash to avoid leftovers of hash keys potentially no longer present. |
2 | Save the flattened entry as hash. |
3 | Add the key of the hash written in <1> to the helper index of entities in the same keyspace. |
4 | Get existing index structures that might need to be updated. |
5 | Check if the index exists and what type it is (text, geo, …). |
6 | Remove a potentially existing key from the index. |
7 | Remove the helper holding index information. |
8 | Add the key of the hash added in <2> to the secondary index of firstnames with the properties value. |
9 | Add the index of <6> to the set of helper structures for entry to keep track of indexes to clean on delete/update. |
7.11.3. Save Geo Data
Geo indexes follow the same rules as normal text based ones but use geo structure to store values. Saving an entity that uses a Geo-indexed property results in the following commands:
GEOADD "people:hometown:location" "13.361389" "38.115556" "76900e94-b057-44bc-abcf-8126d51a621b" (1)
SADD "people:76900e94-b057-44bc-abcf-8126d51a621b:idx" "people:hometown:location" (2)
1 | Add the key of the saved entry to the the geo index. |
2 | Keep track of the index structure. |
7.11.4. Find using simple index
repository.findByFirstname("egwene");
SINTER "people:firstname:egwene" (1)
HGETALL "people:d70091b5-0b9a-4c0a-9551-519e61bc9ef3" (2)
HGETALL ...
1 | Fetch keys contained in the secondary index. |
2 | Fetch each key returned by <1> individually. |
7.11.5. Find using Geo Index
repository.findByHometownLocationNear(new Point(15, 37), new Distance(200, KILOMETERS));
GEORADIUS "people:hometown:location" "15.0" "37.0" "200.0" "km" (1)
HGETALL "people:76900e94-b057-44bc-abcf-8126d51a621b" (2)
HGETALL ...
1 | Fetch keys contained in the secondary index. |
2 | Fetch each key returned by <1> individually. |
Appendixes
Appendix Document Structure
The appendix contains various additional detail that complements the information in the rest of the reference documentation:
-
“Schema” defines the schemas provided by Spring Data Redis.
-
“Command Reference” details which commands are supported by
RedisTemplate
.
Appendix B: Command Reference
Supported Commands
Command | Template Support |
---|---|
APPEND |
X |
AUTH |
X |
BGREWRITEAOF |
X |
BGSAVE |
X |
BITCOUNT |
X |
BITOP |
X |
BLPOP |
X |
BRPOP |
X |
BRPOPLPUSH |
X |
CLIENT KILL |
X |
CLIENT GETNAME |
X |
CLIENT LIST |
X |
CLIENT SETNAME |
X |
CLUSTER SLOTS |
- |
COMMAND |
- |
COMMAND COUNT |
- |
COMMAND GETKEYS |
- |
COMMAND INFO |
- |
CONFIG GET |
X |
CONFIG RESETSTAT |
X |
CONFIG REWRITE |
- |
CONFIG SET |
X |
DBSIZE |
X |
DEBUG OBJECT |
- |
DEBUG SEGFAULT |
- |
DECR |
X |
DECRBY |
X |
DEL |
X |
DISCARD |
X |
DUMP |
X |
ECHO |
X |
EVAL |
X |
EVALSHA |
X |
EXEC |
X |
EXISTS |
X |
EXPIRE |
X |
EXPIREAT |
X |
FLUSHALL |
X |
FLUSHDB |
X |
GET |
X |
GETBIT |
X |
GETRANGE |
X |
GETSET |
X |
HDEL |
X |
HEXISTS |
X |
HGET |
X |
HGETALL |
X |
HINCRBY |
X |
HINCRBYFLOAT |
X |
HKEYS |
X |
HLEN |
X |
HMGET |
X |
HMSET |
X |
HSCAN |
X |
HSET |
X |
HSETNX |
X |
HVALS |
X |
INCR |
X |
INCRBY |
X |
INCRBYFLOAT |
X |
INFO |
X |
KEYS |
X |
LASTSAVE |
X |
LINDEX |
X |
LINSERT |
X |
LLEN |
X |
LPOP |
X |
LPUSH |
X |
LPUSHX |
X |
LRANGE |
X |
LREM |
X |
LSET |
X |
LTRIM |
X |
MGET |
X |
MIGRATE |
- |
MONITOR |
- |
MOVE |
X |
MSET |
X |
MSETNX |
X |
MULTI |
X |
OBJECT |
- |
PERSIST |
X |
PEXIPRE |
X |
PEXPIREAT |
X |
PFADD |
X |
PFCOUNT |
X |
PFMERGE |
X |
PING |
X |
PSETEX |
X |
PSUBSCRIBE |
X |
PTTL |
X |
PUBLISH |
X |
PUBSUB |
- |
PUBSUBSCRIBE |
- |
QUIT |
X |
RANDOMKEY |
X |
RENAME |
X |
RENAMENX |
X |
RESTORE |
X |
ROLE |
- |
RPOP |
X |
RPOPLPUSH |
X |
RPUSH |
X |
RPUSHX |
X |
SADD |
X |
SAVE |
X |
SCAN |
X |
SCARD |
X |
SCRIPT EXITS |
X |
SCRIPT FLUSH |
X |
SCRIPT KILL |
X |
SCRIPT LOAD |
X |
SDIFF |
X |
SDIFFSTORE |
X |
SELECT |
X |
SENTINEL FAILOVER |
X |
SENTINEL GET-MASTER-ADD-BY-NAME |
- |
SENTINEL MASTER |
- |
SENTINEL MASTERS |
X |
SENTINEL MONITOR |
X |
SENTINEL REMOVE |
X |
SENTINEL RESET |
- |
SENTINEL SET |
- |
SENTINEL SLAVES |
X |
SET |
X |
SETBIT |
X |
SETEX |
X |
SETNX |
X |
SETRANGE |
X |
SHUTDOWN |
X |
SINTER |
X |
SINTERSTORE |
X |
SISMEMBER |
X |
SLAVEOF |
X |
SLOWLOG |
- |
SMEMBERS |
X |
SMOVE |
X |
SORT |
X |
SPOP |
X |
SRANDMEMBER |
X |
SREM |
X |
SSCAN |
X |
STRLEN |
X |
SUBSCRIBE |
X |
SUNION |
X |
SUNIONSTORE |
X |
SYNC |
- |
TIME |
X |
TTL |
X |
TYPE |
X |
UNSUBSCRIBE |
X |
UNWATCH |
X |
WATCH |
X |
ZADD |
X |
ZCARD |
X |
ZCOUNT |
X |
ZINCRBY |
X |
ZINTERSTORE |
X |
ZLEXCOUNT |
- |
ZRANGE |
X |
ZRANGEBYLEX |
- |
ZREVRANGEBYLEX |
- |
ZRANGEBYSCORE |
X |
ZRANK |
X |
ZREM |
X |
ZREMRANGEBYLEX |
- |
ZREMRANGEBYRANK |
X |
ZREVRANGE |
X |
ZREVRANGEBYSCORE |
X |
ZREVRANK |
X |
ZSCAN |
X |
ZSCORE |
X |
ZUNINONSTORE |
X |