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 549135621..bff2eb4e8 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 @@ -344,6 +344,14 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean { if (!customConversions.hasCustomWriteTarget(source.getClass())) { typeMapper.writeType(ClassUtils.getUserClass(source), sink); } + + if (entity == null) { + + typeMapper.writeType(ClassUtils.getUserClass(source), sink); + sink.getBucket().put("_raw", conversionService.convert(source, byte[].class)); + return; + } + sink.setKeyspace(entity.getKeySpace()); writeInternal(entity.getKeySpace(), "", source, entity.getTypeInformation(), sink); diff --git a/src/main/java/org/springframework/data/redis/hash/ConvertingHashMapper.java b/src/main/java/org/springframework/data/redis/hash/ConvertingHashMapper.java new file mode 100644 index 000000000..a19161aa6 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/hash/ConvertingHashMapper.java @@ -0,0 +1,152 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.hash; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.springframework.data.redis.core.convert.CustomConversions; +import org.springframework.data.redis.core.convert.IndexResolver; +import org.springframework.data.redis.core.convert.IndexedData; +import org.springframework.data.redis.core.convert.MappingRedisConverter; +import org.springframework.data.redis.core.convert.RedisData; +import org.springframework.data.redis.core.convert.ReferenceResolver; +import org.springframework.data.redis.core.mapping.RedisMappingContext; +import org.springframework.data.util.TypeInformation; + +/** + * {@link HashMapper} based on {@link MappingRedisConverter}. Does supports nested properties and simple types like + * {@link String}. + * + *
+ * 
+ * class Person {
+ *
+ *   String firstname;
+ *
+ *   List<String> nicknames;
+ *   List<Person> coworkers;
+ *
+ *   Address address;
+ * }
+ * 
+ * 
+ * + * The above is represented as: + * + *
+ * 
+ * _class=org.example.Person
+ * firstname=rand
+ * lastname=al'thor
+ * coworkers.[0].firstname=mat
+ * coworkers.[0].nicknames.[0]=prince of the ravens
+ * coworkers.[1].firstname=perrin
+ * coworkers.[1].address.city=two rivers
+ * 
+ * 
+ * + * @author Christoph Strobl + * @since 1.8 + */ +public class ConvertingHashMapper implements HashMapper { + + private final MappingRedisConverter converter; + + /** + * Creates new {@link ConvertingHashMapper}. + */ + public ConvertingHashMapper() { + this(new CustomConversions()); + } + + /** + * Creates new {@link ConvertingHashMapper}. + * + * @param customConversions can be {@literal null}. + */ + public ConvertingHashMapper(CustomConversions customConversions) { + + MappingRedisConverter mappingConverter = new MappingRedisConverter(new RedisMappingContext(), + new NoOpIndexResolver(), new NoOpReferenceResolver()); + mappingConverter.setCustomConversions(customConversions == null ? new CustomConversions() : customConversions); + mappingConverter.afterPropertiesSet(); + + converter = mappingConverter; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.hash.HashMapper#toHash(java.lang.Object) + */ + @Override + public Map toHash(Object source) { + + if (source == null) { + return Collections.emptyMap(); + } + + RedisData sink = new RedisData(); + converter.write(source, sink); + return sink.getBucket().rawMap(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.hash.HashMapper#fromHash(java.util.Map) + */ + @Override + public Object fromHash(Map hash) { + + if (hash == null || hash.isEmpty()) { + return null; + } + + return converter.read(Object.class, new RedisData(hash)); + } + + /** + * {@link ReferenceResolver} implementation always returning an empty {@link Map}. + * + * @author Christoph Strobl + */ + private static class NoOpReferenceResolver implements ReferenceResolver { + + private static final Map NO_REFERENCE = Collections.emptyMap(); + + @Override + public Map resolveReference(Serializable id, String keyspace) { + return NO_REFERENCE; + } + } + + /** + * {@link IndexResolver} always returning an empty {@link Set}. + * + * @author Christoph Strobl + */ + private static class NoOpIndexResolver implements IndexResolver { + + private static final Set NO_INDEXES = Collections.emptySet(); + + @Override + public Set resolveIndexesFor(TypeInformation typeInformation, Object value) { + return NO_INDEXES; + } + } +} diff --git a/src/test/java/org/springframework/data/redis/mapping/ConvertingHashMapperTests.java b/src/test/java/org/springframework/data/redis/mapping/ConvertingHashMapperTests.java new file mode 100644 index 000000000..f2777bf37 --- /dev/null +++ b/src/test/java/org/springframework/data/redis/mapping/ConvertingHashMapperTests.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.mapping; + +import org.junit.Test; +import org.springframework.data.redis.hash.ConvertingHashMapper; + +/** + * @author Christoph Strobl + */ +public class ConvertingHashMapperTests extends AbstractHashMapperTest { + + protected ConvertingHashMapper mapperFor(Class t) { + return new ConvertingHashMapper(); + } + + /** + * @see DATAREDIS-503 + */ + @Test + public void testSimpleType() { + assertBackAndForwardMapping(new Integer(100)); + } +}