Pass-thru byte arrays in entities into Bucket.

MappingRedisConverter now accepts data for byte arrays as single property to avoid flattening out binary data into a collection form. We still accept data in a collection form to retain compatibility.

Closes: #1981
Original Pull Request: #2015
This commit is contained in:
Mark Paluch
2021-03-23 14:57:28 +01:00
committed by Christoph Strobl
parent b2349fd1ea
commit d5ba93cc25
6 changed files with 114 additions and 7 deletions

View File

@@ -97,7 +97,7 @@ public class Bucket {
/**
* Get value assigned with path.
*
* @param path path must not be {@literal null} or {@link String#isEmpty()}.
* @param path must not be {@literal null} or {@link String#isEmpty()}.
* @return {@literal null} if not set.
*/
@Nullable
@@ -107,6 +107,17 @@ public class Bucket {
return data.get(path);
}
/**
* Return whether {@code path} is associated with a non-{@code null} value.
*
* @param path must not be {@literal null} or {@link String#isEmpty()}.
* @return {@literal true} if the {@code path} is associated with a non-{@code null} value.
* @since 2.5
*/
public boolean hasValue(String path) {
return get(path) != null;
}
/**
* A set view of the mappings contained in this bucket.
*

View File

@@ -283,8 +283,18 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
if (typeInformation.isCollectionLike()) {
return readCollectionOrArray(currentPath, typeInformation.getType(),
typeInformation.getRequiredComponentType().getActualType().getType(), source.getBucket());
if (isByteArray(typeInformation)) {
// accept collection form for byte[] if there's no dedicated key
if (!source.getBucket().hasValue(currentPath) && isByteArray(typeInformation)) {
return readCollectionOrArray(currentPath, typeInformation.getType(),
typeInformation.getRequiredComponentType().getActualType().getType(), source.getBucket());
}
} else {
return readCollectionOrArray(currentPath, typeInformation.getType(),
typeInformation.getRequiredComponentType().getActualType().getType(), source.getBucket());
}
}
if (persistentProperty.isEntity()
@@ -309,7 +319,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
return sourceBytes == null ? fromBytes(sourceBytes, typeInformation.getActualType().getType()) : source.getId();
}
Class<?> typeToUse = getTypeHint(currentPath, source.getBucket(), persistentProperty.getActualType());
Class<?> typeToUse = getTypeHint(currentPath, source.getBucket(), persistentProperty.getType());
return fromBytes(sourceBytes, typeToUse);
}
@@ -508,7 +518,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
sink.getBucket().put(pUpdate.getPropertyPath(), toBytes(ref.getKeySpace() + ":" + refId));
}
}
} else if (targetProperty.isCollectionLike()) {
} else if (targetProperty.isCollectionLike() && !isByteArray(targetProperty)) {
Collection<?> collection = pUpdate.getValue() instanceof Collection ? (Collection<?>) pUpdate.getValue()
: Collections.singleton(pUpdate.getValue());
@@ -595,6 +605,11 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
return;
}
if (value instanceof byte[]) {
sink.getBucket().put(StringUtils.hasText(path) ? path : "_raw", (byte[]) value);
return;
}
if (value.getClass() != typeHint.getType()) {
typeMapper.writeType(value.getClass(), sink.getBucket().getPropertyPath(path));
}
@@ -620,7 +635,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
if (propertyValue != null) {
writeMap(keyspace, propertyStringPath, persistentProperty.getMapValueType(), (Map<?, ?>) propertyValue, sink);
}
} else if (persistentProperty.isCollectionLike()) {
} else if (persistentProperty.isCollectionLike() && !isByteArray(persistentProperty)) {
if (propertyValue == null) {
writeCollection(keyspace, propertyStringPath, null,
@@ -753,6 +768,11 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
return;
}
if (value instanceof byte[]) {
sink.getBucket().put(path, toBytes(value));
return;
}
if (customConversions.hasCustomWriteTarget(value.getClass())) {
Optional<Class<?>> targetType = customConversions.getCustomWriteTarget(value.getClass());
@@ -969,6 +989,11 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
* @throws ConverterNotFoundException
*/
public <T> T fromBytes(byte[] source, Class<T> type) {
if (type.isInstance(source)) {
return type.cast(source);
}
return conversionService.convert(source, type);
}
@@ -1055,6 +1080,14 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
customConversions.registerConvertersIn(conversionService);
}
private static boolean isByteArray(RedisPersistentProperty property) {
return property.getType().equals(byte[].class);
}
private static boolean isByteArray(TypeInformation<?> type) {
return type.getType().equals(byte[].class);
}
/**
* @author Christoph Strobl
* @author Mark Paluch