diff --git a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java index 0a27297f8..b2931d31a 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/Bucket.java +++ b/src/main/java/org/springframework/data/redis/core/convert/Bucket.java @@ -56,7 +56,7 @@ public class Bucket { Bucket(Map data) { - Assert.notNull(data, "Inital data must not be null!"); + Assert.notNull(data, "Initial data must not be null!"); this.data = new LinkedHashMap<>(data.size()); this.data.putAll(data); } @@ -67,7 +67,7 @@ public class Bucket { * @param path must not be {@literal null} or {@link String#isEmpty()}. * @param value can be {@literal null}. */ - public void put(String path, byte[] value) { + public void put(String path, @Nullable byte[] value) { Assert.hasText(path, "Path to property must not be null or empty."); data.put(path, value); 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 c4dd78d65..6329fe6e2 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 @@ -175,27 +175,27 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { * @see org.springframework.data.convert.EntityReader#read(java.lang.Class, java.lang.Object) */ @Override + @SuppressWarnings("unchecked") public R read(Class type, RedisData source) { TypeInformation readType = typeMapper.readType(source.getBucket().getPath(), ClassTypeInformation.from(type)); - if(readType.isCollectionLike()) { - return (R) readCollectionOrArray("", ArrayList.class, Object.class, source.getBucket()); - } - return readInternal("", type, source); + + return readType.isCollectionLike() + ? (R) readCollectionOrArray("", ArrayList.class, Object.class, source.getBucket()) + : doReadInternal("", type, source); + + } + + @Nullable + private R readInternal(String path, Class type, RedisData source) { + return source.getBucket().isEmpty() ? null : doReadInternal(path, type, source); } @SuppressWarnings("unchecked") - @Nullable - private R readInternal(String path, Class type, RedisData source) { - - if (source.getBucket() == null || source.getBucket().isEmpty()) { - return null; - } + private R doReadInternal(String path, Class type, RedisData source) { TypeInformation readType = typeMapper.readType(source.getBucket().getPath(), ClassTypeInformation.from(type)); - RedisPersistentEntity entity = mappingContext.getPersistentEntity(readType); - if (customConversions.hasCustomReadTarget(Map.class, readType.getType())) { Map partial = new HashMap<>(); @@ -211,6 +211,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { } R instance = (R) conversionService.convert(partial, readType.getType()); + RedisPersistentEntity entity = mappingContext.getPersistentEntity(readType); if (entity != null && entity.hasIdProperty()) { PersistentPropertyAccessor propertyAccessor = entity.getPropertyAccessor(instance); @@ -226,19 +227,20 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { readType.getType()); } + RedisPersistentEntity entity = mappingContext.getRequiredPersistentEntity(readType); EntityInstantiator instantiator = entityInstantiators.getInstantiatorFor(entity); Object instance = instantiator.createInstance((RedisPersistentEntity) entity, new PersistentEntityParameterValueProvider<>(entity, new ConverterAwareParameterValueProvider(path, source, conversionService), this.conversionService)); - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(instance); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(instance); entity.doWithProperties((PropertyHandler) persistentProperty -> { PreferredConstructor constructor = entity.getPersistenceConstructor(); - if (constructor.isConstructorParameter(persistentProperty)) { + if (constructor != null && constructor.isConstructorParameter(persistentProperty)) { return; } @@ -311,7 +313,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { } private void readAssociation(String path, RedisData source, RedisPersistentEntity entity, - PersistentPropertyAccessor accessor) { + PersistentPropertyAccessor accessor) { entity.doWithAssociations((AssociationHandler) association -> { @@ -400,8 +402,9 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { sink.setKeyspace(entity.getKeySpace()); - if(entity.getTypeInformation().isCollectionLike()) { - writeCollection(entity.getKeySpace(), "", (List) source, entity.getTypeInformation().getComponentType(), sink); + if (entity.getTypeInformation().isCollectionLike()) { + writeCollection(entity.getKeySpace(), "", (List) source, entity.getTypeInformation().getRequiredComponentType(), + sink); } else { writeInternal(entity.getKeySpace(), "", source, entity.getTypeInformation(), sink); } @@ -483,7 +486,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { if (targetProperty.isCollectionLike()) { RedisPersistentEntity ref = mappingContext.getPersistentEntity(targetProperty.getRequiredAssociation() - .getInverse().getTypeInformation().getRequiredComponentType().getActualType()); + .getInverse().getTypeInformation().getRequiredComponentType().getRequiredActualType()); int i = 0; for (Object o : (Collection) pUpdate.getValue()) { @@ -507,9 +510,9 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { } else if (targetProperty.isCollectionLike()) { Collection collection = pUpdate.getValue() instanceof Collection ? (Collection) pUpdate.getValue() - : Collections. singleton(pUpdate.getValue()); + : Collections.singleton(pUpdate.getValue()); writeCollection(entity.getKeySpace(), pUpdate.getPropertyPath(), collection, - targetProperty.getTypeInformation().getActualType(), sink); + targetProperty.getTypeInformation().getRequiredActualType(), sink); } else if (targetProperty.isMap()) { Map map = new HashMap<>(); @@ -565,7 +568,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { * @param typeHint * @param sink */ - private void writeInternal(String keyspace, String path, @Nullable Object value, TypeInformation typeHint, + private void writeInternal(@Nullable String keyspace, String path, @Nullable Object value, + TypeInformation typeHint, RedisData sink) { if (value == null) { @@ -576,8 +580,9 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { Optional> targetType = customConversions.getCustomWriteTarget(value.getClass()); - if (!StringUtils.hasText(path) && targetType.isPresent() && ClassUtils.isAssignable(byte[].class, targetType.get())) { - sink.getBucket().put(StringUtils.hasText(path) ? path : "_raw", conversionService.convert(value, byte[].class)); + if (!StringUtils.hasText(path) && targetType.isPresent() + && ClassUtils.isAssignable(byte[].class, targetType.get())) { + sink.getBucket().put(StringUtils.hasText(path) ? path : "_raw", conversionService.convert(value, byte[].class)); } else { if (!ClassUtils.isAssignable(typeHint.getType(), value.getClass())) { @@ -594,7 +599,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { } RedisPersistentEntity entity = mappingContext.getRequiredPersistentEntity(value.getClass()); - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value); entity.doWithProperties((PropertyHandler) persistentProperty -> { @@ -639,7 +644,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { if (propertyValue != null) { writeInternal(keyspace, propertyStringPath, propertyValue, - persistentProperty.getTypeInformation().getActualType(), sink); + persistentProperty.getTypeInformation().getRequiredActualType(), sink); } } else { @@ -652,13 +657,13 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { writeAssociation(path, entity, value, sink); } - private void writeAssociation(String path, RedisPersistentEntity entity, Object value, RedisData sink) { + private void writeAssociation(String path, RedisPersistentEntity entity, @Nullable Object value, RedisData sink) { if (value == null) { return; } - PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value); entity.doWithAssociations((AssociationHandler) association -> { @@ -670,7 +675,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { if (association.getInverse().isCollectionLike()) { RedisPersistentEntity ref = mappingContext.getRequiredPersistentEntity( - association.getInverse().getTypeInformation().getRequiredComponentType().getActualType()); + association.getInverse().getTypeInformation().getRequiredComponentType().getRequiredActualType()); String keyspace = ref.getKeySpace(); String propertyStringPath = (!path.isEmpty() ? path + "." : "") + association.getInverse().getName(); @@ -691,11 +696,13 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { .getRequiredPersistentEntity(association.getInverse().getTypeInformation()); String keyspace = ref.getKeySpace(); - Object refId = ref.getPropertyAccessor(refObject).getProperty(ref.getIdProperty()); + if (keyspace != null) { + Object refId = ref.getPropertyAccessor(refObject).getProperty(ref.getRequiredIdProperty()); - if (refId != null) { - String propertyStringPath = (!path.isEmpty() ? path + "." : "") + association.getInverse().getName(); - sink.getBucket().put(propertyStringPath, toBytes(keyspace + ":" + refId)); + if (refId != null) { + String propertyStringPath = (!path.isEmpty() ? path + "." : "") + association.getInverse().getName(); + sink.getBucket().put(propertyStringPath, toBytes(keyspace + ":" + refId)); + } } } }); @@ -708,7 +715,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { * @param typeHint * @param sink */ - private void writeCollection(String keyspace, String path, @Nullable Iterable values, TypeInformation typeHint, + private void writeCollection(@Nullable String keyspace, String path, @Nullable Iterable values, + TypeInformation typeHint, RedisData sink) { if (values == null) { @@ -769,6 +777,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { } } + @Nullable private Object readCollectionOrArray(String path, Class collectionType, Class valueType, Bucket bucket) { List keys = new ArrayList<>(bucket.extractAllKeysFor(path)); @@ -807,7 +816,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { * @param source * @param sink */ - private void writeMap(String keyspace, String path, Class mapValueType, Map source, RedisData sink) { + private void writeMap(@Nullable String keyspace, String path, Class mapValueType, Map source, + RedisData sink) { if (CollectionUtils.isEmpty(source)) { return; @@ -905,6 +915,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { return target.isEmpty() ? null : target; } + @Nullable private Object extractMapKeyForPath(String path, String key, Class targetType) { String regex = "^(" + Pattern.quote(path) + "\\.\\[)(.*?)(\\])"; @@ -1173,9 +1184,9 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { public static final String DELIMITTER = ":"; public static final String PHANTOM_SUFFIX = DELIMITTER + PHANTOM; - private String keyspace; - private String id; - private boolean phantomKey; + private final String keyspace; + private final String id; + private final boolean phantomKey; private KeyspaceIdentifier(String keyspace, String id, boolean phantomKey) { @@ -1215,7 +1226,7 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { * @param key the key. * @return {@literal true} if the key is valid. */ - public static boolean isValid(String key) { + public static boolean isValid(@Nullable String key) { if (key == null) { return false; @@ -1252,9 +1263,9 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { public static final byte DELIMITTER = ':'; public static final byte[] PHANTOM_SUFFIX = ByteUtils.concat(new byte[] { DELIMITTER }, PHANTOM); - private byte[] keyspace; - private byte[] id; - private boolean phantomKey; + private final byte[] keyspace; + private final byte[] id; + private final boolean phantomKey; private BinaryKeyspaceIdentifier(byte[] keyspace, byte[] id, boolean phantomKey) { diff --git a/src/main/java/org/springframework/data/redis/core/convert/RedisData.java b/src/main/java/org/springframework/data/redis/core/convert/RedisData.java index bd3640e32..70435baa3 100644 --- a/src/main/java/org/springframework/data/redis/core/convert/RedisData.java +++ b/src/main/java/org/springframework/data/redis/core/convert/RedisData.java @@ -76,7 +76,7 @@ public class RedisData { * * @param id */ - public void setId(String id) { + public void setId(@Nullable String id) { this.id = id; } @@ -134,7 +134,7 @@ public class RedisData { /** * @param keyspace */ - public void setKeyspace(String keyspace) { + public void setKeyspace(@Nullable String keyspace) { this.keyspace = keyspace; }