Skip to content

Commit 3e83dad

Browse files
committed
Consider type hints of the actual value in MappingRedisConverter.
We now consider the actual value type when determining whether a value should be considered an entity. Previously, we relied on PersistentProperty type information only which made it impossible to use generic-typed (or Object) properties for entities. Closes #2349
1 parent 71ee02c commit 3e83dad

File tree

2 files changed

+63
-16
lines changed

2 files changed

+63
-16
lines changed

src/main/java/org/springframework/data/redis/core/convert/MappingRedisConverter.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.springframework.data.redis.core.mapping.RedisPersistentProperty;
5757
import org.springframework.data.redis.util.ByteUtils;
5858
import org.springframework.data.util.ClassTypeInformation;
59+
import org.springframework.data.util.ProxyUtils;
5960
import org.springframework.data.util.TypeInformation;
6061
import org.springframework.lang.Nullable;
6162
import org.springframework.util.Assert;
@@ -262,12 +263,20 @@ private <R> R doReadInternal(String path, Class<R> type, RedisData source) {
262263
protected Object readProperty(String path, RedisData source, RedisPersistentProperty persistentProperty) {
263264

264265
String currentPath = !path.isEmpty() ? path + "." + persistentProperty.getName() : persistentProperty.getName();
266+
TypeInformation<?> typeInformation = typeMapper.readType(source.getBucket().getPropertyPath(currentPath),
267+
persistentProperty.getTypeInformation());
265268

266-
TypeInformation<?> typeInformation = persistentProperty.getTypeInformation();
269+
if (typeInformation.isMap()) {
267270

268-
if (persistentProperty.isMap()) {
271+
Class<?> mapValueType = null;
269272

270-
Class<?> mapValueType = persistentProperty.getMapValueType();
273+
if (typeInformation.getMapValueType() != null) {
274+
mapValueType = typeInformation.getMapValueType().getType();
275+
}
276+
277+
if (mapValueType == null && persistentProperty.isMap()) {
278+
mapValueType = persistentProperty.getMapValueType();
279+
}
271280

272281
if (mapValueType == null) {
273282
throw new IllegalArgumentException("Unable to retrieve MapValueType!");
@@ -297,16 +306,14 @@ protected Object readProperty(String path, RedisData source, RedisPersistentProp
297306
}
298307
}
299308

300-
if (persistentProperty.isEntity()
309+
if (mappingContext.getPersistentEntity(typeInformation) != null
301310
&& !conversionService.canConvert(byte[].class, typeInformation.getRequiredActualType().getType())) {
302311

303312
Bucket bucket = source.getBucket().extract(currentPath + ".");
304313

305314
RedisData newBucket = new RedisData(bucket);
306-
TypeInformation<?> typeToRead = typeMapper.readType(bucket.getPropertyPath(currentPath),
307-
typeInformation);
308315

309-
return readInternal(currentPath, typeToRead.getType(), newBucket);
316+
return readInternal(currentPath, typeInformation.getType(), newBucket);
310317
}
311318

312319
byte[] sourceBytes = source.getBucket().get(currentPath);
@@ -588,8 +595,7 @@ RedisPersistentProperty getTargetPropertyOrNullForPath(String path, Class<?> typ
588595
* @param sink
589596
*/
590597
private void writeInternal(@Nullable String keyspace, String path, @Nullable Object value,
591-
TypeInformation<?> typeHint,
592-
RedisData sink) {
598+
TypeInformation<?> typeHint, RedisData sink) {
593599

594600
if (value == null) {
595601
return;
@@ -664,16 +670,14 @@ private void writeInternal(@Nullable String keyspace, String path, @Nullable Obj
664670
}
665671
}
666672

667-
} else if (persistentProperty.isEntity()) {
673+
} else if (propertyValue != null) {
668674

669-
if (propertyValue != null) {
670-
writeInternal(keyspace, propertyStringPath, propertyValue,
671-
persistentProperty.getTypeInformation().getRequiredActualType(), sink);
672-
}
673-
} else {
675+
if (customConversions.isSimpleType(ProxyUtils.getUserClass(propertyValue.getClass()))) {
674676

675-
if (propertyValue != null) {
676677
writeToBucket(propertyStringPath, propertyValue, sink, persistentProperty.getType());
678+
} else {
679+
writeInternal(keyspace, propertyStringPath, propertyValue,
680+
persistentProperty.getTypeInformation().getRequiredActualType(), sink);
677681
}
678682
}
679683
});

src/test/java/org/springframework/data/redis/core/convert/MappingRedisConverterUnitTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import static org.mockito.Mockito.*;
2020
import static org.springframework.data.redis.core.convert.ConversionTestEntities.*;
2121

22+
import lombok.AllArgsConstructor;
23+
2224
import java.nio.charset.Charset;
2325
import java.nio.charset.StandardCharsets;
2426
import java.time.Duration;
@@ -1925,6 +1927,38 @@ void readEntityWithCustomConverter() {
19251927
assertThat(target.getAccountName()).isEqualTo("Golam Mazid Sajib");
19261928
}
19271929

1930+
@Test // GH-2349
1931+
void writeGenericEntity() {
1932+
1933+
WithGenericEntity<User> generic = new WithGenericEntity<>();
1934+
generic.entity = new User("hello");
1935+
1936+
assertThat(write(generic)).hasSize(3) //
1937+
.containsEntry("_class",
1938+
"org.springframework.data.redis.core.convert.MappingRedisConverterUnitTests$WithGenericEntity")
1939+
.containsEntry("entity.name", "hello") //
1940+
.containsEntry("entity._class",
1941+
"org.springframework.data.redis.core.convert.MappingRedisConverterUnitTests$User");
1942+
}
1943+
1944+
@Test // GH-2349
1945+
void readGenericEntity() {
1946+
1947+
Bucket bucket = new Bucket();
1948+
bucket.put("entity.name", "hello".getBytes());
1949+
bucket.put("entity._class",
1950+
"org.springframework.data.redis.core.convert.MappingRedisConverterUnitTests$User".getBytes());
1951+
1952+
RedisData redisData = new RedisData(bucket);
1953+
redisData.setKeyspace(KEYSPACE_ACCOUNT);
1954+
redisData.setId("ai-id-1");
1955+
1956+
WithGenericEntity<User> generic = converter.read(WithGenericEntity.class, redisData);
1957+
1958+
assertThat(generic.entity).isNotNull();
1959+
assertThat(generic.entity.name).isEqualTo("hello");
1960+
}
1961+
19281962
@Test // DATAREDIS-1175
19291963
@EnabledOnJre(JRE.JAVA_8)
19301964
// FIXME: https://github.com/spring-projects/spring-data-redis/issues/2168
@@ -2083,4 +2117,13 @@ public AccountInfo convert(byte[] bytes) {
20832117
}
20842118
}
20852119

2120+
static class WithGenericEntity<T> {
2121+
T entity;
2122+
}
2123+
2124+
@AllArgsConstructor
2125+
static class User {
2126+
String name;
2127+
}
2128+
20862129
}

0 commit comments

Comments
 (0)