Skip to content
Closed
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
2 changes: 1 addition & 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.0-GH-3219-SNAPSHOT</version>

<name>Spring Data Redis</name>
<description>Spring Data module for Redis</description>
Expand Down
13 changes: 8 additions & 5 deletions src/main/antora/modules/ROOT/pages/redis/hash-mappers.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[[redis.hashmappers.root]]
= Hash Mapping

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):
Data can be stored by using various data structures within Redis. javadoc:org.springframework.data.redis.serializer.JacksonJsonRedisSerializer[] 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 +19,7 @@ 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.jackson3,`Jackson3HashMapper`>> using https://github.com/FasterXML/jackson[FasterXML Jackson 3].
* <<redis.hashmappers.jackson3,`JacksonHashMapper`>> 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 @@ -52,10 +55,10 @@ public class HashMapping {
----

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

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

public AddressToBytesConverter() {

serializer = new Jackson3JsonRedisSerializer<Address>(Address.class);
serializer = new JacksonJsonRedisSerializer<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 Jackson3JsonRedisSerializer<Address> serializer;
private final JacksonJsonRedisSerializer<Address> serializer;

public BytesToAddressConverter() {

serializer = new Jackson3JsonRedisSerializer<Address>(Address.class);
serializer = new JacksonJsonRedisSerializer<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 Jackson3HashMapper(true))
.opsForStream(new JacksonHashMapper(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.Jackson3JsonRedisSerializer[] or javadoc:org.springframework.data.redis.serializer.GenericJackson3JsonRedisSerializer[] 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.JacksonJsonRedisSerializer[] or javadoc:org.springframework.data.redis.serializer.GenericJacksonJsonRedisSerializer[] 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 @@ -144,7 +144,7 @@
* @author Mark Paluch
* @author John Blum
* @since 1.8
* @deprecated since 4.0 in favor of {@link org.springframework.data.redis.hash.Jackson3HashMapper}.
* @deprecated since 4.0 in favor of {@link JacksonHashMapper}.
*/
@Deprecated(since = "4.0", forRemoval = true)
public class Jackson2HashMapper implements HashMapper<Object, String, Object> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,26 +160,26 @@
* @author Mark Paluch
* @since 4.0
*/
public class Jackson3HashMapper implements HashMapper<Object, String, Object> {
public class JacksonHashMapper implements HashMapper<Object, String, Object> {

private static final Lazy<Jackson3HashMapper> sharedFlattening = Lazy
.of(() -> create(Jackson3HashMapperBuilder::flatten));
private static final Lazy<Jackson3HashMapper> sharedHierarchical = Lazy
.of(() -> create(Jackson3HashMapperBuilder::hierarchical));
private static final Lazy<JacksonHashMapper> sharedFlattening = Lazy
.of(() -> create(JacksonHashMapperBuilder::flatten));
private static final Lazy<JacksonHashMapper> sharedHierarchical = Lazy
.of(() -> create(JacksonHashMapperBuilder::hierarchical));

private final ObjectMapper typingMapper;
private final ObjectMapper untypedMapper;
private final boolean flatten;

/**
* Creates a new {@link Jackson3HashMapper} initialized with a custom Jackson {@link ObjectMapper}.
* Creates a new {@link JacksonHashMapper} initialized with a custom Jackson {@link ObjectMapper}.
*
* @param mapper Jackson {@link ObjectMapper} used to de/serialize hashed {@link Object objects}; must not be
* {@literal null}.
* @param flatten boolean used to configure whether JSON de/serialized {@link Object} properties will be un/flattened
* using {@literal dot notation}, or whether to retain the hierarchical node structure created by Jackson.
*/
public Jackson3HashMapper(ObjectMapper mapper, boolean flatten) {
public JacksonHashMapper(ObjectMapper mapper, boolean flatten) {

Assert.notNull(mapper, "Mapper must not be null");

Expand All @@ -189,63 +189,61 @@ public Jackson3HashMapper(ObjectMapper mapper, boolean flatten) {
}

/**
* Returns a flattening {@link Jackson3HashMapper} using {@literal dot notation} for properties.
* Returns a flattening {@link JacksonHashMapper} using {@literal dot notation} for properties.
*
* @return a flattening {@link Jackson3HashMapper} instance.
* @return a flattening {@link JacksonHashMapper} instance.
*/
public static Jackson3HashMapper flattening() {
public static JacksonHashMapper flattening() {
return sharedFlattening.get();
}

/**
* Returns a {@link Jackson3HashMapper} retain the hierarchical node structure created by Jackson.
* Returns a {@link JacksonHashMapper} retain the hierarchical node structure created by Jackson.
*
* @return a hierarchical {@link Jackson3HashMapper} instance.
* @return a hierarchical {@link JacksonHashMapper} instance.
*/
public static Jackson3HashMapper hierarchical() {
public static JacksonHashMapper hierarchical() {
return sharedHierarchical.get();
}

/**
* Creates a new {@link Jackson3HashMapper} allowing further configuration through {@code configurer}.
* Creates a new {@link JacksonHashMapper} allowing further configuration through {@code configurer}.
*
* @param configurer the configurer for {@link Jackson3HashMapperBuilder}.
* @return a new {@link Jackson3HashMapper} instance.
* @param configurer the configurer for {@link JacksonHashMapperBuilder}.
* @return a new {@link JacksonHashMapper} instance.
*/
public static Jackson3HashMapper create(
Consumer<Jackson3HashMapperBuilder<JsonMapper.Builder>> configurer) {
public static JacksonHashMapper create(Consumer<JacksonHashMapperBuilder<JsonMapper.Builder>> configurer) {

Assert.notNull(configurer, "Builder configurer must not be null");

Jackson3HashMapperBuilder<JsonMapper.Builder> builder = builder();
JacksonHashMapperBuilder<JsonMapper.Builder> builder = builder();
configurer.accept(builder);

return builder.build();
}

/**
* Creates a {@link Jackson3HashMapperBuilder} to build a {@link Jackson3HashMapper} instance using
* {@link JsonMapper}.
* Creates a {@link JacksonHashMapperBuilder} to build a {@link JacksonHashMapper} instance using {@link JsonMapper}.
*
* @return a {@link Jackson3HashMapperBuilder} to build a {@link Jackson3HashMapper} instance.
* @return a {@link JacksonHashMapperBuilder} to build a {@link JacksonHashMapper} instance.
*/
public static Jackson3HashMapperBuilder<JsonMapper.Builder> builder() {
public static JacksonHashMapperBuilder<JsonMapper.Builder> builder() {
return builder(JsonMapper::builder);
}

/**
* Creates a new {@link Jackson3HashMapperBuilder} to configure and build a {@link Jackson3HashMapper}.
* Creates a new {@link JacksonHashMapperBuilder} to configure and build a {@link JacksonHashMapper}.
*
* @param builderFactory factory to create a {@link MapperBuilder} for the {@link ObjectMapper}.
* @param <B> type of the {@link MapperBuilder} to use.
* @return a new {@link Jackson3HashMapperBuilder}.
* @return a new {@link JacksonHashMapperBuilder}.
*/
public static <B extends MapperBuilder<? extends ObjectMapper, ? extends MapperBuilder<?, ?>>> Jackson3HashMapperBuilder<B> builder(
public static <B extends MapperBuilder<? extends ObjectMapper, ? extends MapperBuilder<?, ?>>> JacksonHashMapperBuilder<B> builder(
Supplier<B> builderFactory) {

Assert.notNull(builderFactory, "MapperBuilder Factory must not be null");

return new Jackson3HashMapperBuilder<>(builderFactory);
return new JacksonHashMapperBuilder<>(builderFactory);
}

/**
Expand All @@ -272,7 +270,7 @@ public static void preconfigure(MapperBuilder<? extends ObjectMapper, ? extends
public Map<String, Object> toHash(@Nullable Object source) {

JsonNode tree = this.typingMapper.valueToTree(source);
return this.flatten ? FlatEric.flatten(Jackson3AdapterFactory.INSTANCE, tree.properties())
return this.flatten ? FlatEric.flatten(JacksonAdapterFactory.INSTANCE, tree.properties())
: this.untypedMapper.convertValue(tree, Map.class);
}

Expand All @@ -299,11 +297,11 @@ public Object fromHash(Map<String, Object> hash) {


/**
* Builder to create a {@link Jackson3HashMapper} instance.
* Builder to create a {@link JacksonHashMapper} instance.
*
* @param <B> type of the {@link MapperBuilder}.
*/
public static class Jackson3HashMapperBuilder<B extends MapperBuilder<? extends ObjectMapper, ? extends MapperBuilder<?, ?>>> {
public static class JacksonHashMapperBuilder<B extends MapperBuilder<? extends ObjectMapper, ? extends MapperBuilder<?, ?>>> {

private final Supplier<B> builderFactory;

Expand All @@ -313,7 +311,7 @@ public static class Jackson3HashMapperBuilder<B extends MapperBuilder<? extends
private boolean flatten = false;
private Consumer<B> mapperBuilderCustomizer = builder -> {};

private Jackson3HashMapperBuilder(Supplier<B> builderFactory) {
private JacksonHashMapperBuilder(Supplier<B> builderFactory) {
this.builderFactory = builderFactory;
}

Expand All @@ -323,7 +321,7 @@ private Jackson3HashMapperBuilder(Supplier<B> builderFactory) {
* @return {@code this} builder.
*/
@Contract("-> this")
public Jackson3HashMapperBuilder<B> flatten() {
public JacksonHashMapperBuilder<B> flatten() {
return flatten(true);
}

Expand All @@ -333,7 +331,7 @@ public Jackson3HashMapperBuilder<B> flatten() {
* @return {@code this} builder.
*/
@Contract("-> this")
public Jackson3HashMapperBuilder<B> hierarchical() {
public JacksonHashMapperBuilder<B> hierarchical() {
return flatten(false);
}

Expand All @@ -347,7 +345,7 @@ public Jackson3HashMapperBuilder<B> hierarchical() {
* @return {@code this} builder.
*/
@Contract("_ -> this")
public Jackson3HashMapperBuilder<B> flatten(boolean flatten) {
public JacksonHashMapperBuilder<B> flatten(boolean flatten) {
this.flatten = flatten;
return this;
}
Expand All @@ -359,7 +357,7 @@ public Jackson3HashMapperBuilder<B> flatten(boolean flatten) {
* @return {@code this} builder.
*/
@Contract("-> this")
public Jackson3HashMapperBuilder<B> jackson2CompatibilityMode() {
public JacksonHashMapperBuilder<B> jackson2CompatibilityMode() {
this.jackson2CompatibilityMode = true;
return this;
}
Expand All @@ -371,7 +369,7 @@ public Jackson3HashMapperBuilder<B> jackson2CompatibilityMode() {
* @return {@code this} builder.
*/
@Contract("_ -> this")
public Jackson3HashMapperBuilder<B> typeValidator(PolymorphicTypeValidator typeValidator) {
public JacksonHashMapperBuilder<B> typeValidator(PolymorphicTypeValidator typeValidator) {

Assert.notNull(typeValidator, "Type validator must not be null");

Expand All @@ -386,7 +384,7 @@ public Jackson3HashMapperBuilder<B> typeValidator(PolymorphicTypeValidator typeV
* @return {@code this} builder.
*/
@Contract("_ -> this")
public Jackson3HashMapperBuilder<B> customize(Consumer<B> mapperBuilderCustomizer) {
public JacksonHashMapperBuilder<B> customize(Consumer<B> mapperBuilderCustomizer) {

Assert.notNull(mapperBuilderCustomizer, "JSON mapper customizer must not be null");

Expand All @@ -395,12 +393,12 @@ public Jackson3HashMapperBuilder<B> customize(Consumer<B> mapperBuilderCustomize
}

/**
* Build a new {@link Jackson3HashMapper} instance with the configured settings.
* Build a new {@link JacksonHashMapper} instance with the configured settings.
*
* @return a new {@link Jackson3HashMapper} instance.
* @return a new {@link JacksonHashMapper} instance.
*/
@Contract("-> new")
public Jackson3HashMapper build() {
public JacksonHashMapper build() {

B mapperBuilder = builderFactory.get();

Expand All @@ -409,7 +407,7 @@ public Jackson3HashMapper build() {

mapperBuilderCustomizer.accept(mapperBuilder);

return new Jackson3HashMapper(mapperBuilder.build(), flatten);
return new JacksonHashMapper(mapperBuilder.build(), flatten);
}

private static TypeResolverBuilder<?> getDefaultTyping(PolymorphicTypeValidator typeValidator, boolean flatten,
Expand Down Expand Up @@ -564,14 +562,14 @@ public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws Ja
}
}

private enum Jackson3AdapterFactory implements FlatEric.JsonNodeAdapterFactory {
private enum JacksonAdapterFactory implements FlatEric.JsonNodeAdapterFactory {

INSTANCE;


@Override
public FlatEric.JsonNodeAdapter adapt(Object node) {
return node instanceof FlatEric.JsonNodeAdapter na ? na : new Jackson3JsonNodeAdapter((JsonNode) node);
return node instanceof FlatEric.JsonNodeAdapter na ? na : new JacksonJsonNodeAdapter((JsonNode) node);
}

@Override
Expand All @@ -580,7 +578,7 @@ public boolean isJsonNode(Object value) {
}
}

private record Jackson3JsonNodeAdapter(JsonNode node) implements FlatEric.JsonNodeAdapter {
private record JacksonJsonNodeAdapter(JsonNode node) implements FlatEric.JsonNodeAdapter {

@Override
public FlatEric.JsonNodeType getNodeType() {
Expand All @@ -594,7 +592,7 @@ public boolean isArray() {

@Override
public Collection<? extends FlatEric.JsonNodeAdapter> values() {
return node().valueStream().map(Jackson3JsonNodeAdapter::new).toList();
return node().valueStream().map(JacksonJsonNodeAdapter::new).toList();
}

@Override
Expand Down Expand Up @@ -650,7 +648,7 @@ public boolean isObject() {
@Override
public Collection<Entry<String, FlatEric.JsonNodeAdapter>> properties() {
return node().propertyStream()
.map(it -> Map.entry(it.getKey(), (FlatEric.JsonNodeAdapter) new Jackson3JsonNodeAdapter(it.getValue())))
.map(it -> Map.entry(it.getKey(), (FlatEric.JsonNodeAdapter) new JacksonJsonNodeAdapter(it.getValue())))
.toList();
}

Expand Down
Loading