DATAREDIS-471 - Polishing.

Update reference documentation. Enhance JavaDoc. Remove destroy of managed bean. Refactor property update of writePartialUpdate into own method. Remove trailing whitespaces in JavaDoc.

Original pull request: #191.
This commit is contained in:
Mark Paluch
2016-05-24 16:30:23 +02:00
parent 76229c10a4
commit d88d992f6d
16 changed files with 152 additions and 150 deletions

View File

@@ -496,7 +496,7 @@ Indexes set on properties of referenced types will not be resolved.
[[redis.repositories.partial-updates]] [[redis.repositories.partial-updates]]
== Persisting Partial Updates == Persisting Partial Updates
In some cases it is not necessary to load and rewrite the entire entity just to set a new value within it. A session timestamp for last active time might be such a scenario where you just want to alter one property. In some cases it is not necessary to load and rewrite the entire entity just to set a new value within it. A session timestamp for last active time might be such a scenario where you just want to alter one property.
`PartialUpdate` allows to define `set`, `delete` actions on existing objects while taking care of updating potential expiration times of the entity itself as well as index structures. `PartialUpdate` allows to define `set` and `delete` actions on existing objects while taking care of updating potential expiration times of the entity itself as well as index structures.
.Sample Partial Update .Sample Partial Update
==== ====
@@ -521,7 +521,7 @@ update = new PartialUpdate<Person>("e2c7dcee", Person.class)
template.update(update); template.update(update);
---- ----
<1> Set the simple property _firstname_ to _mat_ <1> Set the simple property _firstname_ to _mat_.
<2> Set the simple property _address.city_ to _emond's field_ without having to pass in the entire object. This does not work when a custom conversion is registered. <2> Set the simple property _address.city_ to _emond's field_ without having to pass in the entire object. This does not work when a custom conversion is registered.
<3> Remove the property _age_. <3> Remove the property _age_.
<4> Set complex property _address_. <4> Set complex property _address_.

View File

@@ -150,6 +150,7 @@ class IndexWriter {
protected void removeKeyFromExistingIndexes(byte[] key, IndexedData indexedData) { protected void removeKeyFromExistingIndexes(byte[] key, IndexedData indexedData) {
Assert.notNull(indexedData, "IndexedData must not be null!"); Assert.notNull(indexedData, "IndexedData must not be null!");
Set<byte[]> existingKeys = connection Set<byte[]> existingKeys = connection
.keys(toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":*")); .keys(toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":*"));
@@ -217,7 +218,8 @@ class IndexWriter {
} }
throw new InvalidDataAccessApiUsageException(String.format( throw new InvalidDataAccessApiUsageException(String.format(
"Cannot convert %s to binary representation for index key generation. Are you missing a Converter? Did you register a non PathBasedRedisIndexDefinition that might apply to a complex type?", "Cannot convert %s to binary representation for index key generation. "
+ "Are you missing a Converter? Did you register a non PathBasedRedisIndexDefinition that might apply to a complex type?",
source.getClass())); source.getClass()));
} }

View File

@@ -114,6 +114,7 @@ public class PartialUpdate<T> {
PartialUpdate<T> update = new PartialUpdate<T>(this.id, this.target, this.value, this.refreshTtl, PartialUpdate<T> update = new PartialUpdate<T>(this.id, this.target, this.value, this.refreshTtl,
this.propertyUpdates); this.propertyUpdates);
update.propertyUpdates.add(new PropertyUpdate(UpdateCommand.SET, path, value)); update.propertyUpdates.add(new PropertyUpdate(UpdateCommand.SET, path, value));
return update; return update;
} }
@@ -130,6 +131,7 @@ public class PartialUpdate<T> {
PartialUpdate<T> update = new PartialUpdate<T>(this.id, this.target, this.value, this.refreshTtl, PartialUpdate<T> update = new PartialUpdate<T>(this.id, this.target, this.value, this.refreshTtl,
this.propertyUpdates); this.propertyUpdates);
update.propertyUpdates.add(new PropertyUpdate(UpdateCommand.DEL, path)); update.propertyUpdates.add(new PropertyUpdate(UpdateCommand.DEL, path));
return update; return update;
} }

View File

@@ -140,29 +140,6 @@ public class RedisKeyValueTemplate extends KeyValueTemplate {
super.update(objectToUpdate); super.update(objectToUpdate);
} }
/*
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.KeyValueTemplate#destroy()
*/
@Override
public void destroy() throws Exception {
execute(new RedisKeyValueCallback<Void>() {
@Override
public Void doInRedis(RedisKeyValueAdapter adapter) {
try {
adapter.destroy();
} catch (Exception e) {
throw new RedisSystemException(e.getMessage(), e);
}
return null;
}
});
}
protected void doPartialUpdate(final PartialUpdate<?> update) { protected void doPartialUpdate(final PartialUpdate<?> update) {
execute(new RedisKeyValueCallback<Void>() { execute(new RedisKeyValueCallback<Void>() {

View File

@@ -72,6 +72,9 @@ public class CompositeIndexResolver implements IndexResolver {
return data; return data;
} }
/* (non-Javadoc)
* @see org.springframework.data.redis.core.convert.IndexResolver#resolveIndexesFor(java.lang.String, java.lang.String, org.springframework.data.util.TypeInformation, java.lang.Object)
*/
@Override @Override
public Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation, public Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation,
Object value) { Object value) {

View File

@@ -38,6 +38,15 @@ public interface IndexResolver {
*/ */
Set<IndexedData> resolveIndexesFor(TypeInformation<?> typeInformation, Object value); Set<IndexedData> resolveIndexesFor(TypeInformation<?> typeInformation, Object value);
/**
* Resolves all indexes for given type information / value combination.
*
* @param keyspace must not be {@literal null}.
* @param path must not be {@literal null}.
* @param typeInformation must not be {@literal null}.
* @param value the actual value. Can be {@literal null}.
* @return never {@literal null}.
*/
Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation, Object value); Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation, Object value);
} }

View File

@@ -108,6 +108,7 @@ import org.springframework.util.comparator.NullSafeComparator;
* *
* @author Christoph Strobl * @author Christoph Strobl
* @author Greg Turnquist * @author Greg Turnquist
* @author Mark Paluch
* @since 1.7 * @since 1.7
*/ */
public class MappingRedisConverter implements RedisConverter, InitializingBean { public class MappingRedisConverter implements RedisConverter, InitializingBean {
@@ -394,8 +395,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
RedisPersistentEntity<?> entity = mappingContext.getPersistentEntity(update.getTarget()); RedisPersistentEntity<?> entity = mappingContext.getPersistentEntity(update.getTarget());
write(update.getValue(), sink); write(update.getValue(), sink);
if (sink.getBucket().keySet().contains("_class")) { if (sink.getBucket().keySet().contains(TYPE_HINT_ALIAS)) {
sink.getBucket().put("_class", null); // overwrite stuff in here sink.getBucket().put(TYPE_HINT_ALIAS, null); // overwrite stuff in here
} }
if (update.isRefreshTtl() && !update.getPropertyUpdates().isEmpty()) { if (update.isRefreshTtl() && !update.getPropertyUpdates().isEmpty()) {
@@ -411,89 +412,98 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
String path = pUpdate.getPropertyPath(); String path = pUpdate.getPropertyPath();
if (UpdateCommand.SET.equals(pUpdate.getCmd())) { if (UpdateCommand.SET.equals(pUpdate.getCmd())) {
writePartialPropertyUpdate(update, pUpdate, sink, entity, path);
KeyValuePersistentProperty targetProperty = getTargetPropertyOrNullForPath(path, update.getTarget());
if (targetProperty == null) {
targetProperty = getTargetPropertyOrNullForPath(path.replaceAll("\\.\\[.*\\]", ""), update.getTarget());
TypeInformation<?> ti = targetProperty == null ? ClassTypeInformation.OBJECT
: (targetProperty.isMap()
? (targetProperty.getTypeInformation().getMapValueType() != null
? targetProperty.getTypeInformation().getMapValueType() : ClassTypeInformation.OBJECT)
: targetProperty.getTypeInformation().getActualType());
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(), ti, sink);
continue;
}
if (targetProperty.isAssociation()) {
if (targetProperty.isCollectionLike()) {
KeyValuePersistentEntity<?> ref = mappingContext.getPersistentEntity(
targetProperty.getAssociation().getInverse().getTypeInformation().getComponentType().getActualType());
int i = 0;
for (Object o : (Collection<?>) pUpdate.getValue()) {
Object refId = ref.getPropertyAccessor(o).getProperty(ref.getIdProperty());
sink.getBucket().put(pUpdate.getPropertyPath() + ".[" + i + "]",
toBytes(ref.getKeySpace() + ":" + refId));
i++;
}
} else {
KeyValuePersistentEntity<?> ref = mappingContext
.getPersistentEntity(targetProperty.getAssociation().getInverse().getTypeInformation());
Object refId = ref.getPropertyAccessor(pUpdate.getValue()).getProperty(ref.getIdProperty());
sink.getBucket().put(pUpdate.getPropertyPath(), toBytes(ref.getKeySpace() + ":" + refId));
}
}
else if (targetProperty.isCollectionLike()) {
Collection<?> collection = pUpdate.getValue() instanceof Collection ? (Collection<?>) pUpdate.getValue()
: Collections.<Object> singleton(pUpdate.getValue());
writeCollection(entity.getKeySpace(), pUpdate.getPropertyPath(), collection,
targetProperty.getTypeInformation().getActualType(), sink);
} else if (targetProperty.isMap()) {
Map<Object, Object> map = new HashMap<Object, Object>();
if (pUpdate.getValue() instanceof Map) {
map.putAll((Map<?, ?>) pUpdate.getValue());
} else if (pUpdate.getValue() instanceof Map.Entry) {
map.put(((Map.Entry<?, ?>) pUpdate.getValue()).getKey(), ((Map.Entry<?, ?>) pUpdate.getValue()).getValue());
} else {
throw new MappingException(
String.format("Cannot set update value for map property '%s' to '%s'. Please use a Map or Map.Entry.",
pUpdate.getPropertyPath(), pUpdate.getValue()));
}
writeMap(entity.getKeySpace(), pUpdate.getPropertyPath(), targetProperty.getMapValueType(), map, sink);
} else {
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(),
targetProperty.getTypeInformation(), sink);
Set<IndexedData> data = indexResolver.resolveIndexesFor(entity.getKeySpace(), pUpdate.getPropertyPath(),
targetProperty.getTypeInformation(), pUpdate.getValue());
if (data.isEmpty()) {
data = indexResolver.resolveIndexesFor(entity.getKeySpace(), pUpdate.getPropertyPath(),
targetProperty.getOwner().getTypeInformation(), pUpdate.getValue());
}
sink.addIndexedData(data);
}
} }
} }
} }
/**
* @param update
* @param pUpdate
* @param sink
* @param entity
* @param path
*/
private void writePartialPropertyUpdate(PartialUpdate<?> update, PropertyUpdate pUpdate, RedisData sink,
RedisPersistentEntity<?> entity, String path) {
KeyValuePersistentProperty targetProperty = getTargetPropertyOrNullForPath(path, update.getTarget());
if (targetProperty == null) {
targetProperty = getTargetPropertyOrNullForPath(path.replaceAll("\\.\\[.*\\]", ""), update.getTarget());
TypeInformation<?> ti = targetProperty == null ? ClassTypeInformation.OBJECT
: (targetProperty.isMap()
? (targetProperty.getTypeInformation().getMapValueType() != null
? targetProperty.getTypeInformation().getMapValueType() : ClassTypeInformation.OBJECT)
: targetProperty.getTypeInformation().getActualType());
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(), ti, sink);
return;
}
if (targetProperty.isAssociation()) {
if (targetProperty.isCollectionLike()) {
KeyValuePersistentEntity<?> ref = mappingContext.getPersistentEntity(
targetProperty.getAssociation().getInverse().getTypeInformation().getComponentType().getActualType());
int i = 0;
for (Object o : (Collection<?>) pUpdate.getValue()) {
Object refId = ref.getPropertyAccessor(o).getProperty(ref.getIdProperty());
sink.getBucket().put(pUpdate.getPropertyPath() + ".[" + i + "]", toBytes(ref.getKeySpace() + ":" + refId));
i++;
}
} else {
KeyValuePersistentEntity<?> ref = mappingContext
.getPersistentEntity(targetProperty.getAssociation().getInverse().getTypeInformation());
Object refId = ref.getPropertyAccessor(pUpdate.getValue()).getProperty(ref.getIdProperty());
sink.getBucket().put(pUpdate.getPropertyPath(), toBytes(ref.getKeySpace() + ":" + refId));
}
} else if (targetProperty.isCollectionLike()) {
Collection<?> collection = pUpdate.getValue() instanceof Collection ? (Collection<?>) pUpdate.getValue()
: Collections.<Object> singleton(pUpdate.getValue());
writeCollection(entity.getKeySpace(), pUpdate.getPropertyPath(), collection,
targetProperty.getTypeInformation().getActualType(), sink);
} else if (targetProperty.isMap()) {
Map<Object, Object> map = new HashMap<Object, Object>();
if (pUpdate.getValue() instanceof Map) {
map.putAll((Map<?, ?>) pUpdate.getValue());
} else if (pUpdate.getValue() instanceof Entry) {
map.put(((Entry<?, ?>) pUpdate.getValue()).getKey(), ((Entry<?, ?>) pUpdate.getValue()).getValue());
} else {
throw new MappingException(
String.format("Cannot set update value for map property '%s' to '%s'. Please use a Map or Map.Entry.",
pUpdate.getPropertyPath(), pUpdate.getValue()));
}
writeMap(entity.getKeySpace(), pUpdate.getPropertyPath(), targetProperty.getMapValueType(), map, sink);
} else {
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(),
targetProperty.getTypeInformation(), sink);
Set<IndexedData> data = indexResolver.resolveIndexesFor(entity.getKeySpace(), pUpdate.getPropertyPath(),
targetProperty.getTypeInformation(), pUpdate.getValue());
if (data.isEmpty()) {
data = indexResolver.resolveIndexesFor(entity.getKeySpace(), pUpdate.getPropertyPath(),
targetProperty.getOwner().getTypeInformation(), pUpdate.getValue());
}
sink.addIndexedData(data);
}
}
KeyValuePersistentProperty getTargetPropertyOrNullForPath(String path, Class<?> type) { KeyValuePersistentProperty getTargetPropertyOrNullForPath(String path, Class<?> type) {
try { try {
@@ -589,10 +599,10 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
} }
}); });
writeAssiciation(keyspace, path, entity, value, sink); writeAssociation(path, entity, value, sink);
} }
private void writeAssiciation(final String keyspace, final String path, final KeyValuePersistentEntity<?> entity, private void writeAssociation(final String path, final KeyValuePersistentEntity<?> entity,
final Object value, final RedisData sink) { final Object value, final RedisData sink) {
if (value == null) { if (value == null) {

View File

@@ -62,7 +62,7 @@ public class PathIndexResolver implements IndexResolver {
/** /**
* Creates new {@link PathIndexResolver} with given {@link IndexConfiguration}. * Creates new {@link PathIndexResolver} with given {@link IndexConfiguration}.
* *
* @param mapppingContext must not be {@literal null}. * @param mappingContext must not be {@literal null}.
*/ */
public PathIndexResolver(RedisMappingContext mappingContext) { public PathIndexResolver(RedisMappingContext mappingContext) {
@@ -80,6 +80,9 @@ public class PathIndexResolver implements IndexResolver {
null, value); null, value);
} }
/* (non-Javadoc)
* @see org.springframework.data.redis.core.convert.IndexResolver#resolveIndexesFor(java.lang.String, java.lang.String, org.springframework.data.util.TypeInformation, java.lang.Object)
*/
@Override @Override
public Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation, public Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation,
Object value) { Object value) {
@@ -134,7 +137,7 @@ public class PathIndexResolver implements IndexResolver {
final Iterable<?> iterable; final Iterable<?> iterable;
if (Iterable.class.isAssignableFrom(propertyValue.getClass())) { if (Iterable.class.isAssignableFrom(propertyValue.getClass())) {
iterable = (Iterable) propertyValue; iterable = (Iterable<?>) propertyValue;
} else if (propertyValue.getClass().isArray()) { } else if (propertyValue.getClass().isArray()) {
iterable = CollectionUtils.arrayToList(propertyValue); iterable = CollectionUtils.arrayToList(propertyValue);
} else { } else {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2015 the original author or authors. * Copyright 2015-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2015 the original author or authors. * Copyright 2015-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -16,7 +16,10 @@
package org.springframework.data.redis.core.convert; package org.springframework.data.redis.core.convert;
/** /**
* {@link RemoveIndexedData} represents a removed index entry from a secondary index for a property path in a given keyspace.
*
* @author Christoph Strobl * @author Christoph Strobl
* @author Mark Paluch
*/ */
public class RemoveIndexedData implements IndexedData { public class RemoveIndexedData implements IndexedData {

View File

@@ -119,6 +119,9 @@ public class SpelIndexResolver implements IndexResolver {
return indexes; return indexes;
} }
/* (non-Javadoc)
* @see org.springframework.data.redis.core.convert.IndexResolver#resolveIndexesFor(java.lang.String, java.lang.String, org.springframework.data.util.TypeInformation, java.lang.Object)
*/
@Override @Override
public Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation, public Set<IndexedData> resolveIndexesFor(String keyspace, String path, TypeInformation<?> typeInformation,
Object value) { Object value) {

View File

@@ -15,10 +15,7 @@
*/ */
package org.springframework.data.redis.core; package org.springframework.data.redis.core;
import static org.hamcrest.core.Is.*; import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsCollectionContaining.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.hamcrest.core.IsNot.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.Arrays; import java.util.Arrays;

View File

@@ -15,9 +15,7 @@
*/ */
package org.springframework.data.redis.core; package org.springframework.data.redis.core;
import static org.hamcrest.core.Is.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.core.IsCollectionContaining.*;
import static org.hamcrest.core.IsEqual.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.ArrayList; import java.util.ArrayList;
@@ -109,6 +107,7 @@ public class RedisKeyValueTemplateTests {
}); });
template.destroy(); template.destroy();
adapter.destroy();
} }
/** /**

View File

@@ -15,11 +15,7 @@
*/ */
package org.springframework.data.redis.core.convert; package org.springframework.data.redis.core.convert;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsCollectionContaining.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Matchers.*; import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@@ -1586,7 +1582,6 @@ public class MappingRedisConverterUnitTests {
} }
/** /**
* <<<<<<< HEAD
* *
* @see DATAREDIS-509 * @see DATAREDIS-509
*/ */

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2015 the original author or authors. * Copyright 2015-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -15,8 +15,7 @@
*/ */
package org.springframework.data.redis.core.mapping; package org.springframework.data.redis.core.mapping;
import static org.hamcrest.core.Is.*; import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Before; import org.junit.Before;