diff --git a/pom.xml b/pom.xml index 7f8edc3cda..c4be36ae30 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.0.0-SNAPSHOT + 4.0.x-GH-3179-SNAPSHOT Spring Data Redis Spring Data module for 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 ee03f07f61..1489358c60 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 @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -178,7 +179,7 @@ public R read(Class type, RedisData source) { TypeInformation readType = typeMapper.readType(source.getBucket().getPath(), TypeInformation.of(type)); return readType.isCollectionLike() - ? (R) readCollectionOrArray("", ArrayList.class, Object.class, source.getBucket()) + ? (R) readCollectionOrArray("", readType.getType(), Object.class, source.getBucket()) : doReadInternal("", type, source); } @@ -403,6 +404,14 @@ public void write(@Nullable Object source, RedisData sink) { } if (source instanceof Collection collection) { + + Class collectionTargetType = Collection.class; + if (collection instanceof List) { + collectionTargetType = List.class; + } else if (collection instanceof Set) { + collectionTargetType = collection instanceof EnumSet ? EnumSet.class : Set.class; + } + typeMapper.writeType(collectionTargetType, sink.getBucket().getPath()); writeCollection(sink.getKeyspace(), "", collection, TypeInformation.of(Object.class), sink); return; } diff --git a/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java b/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java index 77d9cc3825..f1db511644 100644 --- a/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java +++ b/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java @@ -91,6 +91,14 @@ protected RedisPersistentEntity createPersistentEntity(TypeInformation return new BasicRedisPersistentEntity<>(typeInformation, getKeySpaceResolver(), timeToLiveAccessor); } + @Override + protected boolean shouldCreatePersistentEntityFor(TypeInformation typeInformation) { + if (typeInformation.isMap() || typeInformation.isCollectionLike()) { + return false; + } + return super.shouldCreatePersistentEntityFor(typeInformation); + } + @Override protected RedisPersistentProperty createPersistentProperty(Property property, RedisPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { 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 a01123bafe..f1affe5988 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 @@ -56,17 +56,24 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.stream.Stream; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.convert.converter.Converter; @@ -78,6 +85,7 @@ import org.springframework.data.redis.core.mapping.RedisMappingContext; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.test.util.RedisTestData; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; @@ -1994,16 +2002,20 @@ void readGenericEntity() { assertThat(generic.entity.name).isEqualTo("hello"); } - @Test // GH-2168 + @Test // GH-2168, GH-3179 void writePlainList() { List source = Arrays.asList("Hello", "stream", "message", 100L); RedisTestData target = write(source); - assertThat(target).containsEntry("[0]", "Hello") // - .containsEntry("[1]", "stream") // - .containsEntry("[2]", "message") // - .containsEntry("[3]", "100"); + assertThat(target) // + .containsEntry("_class", "java.util.List") // + .containsEntry("[0]", "Hello") // + .containsEntry("[0]._class", "java.lang.String") // + .containsEntry("[1]", "stream") // + .containsEntry("[2]", "message") // + .containsEntry("[3]", "100") // + .containsEntry("[3]._class", "java.lang.Long"); } @Test // DATAREDIS-1175 @@ -2024,6 +2036,21 @@ void readPlainList() { assertThat(target).containsExactly("Hello", "stream", "message", 100L); } + @ParameterizedTest // GH-3179 + @MethodSource("justCollections") + void readsPlainCollectionIfObjectTypeRequested(Class type, Collection collection) { + + RedisTestData source = write(collection); + + Object target = this.converter.read(Object.class, source.getRedisData()); + + assertThat(target).isInstanceOf(type).asInstanceOf(InstanceOfAssertFactories.COLLECTION).containsExactlyElementsOf(collection); + } + + private static Stream justCollections() { + return Stream.of(Arguments.of(List.class, Arrays.asList("Hello", "stream", "message", 100L)), Arguments.of(Set.class, Set.of("Hello", "stream", "message", 100L))); + } + private RedisTestData write(Object source) { RedisData rdo = new RedisData();