diff --git a/pom.xml b/pom.xml index e6d868411f..8ea9f36d37 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-492-SNAPSHOT Spring Data Redis diff --git a/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java b/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java index 87f16256e9..eb90a37b42 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java +++ b/src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java @@ -16,6 +16,7 @@ package org.springframework.data.redis.core.convert; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; @@ -99,6 +100,7 @@ * * * @author Christoph Strobl + * @author Greg Turnquist * @since 1.7 */ public class MappingRedisConverter implements RedisConverter, InitializingBean { @@ -354,8 +356,8 @@ public void write(Object source, final RedisData sink) { sink.setTimeToLive(ttl); } - for (IndexedData indexeData : indexResolver.resolveIndexesFor(entity.getTypeInformation(), source)) { - sink.addIndexedData(indexeData); + for (IndexedData indexedData : indexResolver.resolveIndexesFor(entity.getTypeInformation(), source)) { + sink.addIndexedData(indexedData); } } @@ -408,8 +410,22 @@ public void doWithPersistentProperty(KeyValuePersistentProperty persistentProper writeMap(keyspace, propertyStringPath, persistentProperty.getMapValueType(), (Map) accessor.getProperty(persistentProperty), sink); } else if (persistentProperty.isCollectionLike()) { - writeCollection(keyspace, propertyStringPath, (Collection) accessor.getProperty(persistentProperty), + + final Object property = accessor.getProperty(persistentProperty); + + if (property == null || Iterable.class.isAssignableFrom(property.getClass())) { + + writeCollection(keyspace, propertyStringPath, (Iterable) property, persistentProperty.getTypeInformation().getComponentType(), sink); + } else if (property.getClass().isArray()) { + + writeCollection(keyspace, propertyStringPath, Arrays.asList((Object[]) property), + persistentProperty.getTypeInformation().getComponentType(), sink); + } else { + + throw new RuntimeException("Don't know how to handle " + property.getClass() + " type collection"); + } + } else if (persistentProperty.isEntity()) { writeInternal(keyspace, propertyStringPath, accessor.getProperty(persistentProperty), persistentProperty.getTypeInformation().getActualType(), sink); @@ -481,7 +497,7 @@ public void doWithAssociation(Association associatio * @param typeHint * @param sink */ - private void writeCollection(String keyspace, String path, Collection values, TypeInformation typeHint, + private void writeCollection(String keyspace, String path, Iterable values, TypeInformation typeHint, RedisData sink) { if (values == null) { @@ -491,6 +507,10 @@ private void writeCollection(String keyspace, String path, Collection values, int i = 0; for (Object value : values) { + if (value == null) { + break; + } + String currentPath = path + ".[" + i + "]"; if (customConversions.hasCustomWriteTarget(value.getClass())) { diff --git a/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java b/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java index 94c5334215..cd31ee665d 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java +++ b/src/main/java/org/springframework/data/redis/core/convert/PathIndexResolver.java @@ -15,8 +15,10 @@ */ package org.springframework.data.redis.core.convert; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -43,6 +45,7 @@ * {@link IndexConfiguration}. * * @author Christoph Strobl + * @author Greg Turnquist * @since 1.7 */ public class PathIndexResolver implements IndexResolver { @@ -116,11 +119,23 @@ public void doWithPersistentProperty(KeyValuePersistentProperty persistentProper } else if (persistentProperty.isCollectionLike()) { - for (Object listValue : (Iterable) propertyValue) { + final Iterable iterable; - TypeInformation typeToUse = updateTypeHintForActualValue(typeHint, listValue); - indexes.addAll( + if (Iterable.class.isAssignableFrom(propertyValue.getClass())) { + iterable = (Iterable) propertyValue; + } else if (propertyValue.getClass().isArray()) { + iterable = Arrays.asList((Object[]) propertyValue); + } else { + throw new RuntimeException("Don't know how to handle " + propertyValue.getClass() + " type of collection"); + } + + for (Object listValue : iterable) { + + if (listValue != null) { + TypeInformation typeToUse = updateTypeHintForActualValue(typeHint, listValue); + indexes.addAll( doResolveIndexesFor(keyspace, currentPath, typeToUse.getActualType(), persistentProperty, listValue)); + } } } diff --git a/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java b/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java index 3b655dc6b7..df88ffd35b 100644 --- a/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java +++ b/src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java @@ -43,6 +43,7 @@ import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -75,6 +76,7 @@ /** * @author Christoph Strobl + * @author Greg Turnquist */ @RunWith(MockitoJUnitRunner.class) public class MappingRedisConverterUnitTests { @@ -146,7 +148,7 @@ public void writeDoesNotAppendPropertiesWithNullValues() { * @see DATAREDIS-425 */ @Test - public void writeDoesNotAppendPropertiesWithEmtpyCollections() { + public void writeDoesNotAppendPropertiesWithEmptyCollections() { rand.firstname = "rand"; @@ -435,6 +437,27 @@ public void writeAppendsMapWithSimpleKeyCorrectly() { .containingUtf8String("physicalAttributes.[eye-color]", "grey")); } + /** + * @see DATAREDIS-492 + */ + @Test + public void writeHandlesArraysProperly() { + + this.converter = new MappingRedisConverter(null, null, resolverMock); + this.converter + .setCustomConversions(new CustomConversions(Collections.singletonList(new ListToByteConverter()))); + this.converter.afterPropertiesSet(); + + Map innerMap = new LinkedHashMap(); + innerMap.put("address", "tyrionl@netflix.com"); + innerMap.put("when", new String[]{"pipeline.failed"}); + + Map map = new LinkedHashMap(); + map.put("email", Collections.singletonList(innerMap)); + + RedisData target = write(map); + } + /** * @see DATAREDIS-425 */ @@ -1375,6 +1398,35 @@ public Map convert(Species source) { } } + @WritingConverter + static class ListToByteConverter implements Converter { + + private final ObjectMapper mapper; + private final Jackson2JsonRedisSerializer serializer; + + ListToByteConverter() { + + mapper = new ObjectMapper(); + mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(Visibility.ANY).withGetterVisibility(Visibility.NONE) + .withSetterVisibility(Visibility.NONE).withCreatorVisibility(Visibility.NONE)); + + serializer = new Jackson2JsonRedisSerializer(List.class); + serializer.setObjectMapper(mapper); + } + + @Override + public byte[] convert(List source) { + + if (source == null || source.isEmpty()) { + return null; + } + + return serializer.serialize(source); + } + } + + @ReadingConverter static class MapToSpeciesConverter implements Converter, Species> {