Skip to content

Jackson3 based HashMapper and RedisSerializer #3168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.x-GH-3154-SNAPSHOT</version>

<name>Spring Data Redis</name>
<description>Spring Data module for Redis</description>
Expand Down Expand Up @@ -148,6 +148,21 @@
<optional>true</optional>
</dependency>

<!-- jackson 3 -->
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.0-rc5</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>3.0-rc5</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
Expand Down
87 changes: 85 additions & 2 deletions src/main/antora/modules/ROOT/pages/redis/hash-mappers.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[[redis.hashmappers.root]]
= Hash Mapping

Data can be stored by using various data structures within Redis. javadoc:org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer[] can convert objects in https://en.wikipedia.org/wiki/JSON[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):
Data can be stored by using various data structures within Redis. javadoc:org.springframework.data.redis.serializer.Jackson3JsonRedisSerializer[] can convert objects in https://en.wikipedia.org/wiki/JSON[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 javadoc:org.springframework.data.redis.core.HashOperations[] and a xref:redis.adoc#redis:serializer[serializer]
* Using xref:repositories.adoc[Redis Repositories]
Expand All @@ -16,7 +16,8 @@ Multiple implementations are available:

* javadoc:org.springframework.data.redis.hash.BeanUtilsHashMapper[] using Spring's {spring-framework-javadoc}/org/springframework/beans/BeanUtils.html[BeanUtils].
* javadoc:org.springframework.data.redis.hash.ObjectHashMapper[] using xref:redis/redis-repositories/mapping.adoc[Object-to-Hash Mapping].
* <<redis.hashmappers.jackson2,`Jackson2HashMapper`>> using https://github.com/FasterXML/jackson[FasterXML Jackson].
* <<redis.hashmappers.jackson3,`Jackson3HashMapper`>> using https://github.com/FasterXML/jackson[FasterXML Jackson 3].
* <<redis.hashmappers.jackson2,`Jackson2HashMapper`>> (deprecated) using https://github.com/FasterXML/jackson[FasterXML Jackson 2].

The following example shows one way to implement hash mapping:

Expand Down Expand Up @@ -50,9 +51,91 @@ public class HashMapping {
}
----

[[redis.hashmappers.jackson3]]
=== Jackson3HashMapper

javadoc:org.springframework.data.redis.hash.Jackson3HashMapper[] provides Redis Hash mapping for domain objects by using https://github.com/FasterXML/jackson[FasterXML Jackson 3].
`Jackson3HashMapper` 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:

[source,java]
----
public class Person {
String firstname;
String lastname;
Address address;
Date date;
LocalDateTime localDateTime;
}

public class Address {
String city;
String country;
}
----

The following table shows how the data in the preceding class would appear in normal mapping:

.Normal Mapping
[width="80%",cols="<1,<2",options="header"]
|====
|Hash Field
|Value

|firstname
|`Jon`

|lastname
|`Snow`

|address
|`{ "city" : "Castle Black", "country" : "The North" }`

|date
|1561543964015

|localDateTime
|`2018-01-02T12:13:14`
|====

The following table shows how the data in the preceding class would appear in flat mapping:

.Flat Mapping
[width="80%",cols="<1,<2",options="header"]
|====
|Hash Field
|Value

|firstname
|`Jon`

|lastname
|`Snow`

|address.city
|`Castle Black`

|address.country
|`The North`

|date
|1561543964015

|localDateTime
|`2018-01-02T12:13:14`
|====

NOTE: 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.

[[redis.hashmappers.jackson2]]
=== Jackson2HashMapper

WARNING: Jackson 2 based implementations have been deprecated and are subject to removal in a subsequent release.

javadoc:org.springframework.data.redis.hash.Jackson2HashMapper[] provides Redis Hash mapping for domain objects by using https://github.com/FasterXML/jackson[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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ The following example shows two sample byte array converters:
@WritingConverter
public class AddressToBytesConverter implements Converter<Address, byte[]> {

private final Jackson2JsonRedisSerializer<Address> serializer;
private final Jackson3JsonRedisSerializer<Address> serializer;

public AddressToBytesConverter() {

serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
serializer = new Jackson3JsonRedisSerializer<Address>(Address.class);
serializer.setObjectMapper(new ObjectMapper());
}

Expand All @@ -109,11 +109,11 @@ public class AddressToBytesConverter implements Converter<Address, byte[]> {
@ReadingConverter
public class BytesToAddressConverter implements Converter<byte[], Address> {

private final Jackson2JsonRedisSerializer<Address> serializer;
private final Jackson3JsonRedisSerializer<Address> serializer;

public BytesToAddressConverter() {

serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
serializer = new Jackson3JsonRedisSerializer<Address>(Address.class);
serializer.setObjectMapper(new ObjectMapper());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ You may provide a `HashMapper` suitable for your requirements when obtaining `St
[source,java]
----
redisTemplate()
.opsForStream(new Jackson2HashMapper(true))
.opsForStream(new Jackson3HashMapper(true))
.add(record); <1>
----
<1> XADD user-logon * "firstname" "night" "@class" "com.example.User" "lastname" "angel"
Expand Down
2 changes: 1 addition & 1 deletion src/main/antora/modules/ROOT/pages/redis/template.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ Multiple implementations are available (including two that have been already men
* javadoc:org.springframework.data.redis.serializer.JdkSerializationRedisSerializer[], which is used by default for javadoc:org.springframework.data.redis.cache.RedisCache[] and javadoc:org.springframework.data.redis.core.RedisTemplate[].
* the `StringRedisSerializer`.

However, one can use `OxmSerializer` for Object/XML mapping through Spring {spring-framework-docs}/data-access.html#oxm[OXM] support or javadoc:org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer[] or javadoc:org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer[] for storing data in https://en.wikipedia.org/wiki/JSON[JSON] format.
However, one can use `OxmSerializer` for Object/XML mapping through Spring {spring-framework-docs}/data-access.html#oxm[OXM] support or javadoc:org.springframework.data.redis.serializer.Jackson3JsonRedisSerializer[] or javadoc:org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer[] for storing data in https://en.wikipedia.org/wiki/JSON[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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,10 @@ default Mono<Long> touch(Collection<ByteBuffer> keys) {
Flux<NumericResponse<Collection<ByteBuffer>, Long>> touch(Publisher<Collection<ByteBuffer>> keys);

/**
* Find all keys matching the given {@literal pattern}.<br />
* It is recommended to use {@link #scan(ScanOptions)} to iterate over the keyspace as {@link #keys(ByteBuffer)} is a
* non-interruptible and expensive Redis operation.
* Retrieve all keys matching the given pattern via {@code KEYS} command.
* <p>
* <strong>IMPORTANT:</strong> This command is non-interruptible and scans the entire keyspace which may cause
* performance issues. Consider {@link #scan(ScanOptions)} for large datasets.
*
* @param pattern must not be {@literal null}.
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ default Boolean exists(byte[] key) {
Long touch(byte[]... keys);

/**
* Find all keys matching the given {@code pattern}.
* Retrieve all keys matching the given pattern.
* <p>
* <strong>IMPORTANT:</strong> The {@literal KEYS} command is non-interruptible and scans the entire keyspace which
* may cause performance issues. Consider {@link #scan(ScanOptions)} for large datasets.
*
* @param pattern must not be {@literal null}.
* @return empty {@link Set} if no match found. {@literal null} when used in pipeline / transaction.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ interface StringTuple extends Tuple {
Long touch(String... keys);

/**
* Find all keys matching the given {@code pattern}.
* Retrieve all keys matching the given pattern via {@code KEYS} command.
* <p>
* <strong>IMPORTANT:</strong> This command is non-interruptible and scans the entire keyspace which may cause
* performance issues. Consider {@link #scan(ScanOptions)} for large datasets.
*
* @param pattern must not be {@literal null}.
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
public interface ClusterOperations<K, V> {

/**
* Get all keys located at given node.
* Retrieve all keys located at given node matching the given pattern.
* <p>
* <strong>IMPORTANT:</strong> The {@literal KEYS} command is non-interruptible and scans the entire keyspace which
* may cause performance issues.
*
* @param node must not be {@literal null}.
* @param pattern
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,10 @@ default Mono<Flux<? extends Message<String, V>>> listenToPatternLater(String...
Mono<DataType> type(K key);

/**
* Find all keys matching the given {@code pattern}. <br />
* <strong>IMPORTANT:</strong> It is recommended to use {@link #scan()} to iterate over the keyspace as
* {@link #keys(Object)} is a non-interruptible and expensive Redis operation.
* Retrieve all keys matching the given pattern via {@code KEYS} command.
* <p>
* <strong>IMPORTANT:</strong> This command is non-interruptible and scans the entire keyspace which may cause
* performance issues. Consider {@link #scan(ScanOptions)} for large datasets.
*
* @param pattern must not be {@literal null}.
* @return the {@link Flux} emitting matching keys one by one.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,14 @@ <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSer
DataType type(K key);

/**
* Find all keys matching the given {@code pattern}.
*
* @param pattern must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/keys">Redis Documentation: KEYS</a>
* Retrieve all keys matching the given pattern via {@code KEYS} command.
* <p>
* <strong>IMPORTANT:</strong> This command is non-interruptible and scans the entire keyspace which may cause
* performance issues. Consider {@link #scan(ScanOptions)} for large datasets.
*
* @param pattern key pattern
* @return set of matching keys, or {@literal null} when used in pipeline / transaction
* @see <a href="https://redis.io/commands/keys">Redis KEYS command</a>
*/
@Nullable
Set<K> keys(K pattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@
* @author Mark Paluch
* @author John Blum
* @since 1.8
* @deprecated since 4.0
*/
@Deprecated(since = "4.0", forRemoval = true)
public class Jackson2HashMapper implements HashMapper<Object, String, Object> {

private static final boolean SOURCE_VERSION_PRESENT =
Expand Down
Loading