Skip to content

Commit d5ba93c

Browse files
mp911dechristophstrobl
authored andcommitted
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
1 parent b2349fd commit d5ba93c

File tree

6 files changed

+114
-7
lines changed

6 files changed

+114
-7
lines changed

src/main/asciidoc/new-features.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33

44
This section briefly covers items that are new and noteworthy in the latest releases.
55

6+
[[new-in-2.5.0]]
7+
== New in Spring Data Redis 2.5
8+
9+
* `MappingRedisConverter` no longer converts <<redis.repositories.mapping,byte arrays to a collection>> representation.
10+
611
[[new-in-2.4.0]]
712
== New in Spring Data Redis 2.4
813

src/main/asciidoc/reference/redis-repositories.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ The following table describes the default mapping rules:
131131
| String firstname = "rand";
132132
| firstname = "rand"
133133

134+
| Byte array (`byte[]`)
135+
| byte[] image = "rand".getBytes();
136+
| image = "rand"
137+
134138
| Complex Type +
135139
(for example, Address)
136140
| Address address = new Address("emond's field");

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public void remove(String path) {
9797
/**
9898
* Get value assigned with path.
9999
*
100-
* @param path path must not be {@literal null} or {@link String#isEmpty()}.
100+
* @param path must not be {@literal null} or {@link String#isEmpty()}.
101101
* @return {@literal null} if not set.
102102
*/
103103
@Nullable
@@ -107,6 +107,17 @@ public byte[] get(String path) {
107107
return data.get(path);
108108
}
109109

110+
/**
111+
* Return whether {@code path} is associated with a non-{@code null} value.
112+
*
113+
* @param path must not be {@literal null} or {@link String#isEmpty()}.
114+
* @return {@literal true} if the {@code path} is associated with a non-{@code null} value.
115+
* @since 2.5
116+
*/
117+
public boolean hasValue(String path) {
118+
return get(path) != null;
119+
}
120+
110121
/**
111122
* A set view of the mappings contained in this bucket.
112123
*

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

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,18 @@ protected Object readProperty(String path, RedisData source, RedisPersistentProp
283283

284284
if (typeInformation.isCollectionLike()) {
285285

286-
return readCollectionOrArray(currentPath, typeInformation.getType(),
287-
typeInformation.getRequiredComponentType().getActualType().getType(), source.getBucket());
286+
if (isByteArray(typeInformation)) {
287+
288+
// accept collection form for byte[] if there's no dedicated key
289+
if (!source.getBucket().hasValue(currentPath) && isByteArray(typeInformation)) {
290+
291+
return readCollectionOrArray(currentPath, typeInformation.getType(),
292+
typeInformation.getRequiredComponentType().getActualType().getType(), source.getBucket());
293+
}
294+
} else {
295+
return readCollectionOrArray(currentPath, typeInformation.getType(),
296+
typeInformation.getRequiredComponentType().getActualType().getType(), source.getBucket());
297+
}
288298
}
289299

290300
if (persistentProperty.isEntity()
@@ -309,7 +319,7 @@ protected Object readProperty(String path, RedisData source, RedisPersistentProp
309319
return sourceBytes == null ? fromBytes(sourceBytes, typeInformation.getActualType().getType()) : source.getId();
310320
}
311321

312-
Class<?> typeToUse = getTypeHint(currentPath, source.getBucket(), persistentProperty.getActualType());
322+
Class<?> typeToUse = getTypeHint(currentPath, source.getBucket(), persistentProperty.getType());
313323
return fromBytes(sourceBytes, typeToUse);
314324
}
315325

@@ -508,7 +518,7 @@ private void writePartialPropertyUpdate(PartialUpdate<?> update, PropertyUpdate
508518
sink.getBucket().put(pUpdate.getPropertyPath(), toBytes(ref.getKeySpace() + ":" + refId));
509519
}
510520
}
511-
} else if (targetProperty.isCollectionLike()) {
521+
} else if (targetProperty.isCollectionLike() && !isByteArray(targetProperty)) {
512522

513523
Collection<?> collection = pUpdate.getValue() instanceof Collection ? (Collection<?>) pUpdate.getValue()
514524
: Collections.singleton(pUpdate.getValue());
@@ -595,6 +605,11 @@ private void writeInternal(@Nullable String keyspace, String path, @Nullable Obj
595605
return;
596606
}
597607

608+
if (value instanceof byte[]) {
609+
sink.getBucket().put(StringUtils.hasText(path) ? path : "_raw", (byte[]) value);
610+
return;
611+
}
612+
598613
if (value.getClass() != typeHint.getType()) {
599614
typeMapper.writeType(value.getClass(), sink.getBucket().getPropertyPath(path));
600615
}
@@ -620,7 +635,7 @@ private void writeInternal(@Nullable String keyspace, String path, @Nullable Obj
620635
if (propertyValue != null) {
621636
writeMap(keyspace, propertyStringPath, persistentProperty.getMapValueType(), (Map<?, ?>) propertyValue, sink);
622637
}
623-
} else if (persistentProperty.isCollectionLike()) {
638+
} else if (persistentProperty.isCollectionLike() && !isByteArray(persistentProperty)) {
624639

625640
if (propertyValue == null) {
626641
writeCollection(keyspace, propertyStringPath, null,
@@ -753,6 +768,11 @@ private void writeToBucket(String path, @Nullable Object value, RedisData sink,
753768
return;
754769
}
755770

771+
if (value instanceof byte[]) {
772+
sink.getBucket().put(path, toBytes(value));
773+
return;
774+
}
775+
756776
if (customConversions.hasCustomWriteTarget(value.getClass())) {
757777

758778
Optional<Class<?>> targetType = customConversions.getCustomWriteTarget(value.getClass());
@@ -969,6 +989,11 @@ public byte[] toBytes(Object source) {
969989
* @throws ConverterNotFoundException
970990
*/
971991
public <T> T fromBytes(byte[] source, Class<T> type) {
992+
993+
if (type.isInstance(source)) {
994+
return type.cast(source);
995+
}
996+
972997
return conversionService.convert(source, type);
973998
}
974999

@@ -1055,6 +1080,14 @@ private void initializeConverters() {
10551080
customConversions.registerConvertersIn(conversionService);
10561081
}
10571082

1083+
private static boolean isByteArray(RedisPersistentProperty property) {
1084+
return property.getType().equals(byte[].class);
1085+
}
1086+
1087+
private static boolean isByteArray(TypeInformation<?> type) {
1088+
return type.getType().equals(byte[].class);
1089+
}
1090+
10581091
/**
10591092
* @author Christoph Strobl
10601093
* @author Mark Paluch

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ public static class WithArrays {
203203
String[] arrayOfSimpleTypes;
204204
Species[] arrayOfCompexTypes;
205205
int[] arrayOfPrimitives;
206+
byte[] avatar;
206207
}
207208

208209
static class TypeWithObjectValueTypes {

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import org.springframework.data.mapping.MappingException;
5353
import org.springframework.data.redis.core.PartialUpdate;
5454
import org.springframework.data.redis.core.convert.KeyspaceConfiguration.KeyspaceSettings;
55-
import org.springframework.data.redis.core.convert.ConversionTestEntities.*;
5655
import org.springframework.data.redis.core.mapping.RedisMappingContext;
5756
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
5857
import org.springframework.data.redis.test.util.RedisTestData;
@@ -1273,6 +1272,40 @@ void readHandlesArraysOfSimpleTypeProperly() {
12731272
assertThat(target.arrayOfSimpleTypes).isEqualTo(new String[] { "rand", "mat", "perrin" });
12741273
}
12751274

1275+
@Test // GH-1981
1276+
void readHandlesByteArrays() {
1277+
1278+
Map<String, String> source = new LinkedHashMap<>();
1279+
source.put("avatar", "foo-bar-baz");
1280+
source.put("otherAvatar", "foo-bar-baz");
1281+
1282+
WithArrays target = read(WithArrays.class, source);
1283+
1284+
assertThat(target.avatar).isEqualTo("foo-bar-baz".getBytes());
1285+
}
1286+
1287+
@Test // GH-1981
1288+
void writeHandlesByteArrays() {
1289+
1290+
WithArrays withArrays = new WithArrays();
1291+
withArrays.avatar = "foo-bar-baz".getBytes();
1292+
1293+
assertThat(write(withArrays)).containsEntry("avatar", "foo-bar-baz");
1294+
}
1295+
1296+
@Test // GH-1981
1297+
void readHandlesByteArraysUsingCollectionRepresentation() {
1298+
1299+
Map<String, String> source = new LinkedHashMap<>();
1300+
source.put("avatar.[0]", "102");
1301+
source.put("avatar.[1]", "111");
1302+
source.put("avatar.[2]", "111");
1303+
1304+
WithArrays target = read(WithArrays.class, source);
1305+
1306+
assertThat(target.avatar).isEqualTo("foo".getBytes());
1307+
}
1308+
12761309
@Test // DATAREDIS-492
12771310
void writeHandlesArraysOfComplexTypeProperly() {
12781311

@@ -1496,6 +1529,26 @@ void writeShouldWritePartialUpdateSimpleValueCorrectly() {
14961529
assertThat(write(update)).containsEntry("firstname", "rand").containsEntry("age", "24");
14971530
}
14981531

1532+
@Test // GH-1981
1533+
void writeShouldWritePartialUpdateFromEntityByteArrayValueCorrectly() {
1534+
1535+
WithArrays value = new WithArrays();
1536+
value.avatar = "foo-bar-baz".getBytes();
1537+
1538+
PartialUpdate<WithArrays> update = new PartialUpdate<>("123", value);
1539+
1540+
assertThat(write(update)).containsEntry("avatar", "foo-bar-baz");
1541+
}
1542+
1543+
@Test // GH-1981
1544+
void writeShouldWritePartialUpdateFromSetByteArrayValueCorrectly() {
1545+
1546+
PartialUpdate<WithArrays> update = PartialUpdate.newPartialUpdate(42, WithArrays.class).set("avatar",
1547+
"foo-bar-baz".getBytes());
1548+
1549+
assertThat(write(update)).containsEntry("avatar", "foo-bar-baz");
1550+
}
1551+
14991552
@Test // DATAREDIS-471
15001553
void writeShouldWritePartialUpdatePathWithSimpleValueCorrectly() {
15011554

0 commit comments

Comments
 (0)