From 68bb20a391371b4c044549e57dd010371ddb643d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 2 May 2016 09:35:00 +0200 Subject: [PATCH 1/3] DATAREDIS-423 - Add support for Jackson2 based HashMapper. Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e6d868411f..3ad4cf32b2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAREDIS-423-SNAPSHOT Spring Data Redis From caa20981ac86ffb1e194704b3fbd827bbdbc6392 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 3 May 2016 12:01:29 +0200 Subject: [PATCH 2/3] DATAREDIS-423 - Add support for Jackson2 based HashMapper. Added FasterXML Jackson `ObjectMapper` based `HashMapper` implementation that allows flattening. ```java class Person { String firstname; String lastname; Address address; } class Address { String city; String country; } ``` ```bash ### NON Flat firstname:Jon lastname:Snow address:{ city : Castle Black, country : The North } ### Flat firstname:Jon lastname:Snow address.city:Castle Black address.country:The North ``` --- src/main/asciidoc/reference/redis.adoc | 1 + .../data/redis/hash/Jackson2HashMapper.java | 295 ++++++++++++++++++ .../mapping/Jackson2HashMapperTests.java | 99 ++++++ .../mapping/Jackson2HashMapperUnitTests.java | 204 ++++++++++++ 4 files changed, 599 insertions(+) create mode 100644 src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java create mode 100644 src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java create mode 100644 src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java diff --git a/src/main/asciidoc/reference/redis.adoc b/src/main/asciidoc/reference/redis.adoc index 52453487e3..769f5b398d 100644 --- a/src/main/asciidoc/reference/redis.adoc +++ b/src/main/asciidoc/reference/redis.adoc @@ -365,6 +365,7 @@ Multiple implementations are available out of the box: 1. `BeanUtilsHashMapper` using Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html[BeanUtils] 2. `ObjectHashMapper` using <> +3. `Jackson2HashMapper` using https://github.com/FasterXML/jackson[FasterXML Jackson]. [source,java] ---- diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java new file mode 100644 index 0000000000..052373b6bc --- /dev/null +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -0,0 +1,295 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.hash; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * {@link ObjectMapper} based {@link HashMapper} implementation that allows flattening. Given an entity {@code Person} + * with an {@code Address} like below the flattening will create individual hash entries for all nested properties. + * + *
+ * 
+ * class Person {
+ *   String firstname;
+ *   String lastname;
+ *   Address address;
+ * }
+ *
+ * class Address {
+ *   String city;
+ *   String country;
+ * }
+ * 
+ * 
+ * + *
+ * Normal
+ * + * + * + * + *
firstnameJon
lastnameSnow
address{ city : Castle Black, country : The North }
+ * + * Flat:
+ * + *   + * + * + * + *
firstnameJon
lastnameSnow
address.cityCastle Black
address.countryThe North
+ *
+ * + * @author Christoph Strobl + * @since 1.8 + */ +public class Jackson2HashMapper implements HashMapper { + + private final ObjectMapper typingMapper; + private final ObjectMapper untypedMapper; + private final boolean flatten; + + /** + * Creates new {@link Jackson2HashMapper} with default {@link ObjectMapper}. + * + * @param flatten + */ + public Jackson2HashMapper(boolean flatten) { + + this(new ObjectMapper(), flatten); + + typingMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); + typingMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); + typingMapper.setSerializationInclusion(Include.NON_NULL); + typingMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + /** + * Creates new {@link Jackson2HashMapper}. + * + * @param mapper must not be {@literal null}. + * @param flatten + */ + public Jackson2HashMapper(ObjectMapper mapper, boolean flatten) { + + Assert.notNull(mapper, "Mapper must not be null!"); + + this.typingMapper = mapper; + this.flatten = flatten; + + this.untypedMapper = new ObjectMapper(); + this.untypedMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); + this.untypedMapper.setSerializationInclusion(Include.NON_NULL); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.hash.HashMapper#toHash(java.lang.Object) + */ + @Override + @SuppressWarnings("unchecked") + public Map toHash(Object source) { + + JsonNode tree = typingMapper.valueToTree(source); + return flatten ? flattenMap(tree.fields()) : untypedMapper.convertValue(tree, Map.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.hash.HashMapper#fromHash(java.util.Map) + */ + @Override + public Object fromHash(Map hash) { + + try { + + if (flatten) { + + return typingMapper.reader().forType(Object.class) + .readValue(untypedMapper.writeValueAsBytes(doUnflatten(hash))); + } + + return typingMapper.treeToValue(untypedMapper.valueToTree(hash), Object.class); + + } catch (JsonParseException e) { + throw new MappingException(e.getMessage(), e); + } catch (JsonMappingException e) { + throw new MappingException(e.getMessage(), e); + } catch (IOException e) { + throw new MappingException(e.getMessage(), e); + } + } + + @SuppressWarnings("unchecked") + private Map doUnflatten(Map source) { + + Map result = new LinkedHashMap(); + Set treatSeperate = new LinkedHashSet(); + for (Entry entry : source.entrySet()) { + + String key = entry.getKey(); + String[] args = key.split("\\."); + + if (args.length == 1 && !args[0].contains("[")) { + result.put(entry.getKey(), entry.getValue()); + continue; + } + + if (args.length == 1 && args[0].contains("[")) { + + String prunedKey = args[0].substring(0, args[0].indexOf('[')); + if (result.containsKey(prunedKey)) { + appendValueToTypedList(args[0], entry.getValue(), (List) result.get(prunedKey)); + } else { + result.put(prunedKey, createTypedListWithValue(entry.getValue())); + } + } else { + treatSeperate.add(key.substring(0, key.indexOf('.'))); + } + } + + for (String partial : treatSeperate) { + + Map newSource = new LinkedHashMap(); + + for (Entry entry : source.entrySet()) { + if (entry.getKey().startsWith(partial)) { + newSource.put(entry.getKey().substring(partial.length() + 1), entry.getValue()); + } + } + + if (partial.endsWith("]")) { + + String prunedKey = partial.substring(0, partial.indexOf('[')); + + if (result.containsKey(prunedKey)) { + appendValueToTypedList(partial, doUnflatten(newSource), (List) result.get(prunedKey)); + } else { + result.put(prunedKey, createTypedListWithValue(doUnflatten(newSource))); + } + } else { + result.put(partial, doUnflatten(newSource)); + } + } + + return result; + } + + private Map flattenMap(Iterator> source) { + + Map resultMap = new HashMap(); + this.doFlatten("", source, resultMap); + return resultMap; + } + + private void doFlatten(String propertyPrefix, Iterator> inputMap, + Map resultMap) { + + if (StringUtils.hasText(propertyPrefix)) { + propertyPrefix = propertyPrefix + "."; + } + + while (inputMap.hasNext()) { + + Entry entry = inputMap.next(); + flattenElement(propertyPrefix + entry.getKey(), entry.getValue(), resultMap); + } + } + + private void flattenElement(String propertyPrefix, Object source, Map resultMap) { + + if (!(source instanceof JsonNode)) { + + resultMap.put(propertyPrefix, source); + return; + } + + JsonNode element = (JsonNode) source; + if (element.isArray()) { + + Iterator nodes = element.elements(); + + while (nodes.hasNext()) { + + JsonNode cur = nodes.next(); + if (cur.isArray()) { + this.falttenCollection(propertyPrefix, cur.elements(), resultMap); + } + } + + } else if (element.isContainerNode()) { + this.doFlatten(propertyPrefix, element.fields(), resultMap); + } else { + resultMap.put(propertyPrefix, new DirectFieldAccessFallbackBeanWrapper(element).getPropertyValue("_value")); + } + } + + private void falttenCollection(String propertyPrefix, Iterator list, Map resultMap) { + + int counter = 0; + while (list.hasNext()) { + JsonNode element = list.next(); + flattenElement(propertyPrefix + "[" + counter + "]", element, resultMap); + counter++; + } + } + + @SuppressWarnings("unchecked") + private void appendValueToTypedList(String key, Object value, List destination) { + + int index = Integer.valueOf(key.substring(key.indexOf('[') + 1, key.length() - 1)).intValue(); + List resultList = ((List) destination.get(1)); + if (resultList.size() < index) { + resultList.add(value); + } else { + resultList.add(index, value); + } + } + + private List createTypedListWithValue(Object value) { + + List listWithTypeHint = new ArrayList(); + listWithTypeHint.add(ArrayList.class.getName()); // why jackson? why? + List values = new ArrayList(); + values.add(value); + listWithTypeHint.add(values); + return listWithTypeHint; + } +} diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java new file mode 100644 index 0000000000..28678ab7be --- /dev/null +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java @@ -0,0 +1,99 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.mapping; + +import java.util.Arrays; +import java.util.Collection; + +import org.hamcrest.core.Is; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.redis.Address; +import org.springframework.data.redis.ConnectionFactoryTracker; +import org.springframework.data.redis.Person; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.hash.Jackson2HashMapper; + +/** + * Integration tests for {@link Jackson2HashMapper}. + * + * @author Christoph Strobl + */ +@RunWith(Parameterized.class) +public class Jackson2HashMapperTests { + + RedisTemplate template; + RedisConnectionFactory factory; + Jackson2HashMapper mapper; + + public Jackson2HashMapperTests(RedisConnectionFactory factory) throws Exception { + + this.factory = factory; + if (factory instanceof InitializingBean) { + ((InitializingBean) factory).afterPropertiesSet(); + } + + ConnectionFactoryTracker.add(factory); + } + + @Parameters + public static Collection params() { + return Arrays. asList(new JedisConnectionFactory(), new LettuceConnectionFactory()); + } + + @AfterClass + public static void cleanUp() { + ConnectionFactoryTracker.cleanUp(); + } + + @Before + public void setUp() { + + this.template = new RedisTemplate(); + this.template.setConnectionFactory(factory); + template.afterPropertiesSet(); + + this.mapper = new Jackson2HashMapper(true); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldWriteReadHashCorrtectly() { + + Person jon = new Person("jon", "snow", 19); + Address adr = new Address(); + adr.setStreet("the wall"); + adr.setNumber(100); + jon.setAddress(adr); + + template.opsForHash().putAll("JON-SNOW", mapper.toHash(jon)); + + Person result = (Person) mapper.fromHash(template. opsForHash().entries("JON-SNOW")); + Assert.assertThat(result, Is.is(jon)); + } + +} diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java new file mode 100644 index 0000000000..f54583c99b --- /dev/null +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java @@ -0,0 +1,204 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.mapping; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.springframework.data.redis.Address; +import org.springframework.data.redis.Person; +import org.springframework.data.redis.hash.HashMapper; +import org.springframework.data.redis.hash.Jackson2HashMapper; + +import lombok.Data; + +/** + * Unit tests for {@link Jackson2HashMapper}. + * + * @author Christoph Strobl + */ +@RunWith(Parameterized.class) +public class Jackson2HashMapperUnitTests extends AbstractHashMapperTest { + + private final Jackson2HashMapper mapper; + + public Jackson2HashMapperUnitTests(Jackson2HashMapper mapper) { + this.mapper = mapper; + } + + @Parameters + public static Collection params() { + return Arrays.asList(new Jackson2HashMapper(true), new Jackson2HashMapper(false)); + } + + @Override + @SuppressWarnings("rawtypes") + protected HashMapper mapperFor(Class t) { + return this.mapper; + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapTypedListOfSimpleType() { + + WithList source = new WithList(); + source.strings = Arrays.asList("spring", "data", "redis"); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapTypledListOfComplexType() { + + WithList source = new WithList(); + + source.persons = Arrays.asList(new Person("jon", "snow", 19), new Person("tyrion", "lannister", 27)); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapTypedListOfComplexObjectWihtNestedElements() { + + WithList source = new WithList(); + + Person jon = new Person("jon", "snow", 19); + Address adr = new Address(); + adr.setStreet("the wall"); + adr.setNumber(100); + jon.setAddress(adr); + + source.persons = Arrays.asList(jon, new Person("tyrion", "lannister", 27)); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapNestedObject() { + + Person jon = new Person("jon", "snow", 19); + Address adr = new Address(); + adr.setStreet("the wall"); + adr.setNumber(100); + jon.setAddress(adr); + + assertBackAndForwardMapping(jon); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapUntypedList() { + + WithList source = new WithList(); + source.objects = Arrays. asList(Integer.valueOf(100), "foo", new Person("jon", "snow", 19)); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapTypedMapOfSimpleTypes() { + + WithMap source = new WithMap(); + source.strings = new LinkedHashMap(); + source.strings.put("1", "spring"); + source.strings.put("2", "data"); + source.strings.put("3", "redis"); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapTypedMapOfComplexTypes() { + + WithMap source = new WithMap(); + source.persons = new LinkedHashMap(); + source.persons.put("1", new Person("jon", "snow", 19)); + source.persons.put("2", new Person("tyrion", "lannister", 19)); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void shouldMapUntypedMap() { + + WithMap source = new WithMap(); + source.objects = new LinkedHashMap(); + source.objects.put("1", "spring"); + source.objects.put("2", Integer.valueOf(100)); + source.objects.put("3", "redis"); + assertBackAndForwardMapping(source); + } + + /** + * @see DATAREDIS-423 + */ + @Test + public void nestedStuff() { + + WithList nestedList = new WithList(); + nestedList.objects = new ArrayList(); + + WithMap deepNestedMap = new WithMap(); + deepNestedMap.persons = new LinkedHashMap(); + deepNestedMap.persons.put("jon", new Person("jon", "snow", 24)); + + nestedList.objects.add(deepNestedMap); + + WithMap outer = new WithMap(); + outer.objects = new LinkedHashMap(); + outer.objects.put("1", nestedList); + + assertBackAndForwardMapping(outer); + } + + @Data + public static class WithList { + List strings; + List objects; + List persons; + } + + @Data + public static class WithMap { + Map strings; + Map objects; + Map persons; + } +} From f154f1ce4f445c70752f56d88e5747d833d96a1f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 13 May 2016 12:01:54 +0200 Subject: [PATCH 3/3] DATAREDIS-423 - Polishing. Extend documentation. Reformat JavaDoc. Adjust test method names. Original pull request: #197. --- src/main/asciidoc/reference/redis.adoc | 67 ++++++++++++++++++- .../data/redis/hash/Jackson2HashMapper.java | 20 ++++-- .../mapping/Jackson2HashMapperTests.java | 2 +- .../mapping/Jackson2HashMapperUnitTests.java | 2 +- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/main/asciidoc/reference/redis.adoc b/src/main/asciidoc/reference/redis.adoc index 769f5b398d..b7d8a6b30c 100644 --- a/src/main/asciidoc/reference/redis.adoc +++ b/src/main/asciidoc/reference/redis.adoc @@ -363,9 +363,9 @@ Hash mappers are converters to map objects to a `Map` and back. `HashMappe Multiple implementations are available out of the box: -1. `BeanUtilsHashMapper` using Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html[BeanUtils] -2. `ObjectHashMapper` using <> -3. `Jackson2HashMapper` using https://github.com/FasterXML/jackson[FasterXML Jackson]. +1. `BeanUtilsHashMapper` using Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html[BeanUtils]. +2. `ObjectHashMapper` using <>. +3. <> using https://github.com/FasterXML/jackson[FasterXML Jackson]. [source,java] ---- @@ -397,6 +397,67 @@ public class HashMapping { } ---- +[[redis.hashmappers.jackson2]] +=== Jackson2HashMapper + +`Jackson2HashMapper` provides Redis Hash mapping for domain objects using https://github.com/FasterXML/jackson[FasterXML Jackson]. +`Jackson2HashMapper` can map data 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) 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. + +[source,java] +---- +public class Person { + String firstname; + String lastname; + Address address; +} + +public class Address { + String city; + String country; +} +---- + +.Normal Mapping +[width="80%",cols="<1,<2",options="header"] +|==== +|Hash Field +|Value + +|firstname +|`Jon` + +|lastname +|`Snow` + +|address +|`{ "city" : "Castle Black", "country" : "The North" }` +|==== + +.Flat Mapping +[width="80%",cols="<1,<2",options="header"] +|==== +|Hash Field +|Value + +|firstname +|`Jon` + +|lastname +|`Snow` + +|address.city +|`Castle Black` + +|address.country +|`The North` +|==== + +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 using flattening. The resulting hash cannot be mapped back into an Object. + :leveloffset: 2 include::{referenceDir}/redis-messaging.adoc[] diff --git a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java index 052373b6bc..949c6c4a12 100644 --- a/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java +++ b/src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java @@ -43,8 +43,13 @@ /** * {@link ObjectMapper} based {@link HashMapper} implementation that allows flattening. Given an entity {@code Person} - * with an {@code Address} like below the flattening will create individual hash entries for all nested properties. + * with an {@code Address} like below the flattening will create individual hash entries for all nested properties and + * resolve complex types into simple types, as far as possible. + *

+ * Flattening requires all property names to not interfere with JSON paths. Using dots or brackets in map keys or as + * property names is not supported using flattening. The resulting hash cannot be mapped back into an Object. * + * Example *

  * 
  * class Person {
@@ -60,24 +65,25 @@
  * 
  * 
* - *
- * Normal
+ * Normal * + * * * - * + * *
Hash fieldValue
firstnameJon
lastnameSnow
address{ city : Castle Black, country : The North }
address{ "city" : "Castle Black", "country" : "The North" }
- * - * Flat:
+ *
+ * Flat: * + * *   * * * *
Hash fieldValue
firstnameJon
lastnameSnow
address.cityCastle Black
address.countryThe North
- *
* * @author Christoph Strobl + * @author Mark Paluch * @since 1.8 */ public class Jackson2HashMapper implements HashMapper { diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java index 28678ab7be..79fa8132e6 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperTests.java @@ -82,7 +82,7 @@ public void setUp() { * @see DATAREDIS-423 */ @Test - public void shouldWriteReadHashCorrtectly() { + public void shouldWriteReadHashCorrectly() { Person jon = new Person("jon", "snow", 19); Address adr = new Address(); diff --git a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java index f54583c99b..b652943a8a 100644 --- a/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java +++ b/src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java @@ -73,7 +73,7 @@ public void shouldMapTypedListOfSimpleType() { * @see DATAREDIS-423 */ @Test - public void shouldMapTypledListOfComplexType() { + public void shouldMapTypedListOfComplexType() { WithList source = new WithList();