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
This commit is contained in:
@@ -56,6 +56,7 @@ import org.springframework.data.redis.core.mapping.RedisPersistentEntity;
|
||||
import org.springframework.data.redis.core.mapping.RedisPersistentProperty;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.ProxyUtils;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -262,12 +263,20 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
protected Object readProperty(String path, RedisData source, RedisPersistentProperty persistentProperty) {
|
||||
|
||||
String currentPath = !path.isEmpty() ? path + "." + persistentProperty.getName() : persistentProperty.getName();
|
||||
TypeInformation<?> typeInformation = typeMapper.readType(source.getBucket().getPropertyPath(currentPath),
|
||||
persistentProperty.getTypeInformation());
|
||||
|
||||
TypeInformation<?> typeInformation = persistentProperty.getTypeInformation();
|
||||
if (typeInformation.isMap()) {
|
||||
|
||||
if (persistentProperty.isMap()) {
|
||||
Class<?> mapValueType = null;
|
||||
|
||||
Class<?> mapValueType = persistentProperty.getMapValueType();
|
||||
if (typeInformation.getMapValueType() != null) {
|
||||
mapValueType = typeInformation.getMapValueType().getType();
|
||||
}
|
||||
|
||||
if (mapValueType == null && persistentProperty.isMap()) {
|
||||
mapValueType = persistentProperty.getMapValueType();
|
||||
}
|
||||
|
||||
if (mapValueType == null) {
|
||||
throw new IllegalArgumentException("Unable to retrieve MapValueType!");
|
||||
@@ -297,16 +306,14 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
if (persistentProperty.isEntity()
|
||||
if (mappingContext.getPersistentEntity(typeInformation) != null
|
||||
&& !conversionService.canConvert(byte[].class, typeInformation.getRequiredActualType().getType())) {
|
||||
|
||||
Bucket bucket = source.getBucket().extract(currentPath + ".");
|
||||
|
||||
RedisData newBucket = new RedisData(bucket);
|
||||
TypeInformation<?> typeToRead = typeMapper.readType(bucket.getPropertyPath(currentPath),
|
||||
typeInformation);
|
||||
|
||||
return readInternal(currentPath, typeToRead.getType(), newBucket);
|
||||
return readInternal(currentPath, typeInformation.getType(), newBucket);
|
||||
}
|
||||
|
||||
byte[] sourceBytes = source.getBucket().get(currentPath);
|
||||
@@ -588,8 +595,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
* @param sink
|
||||
*/
|
||||
private void writeInternal(@Nullable String keyspace, String path, @Nullable Object value,
|
||||
TypeInformation<?> typeHint,
|
||||
RedisData sink) {
|
||||
TypeInformation<?> typeHint, RedisData sink) {
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
@@ -664,17 +670,15 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
} else if (persistentProperty.isEntity()) {
|
||||
} else if (propertyValue != null) {
|
||||
|
||||
if (propertyValue != null) {
|
||||
if (customConversions.isSimpleType(ProxyUtils.getUserClass(propertyValue.getClass()))) {
|
||||
|
||||
writeToBucket(propertyStringPath, propertyValue, sink, persistentProperty.getType());
|
||||
} else {
|
||||
writeInternal(keyspace, propertyStringPath, propertyValue,
|
||||
persistentProperty.getTypeInformation().getRequiredActualType(), sink);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (propertyValue != null) {
|
||||
writeToBucket(propertyStringPath, propertyValue, sink, persistentProperty.getType());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.redis.core.convert.ConversionTestEntities.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
@@ -1925,6 +1927,38 @@ class MappingRedisConverterUnitTests {
|
||||
assertThat(target.getAccountName()).isEqualTo("Golam Mazid Sajib");
|
||||
}
|
||||
|
||||
@Test // GH-2349
|
||||
void writeGenericEntity() {
|
||||
|
||||
WithGenericEntity<User> generic = new WithGenericEntity<>();
|
||||
generic.entity = new User("hello");
|
||||
|
||||
assertThat(write(generic)).hasSize(3) //
|
||||
.containsEntry("_class",
|
||||
"org.springframework.data.redis.core.convert.MappingRedisConverterUnitTests$WithGenericEntity")
|
||||
.containsEntry("entity.name", "hello") //
|
||||
.containsEntry("entity._class",
|
||||
"org.springframework.data.redis.core.convert.MappingRedisConverterUnitTests$User");
|
||||
}
|
||||
|
||||
@Test // GH-2349
|
||||
void readGenericEntity() {
|
||||
|
||||
Bucket bucket = new Bucket();
|
||||
bucket.put("entity.name", "hello".getBytes());
|
||||
bucket.put("entity._class",
|
||||
"org.springframework.data.redis.core.convert.MappingRedisConverterUnitTests$User".getBytes());
|
||||
|
||||
RedisData redisData = new RedisData(bucket);
|
||||
redisData.setKeyspace(KEYSPACE_ACCOUNT);
|
||||
redisData.setId("ai-id-1");
|
||||
|
||||
WithGenericEntity<User> generic = converter.read(WithGenericEntity.class, redisData);
|
||||
|
||||
assertThat(generic.entity).isNotNull();
|
||||
assertThat(generic.entity.name).isEqualTo("hello");
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1175
|
||||
@EnabledOnJre(JRE.JAVA_8)
|
||||
// FIXME: https://github.com/spring-projects/spring-data-redis/issues/2168
|
||||
@@ -2083,4 +2117,13 @@ class MappingRedisConverterUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class WithGenericEntity<T> {
|
||||
T entity;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
static class User {
|
||||
String name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user