DATAKV-105 - Polish implementation after recent addition of features.

Remove obsolete method hasKeyspace() from KeyValueAdapter. Transition from List to Iterable for find methods in KeyValueTemplate.

We now offer a plug able KeySpace resolution by providing a KeySpaceResolver via the KeyValueMappingContext. By default an AnnotationBasedKeySpaceResolver will be used to examine the type for @KeySpace. In case of non explicit keySpace we fall back to using the class' fully-qualified name as the default key space.

Key space resolution is now fixed on resolving @KeySpace on a given PersistentEntity. In case no annotation key space can be found we by default fall back to a class name based key space. It is possible to configure the fallback via the MappingContext.

Refactored names & visibility of types required for tests.
Removed shortcut for returning results not having an explicit key space, since we now can no longer rely on the type name strategy dividing non explicit types into separate key spaces.

Original pull request: #11.
This commit is contained in:
Christoph Strobl
2015-05-13 13:43:25 +02:00
committed by Oliver Gierke
parent 02759f58ea
commit f19a6c0faf
21 changed files with 598 additions and 230 deletions

View File

@@ -14,17 +14,17 @@ The Key/Value abstraction within Spring Data Commons requires an `Adapter` shiel
----
interface KeyValueOperations {
<T> T insert(T objectToInsert); <1>
<T> T insert(T objectToInsert); <1>
void update(Object objectToUpdate); <2>
void update(Object objectToUpdate); <2>
void delete(Class<?> type); <3>
void delete(Class<?> type); <3>
<T> T findById(Serializable id, Class<T> type); <4>
<T> T findById(Serializable id, Class<T> type); <4>
<T> List<T> findAllOf(Class<T> type); <5>
<T> Iterable<T> findAllOf(Class<T> type); <5>
<T> List<T> find(KeyValueQuery<?> query, Class<T> type); <6>
<T> Iterable<T> find(KeyValueQuery<?> query, Class<T> type); <6>
//... more functionality omitted.
@@ -44,7 +44,7 @@ In its very basic shape the `KeyValueTemplate` uses a `MapAdaper` wrapping a `Co
NOTE: The used `KeyValueAdapter` does the heavy lifting when it comes to storing and retrieving data. The data structure used will influence performance and/or multi threading behavior.
One may choose to use a different type or preinitialize the adapter with some values, and can do so using `MapKeyValueAdapterFactory`.
One may choose to use a different type or preinitialize the adapter with some values, and can do so using various constructors on `MapKeyValueAdapter`.
[source, java]
----
@@ -55,13 +55,7 @@ public KeyValueOperations keyValueTemplate() {
@Bean
public KeyValueAdapter keyValueAdapter() {
MapKeyValueAdapterFactory factory = new MapKeyValueAdapterFactory();
factory.setMapType(ConcurrentSkipListMap.class);
factory.setInitialValuesForKeyspace("lennister", singletonMap("1", "tyrion"));
factory.setInitialValuesForKeyspace("stark", singletonMap("1", "sansa"));
return factory.getAdapter();
return new MapKeyValueAdapter(ConcurrentHashMap.class);
}
----
@@ -91,6 +85,29 @@ template.findAllOf(User.class); <2>
<1> Returns all entities for keyspace "persons".
<2> Returns only elements of type `User` stored in keyspace "persons".
[[key-value.keyspaces-custom]]
=== Custom KeySpace Annotation
It is possible to compose own `KeySpace` annotations for a more domain centric usage by annotating one of the attibutes with `@KeySpace`.
NOTE: The composed annotation needs to inherit `@Persistent`.
[source, java]
----
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
static @interface CacheCentricAnnotation {
@KeySpace String cacheRegion() default "";
}
@CacheCentricAnnotation(cacheRegion = "customers")
class Customer {
//...
}
----
[[key-value.template-query]]
== Querying

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2015 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.keyvalue.core;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Most trivial implementation of {@link KeySpaceResolver} returning the {@link Class#getName()}.
*
* @author Christoph Strobl
*/
public enum ClassNameKeySpaceResolver implements KeySpaceResolver {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.KeySpaceResolver#resolveKeySpace(java.lang.Class)
*/
@Override
public String resolveKeySpace(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
return ClassUtils.getUserClass(type).getName();
}
}

View File

@@ -53,16 +53,28 @@ public class ForwardingCloseableIterator<K, V> implements CloseableIterator<Map.
this.closeHandler = closeHandler;
}
/*
* (non-Javadoc)
* @see java.util.Iterator#hasNext()
*/
@Override
public boolean hasNext() {
return delegate.hasNext();
}
/*
* (non-Javadoc)
* @see java.util.Iterator#next()
*/
@Override
public Map.Entry<K, V> next() {
return delegate.next();
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.CloseableIterator#close()
*/
@Override
public void close() {
if (closeHandler != null) {

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2015 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.keyvalue.core;
/**
* {@link KeySpaceResolver} determines the {@literal keyspace} a given type is assigned to. A keyspace in this context
* is a specific region/collection/grouping of elements sharing a common keyrange. <br />
*
* @author Christoph Strobl
*/
public interface KeySpaceResolver {
/**
* Determine the {@literal keySpace} to use for a given type.
*
* @param type must not be {@literal null}.
* @return
*/
String resolveKeySpace(Class<?> type);
}

View File

@@ -76,7 +76,7 @@ public interface KeyValueAdapter extends DisposableBean {
Iterable<?> getAllOf(Serializable keyspace);
/**
* Returns a {@link KeyValueIterator} that iterates over all entries.
* Returns a {@link KeyValueIterator} that iterates over all entries.
*
* @param keyspace
* @return
@@ -119,12 +119,4 @@ public interface KeyValueAdapter extends DisposableBean {
* @return
*/
long count(KeyValueQuery<?> query, Serializable keyspace);
/**
* Check if values from the given keyspace are contained in the underlying key-value store.
*
* @param keyspace
* @return true if {@literal keyspace} already present in adapter.
*/
boolean hasKeyspace(Serializable keyspace);
}

View File

@@ -15,15 +15,12 @@
*/
package org.springframework.data.keyvalue.core;
import static org.springframework.data.keyvalue.core.KeySpaceUtils.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
@@ -35,6 +32,8 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.domain.Sort;
import org.springframework.data.keyvalue.core.event.KeyValueEvent;
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity;
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
import org.springframework.data.mapping.PersistentEntity;
@@ -43,7 +42,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Basic implementation of {@link KeyValueOperations}.
@@ -57,8 +55,8 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
private static final PersistenceExceptionTranslator DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR = new KeyValuePersistenceExceptionTranslator();
private final KeyValueAdapter adapter;
private final ConcurrentHashMap<Class<?>, String> keySpaceCache = new ConcurrentHashMap<Class<?>, String>();
private final MappingContext<? extends PersistentEntity<?, ? extends PersistentProperty<?>>, ? extends PersistentProperty<?>> mappingContext;
private final MappingContext<? extends KeyValuePersistentEntity<?>, ? extends KeyValuePersistentProperty> mappingContext;
private final IdentifierGenerator identifierGenerator;
private ApplicationEventPublisher eventPublisher;
private final Set<KeyValueEvent.Type> eventTypesToPublish = new HashSet<KeyValueEvent.Type>(4);
@@ -80,10 +78,8 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
* @param adapter must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
@SuppressWarnings("rawtypes")
public KeyValueTemplate(
KeyValueAdapter adapter,
MappingContext<? extends PersistentEntity<?, ? extends PersistentProperty>, ? extends PersistentProperty<?>> mappingContext) {
public KeyValueTemplate(KeyValueAdapter adapter,
MappingContext<? extends KeyValuePersistentEntity<?>, ? extends KeyValuePersistentProperty> mappingContext) {
Assert.notNull(adapter, "Adapter must not be null!");
Assert.notNull(mappingContext, "MappingContext must not be null!");
@@ -192,20 +188,20 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
* @see org.springframework.data.keyvalue.core.KeyValueOperations#findAllOf(java.lang.Class)
*/
@Override
public <T> List<T> findAll(final Class<T> type) {
public <T> Iterable<T> findAll(final Class<T> type) {
Assert.notNull(type, "Type to fetch must not be null!");
return execute(new KeyValueCallback<List<T>>() {
return execute(new KeyValueCallback<Iterable<T>>() {
@SuppressWarnings("unchecked")
@Override
public List<T> doInKeyValue(KeyValueAdapter adapter) {
public Iterable<T> doInKeyValue(KeyValueAdapter adapter) {
Iterable<?> values = adapter.getAllOf(resolveKeySpace(type));
if (getKeySpace(type) == null) {
return new ArrayList<T>(IterableConverter.toList((Iterable<T>) values));
if (values == null) {
return Collections.emptySet();
}
ArrayList<T> filtered = new ArrayList<T>();
@@ -242,7 +238,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
Object result = adapter.get(id, keyspace);
if (result == null || getKeySpace(type) == null || typeCheck(type, result)) {
if (result == null || typeCheck(type, result)) {
return (T) result;
}
@@ -355,18 +351,17 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
* @see org.springframework.data.keyvalue.core.KeyValueOperations#find(org.springframework.data.keyvalue.core.query.KeyValueQuery, java.lang.Class)
*/
@Override
public <T> List<T> find(final KeyValueQuery<?> query, final Class<T> type) {
public <T> Iterable<T> find(final KeyValueQuery<?> query, final Class<T> type) {
return execute(new KeyValueCallback<List<T>>() {
return execute(new KeyValueCallback<Iterable<T>>() {
@SuppressWarnings("unchecked")
@Override
public List<T> doInKeyValue(KeyValueAdapter adapter) {
public Iterable<T> doInKeyValue(KeyValueAdapter adapter) {
Iterable<?> result = adapter.find(query, resolveKeySpace(type));
if (getKeySpace(type) == null) {
return new ArrayList<T>(IterableConverter.toList((Iterable<T>) result));
if (result == null) {
return Collections.emptySet();
}
List<T> filtered = new ArrayList<T>();
@@ -388,7 +383,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
*/
@SuppressWarnings("rawtypes")
@Override
public <T> List<T> findAll(Sort sort, Class<T> type) {
public <T> Iterable<T> findAll(Sort sort, Class<T> type) {
return find(new KeyValueQuery(sort), type);
}
@@ -398,7 +393,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
*/
@SuppressWarnings("rawtypes")
@Override
public <T> List<T> findInRange(int offset, int rows, Class<T> type) {
public <T> Iterable<T> findInRange(int offset, int rows, Class<T> type) {
return find(new KeyValueQuery().skip(offset).limit(rows), type);
}
@@ -408,7 +403,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
*/
@SuppressWarnings("rawtypes")
@Override
public <T> List<T> findInRange(int offset, int rows, Sort sort, Class<T> type) {
public <T> Iterable<T> findInRange(int offset, int rows, Sort sort, Class<T> type) {
return find(new KeyValueQuery(sort).skip(offset).limit(rows), type);
}
@@ -480,29 +475,8 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
}
}
protected String resolveKeySpace(Class<?> type) {
Class<?> userClass = ClassUtils.getUserClass(type);
String potentialKeySpace = keySpaceCache.get(userClass);
if (potentialKeySpace != null) {
return potentialKeySpace;
}
String keySpaceString = null;
Object keySpace = getKeySpace(type);
if (keySpace != null) {
keySpaceString = keySpace.toString();
}
if (!StringUtils.hasText(keySpaceString)) {
keySpaceString = userClass.getName();
}
keySpaceCache.put(userClass, keySpaceString);
return keySpaceString;
private String resolveKeySpace(Class<?> type) {
return this.mappingContext.getPersistentEntity(type).getKeySpace();
}
private static boolean typeCheck(Class<?> requiredType, Object candidate) {
@@ -525,4 +499,5 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
eventPublisher.publishEvent(event);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2015 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
package org.springframework.data.keyvalue.core.mapping;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@@ -26,24 +26,39 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.style.ToStringCreator;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.annotation.KeySpace;
import org.springframework.data.keyvalue.core.KeySpaceUtils.MetaAnnotationUtils.AnnotationDescriptor;
import org.springframework.data.keyvalue.core.KeySpaceResolver;
import org.springframework.data.keyvalue.core.mapping.AnnotationBasedKeySpaceResolver.MetaAnnotationUtils.AnnotationDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* {@link AnnotationBasedKeySpaceResolver} looks up {@link Persistent} and checks for presence of either meta or direct
* usage of {@link KeySpace}. If non found it will default the keyspace to {@link Class#getName()}.
*
* @author Christoph Strobl
* @author Oliver Gierke
*/
abstract class KeySpaceUtils {
enum AnnotationBasedKeySpaceResolver implements KeySpaceResolver {
private KeySpaceUtils() {}
INSTANCE;
/**
* Looks up {@link Persistent} when used as meta annotation to find the {@link KeySpace} attribute.
*
* @return
/*
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.KeySpaceResolver#resolveKeySpace(java.lang.Class)
*/
public static Object getKeySpace(Class<?> type) {
@Override
public String resolveKeySpace(Class<?> type) {
Assert.notNull(type, "Type for keyspace for null!");
Class<?> userClass = ClassUtils.getUserClass(type);
Object keySpace = getKeySpace(userClass);
return keySpace != null ? keySpace.toString() : null;
}
private Object getKeySpace(Class<?> type) {
KeySpace keyspace = AnnotationUtils.findAnnotation(type, KeySpace.class);
@@ -51,8 +66,7 @@ abstract class KeySpaceUtils {
return AnnotationUtils.getValue(keyspace);
}
AnnotationDescriptor<Persistent> descriptor = KeySpaceUtils.MetaAnnotationUtils.findAnnotationDescriptor(type,
Persistent.class);
AnnotationDescriptor<Persistent> descriptor = MetaAnnotationUtils.findAnnotationDescriptor(type, Persistent.class);
if (descriptor != null && descriptor.getComposedAnnotation() != null) {
@@ -392,6 +406,6 @@ abstract class KeySpaceUtils {
}
}
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2015 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.keyvalue.core.mapping;
import org.springframework.data.keyvalue.core.KeySpaceResolver;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link KeyValuePersistentEntity} implementation that adds specific meta-data such as the {@literal keySpace}..
*
* @author Christoph Strobl
* @param <T>
*/
public class BasicKeyValuePersistentEntity<T> extends BasicPersistentEntity<T, KeyValuePersistentProperty> implements
KeyValuePersistentEntity<T> {
private final String keyspace;
/**
* @param information must not be {@literal null}.
* @param keySpaceResolver must not be {@literal null}.
*/
public BasicKeyValuePersistentEntity(TypeInformation<T> information, KeySpaceResolver fallbackKeySpaceResolver) {
super(information);
Assert.notNull(fallbackKeySpaceResolver, "FallbackKeySpaceResolver must not be null!");
String resolvedKeySpace = AnnotationBasedKeySpaceResolver.INSTANCE.resolveKeySpace(information.getType());
this.keyspace = StringUtils.hasText(resolvedKeySpace) ? resolvedKeySpace : fallbackKeySpaceResolver
.resolveKeySpace(information.getType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity#getKeySpace()
*/
@Override
public String getKeySpace() {
return this.keyspace;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2015 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.keyvalue.core.mapping;
import org.springframework.data.mapping.model.MutablePersistentEntity;
/**
* @author Christoph Strobl
* @param <T>
*/
public interface KeyValuePersistentEntity<T> extends MutablePersistentEntity<T, KeyValuePersistentProperty> {
/**
* Get the {@literal keySpace} a given entity assigns to.
*
* @return never {@literal null}.
*/
String getKeySpace();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 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.
@@ -18,26 +18,54 @@ package org.springframework.data.keyvalue.core.mapping.context;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.data.keyvalue.core.ClassNameKeySpaceResolver;
import org.springframework.data.keyvalue.core.KeySpaceResolver;
import org.springframework.data.keyvalue.core.mapping.BasicKeyValuePersistentEntity;
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity;
import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Default implementation of a {@link MappingContext} using {@link KeyValuePersistentEntity} and
* {@link KeyValuePersistentProperty} as primary abstractions.
*
* @author Christoph Strobl
*/
public class KeyValueMappingContext extends
AbstractMappingContext<BasicPersistentEntity<?, KeyValuePersistentProperty>, KeyValuePersistentProperty> {
AbstractMappingContext<KeyValuePersistentEntity<?>, KeyValuePersistentProperty> {
private final KeySpaceResolver fallbackKeySpaceResolver;
/**
* Creates new {@link KeyValueMappingContext} using an {@link ClassNameKeySpaceResolver} for {@literal keyspace}
* resolution.
*/
public KeyValueMappingContext() {
this(ClassNameKeySpaceResolver.INSTANCE);
}
/**
* Creates new {@link KeyValueMappingContext} using given {@link KeySpaceResolver}
*
* @param keySpaceResolver must not be {@literal null}.
*/
public KeyValueMappingContext(KeySpaceResolver fallbackKeySpaceResolver) {
Assert.notNull(fallbackKeySpaceResolver, "FallbackKeySpaceResolver must not be null!");
this.fallbackKeySpaceResolver = fallbackKeySpaceResolver;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
*/
@Override
protected <T> BasicPersistentEntity<?, KeyValuePersistentProperty> createPersistentEntity(
TypeInformation<T> typeInformation) {
return new BasicPersistentEntity<T, KeyValuePersistentProperty>(typeInformation);
protected <T> KeyValuePersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
return new BasicKeyValuePersistentEntity<T>(typeInformation, fallbackKeySpaceResolver);
}
/*
@@ -46,7 +74,7 @@ public class KeyValueMappingContext extends
*/
@Override
protected KeyValuePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
BasicPersistentEntity<?, KeyValuePersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
KeyValuePersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new KeyValuePersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
}

View File

@@ -17,9 +17,7 @@ package org.springframework.data.map;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.CollectionFactory;
@@ -163,17 +161,6 @@ public class MapKeyValueAdapter extends AbstractKeyValueAdapter {
getKeySpaceMap(keyspace).clear();
}
/*
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#hasKeyspace(java.io.Serializable)
*/
@Override
public boolean hasKeyspace(Serializable keyspace) {
Assert.notNull(keyspace, "Collection must not be null for lookup.");
return store.containsKey(keyspace);
}
/*
* (non-Javadoc)
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#clear()

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2015 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.keyvalue;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.annotation.KeySpace;
/**
* Custom composed {@link Persistent} annotation using {@link KeySpace} on name attribute.
*
* @author Christoph Strobl
*/
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface CustomKeySpaceAnnotation {
@KeySpace
String name() default "";
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2015 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.keyvalue;
import org.springframework.data.keyvalue.annotation.KeySpace;
/**
* Class that inherits its {@link KeySpace} from a super class annotated with a custom {@link CustomKeySpaceAnnotation} annotation.
* @author Christoph Strobl
*/
public class SubclassOfTypeWithCustomComposedKeySpaceAnnotation extends TypeWithCustomComposedKeySpaceAnnotation {
public SubclassOfTypeWithCustomComposedKeySpaceAnnotation(String name) {
super(name);
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2015 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.keyvalue;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Persistent;
import org.springframework.util.ObjectUtils;
/**
* A {@link Persistent} type with {@link CustomKeySpaceAnnotation}.
*
* @author Christoph Strobl
*/
@CustomKeySpaceAnnotation(name = "aliased")
public class TypeWithCustomComposedKeySpaceAnnotation {
@Id String id;
String name;
public TypeWithCustomComposedKeySpaceAnnotation(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof TypeWithCustomComposedKeySpaceAnnotation)) {
return false;
}
TypeWithCustomComposedKeySpaceAnnotation other = (TypeWithCustomComposedKeySpaceAnnotation) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.name, other.name)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2015 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.keyvalue;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.annotation.KeySpace;
/**
* A {@link Persistent} type with explict {@link KeySpace}.
*
* @author Christoph Strobl
*/
@KeySpace("rhaegar")
public class TypeWithDirectKeySpaceAnnotation {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2015 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.keyvalue;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.keyvalue.annotation.KeySpace;
/**
* A type inheriting {@link Persistent} from {@link TypeAlias} not having a {@link KeySpace} defined.
*
* @author Christoph Strobl
*/
@TypeAlias("foo")
public class TypeWithInhteritedPersistentAnnotationNotHavingKeySpace {
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2015 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.keyvalue;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.annotation.KeySpace;
/**
* A {@link Persistent} class without a defined {@link KeySpace}.
*
* @author Christoph Strobl
*/
@Persistent
public class TypeWithPersistentAnnotationNotHavingKeySpace {
}

View File

@@ -16,7 +16,7 @@
package org.springframework.data.keyvalue.core;
import static org.hamcrest.collection.IsCollectionWithSize.*;
import static org.hamcrest.collection.IsEmptyCollection.*;
import static org.hamcrest.collection.IsEmptyIterable.*;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
@@ -191,7 +191,7 @@ public class KeyValueTemplateTests {
operations.insert("2", FOO_TWO);
operations.insert("3", FOO_THREE);
assertThat(operations.findInRange(5, 5, Foo.class), empty());
assertThat(operations.findInRange(5, 5, Foo.class), emptyIterable());
}
/**

View File

@@ -22,10 +22,6 @@ import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -42,9 +38,8 @@ import org.springframework.context.ApplicationContext;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.keyvalue.annotation.KeySpace;
import org.springframework.data.keyvalue.TypeWithCustomComposedKeySpaceAnnotation;
import org.springframework.data.keyvalue.SubclassOfTypeWithCustomComposedKeySpaceAnnotation;
import org.springframework.data.keyvalue.core.event.KeyValueEvent;
import org.springframework.data.keyvalue.core.event.KeyValueEvent.DeleteEvent;
import org.springframework.data.keyvalue.core.event.KeyValueEvent.DropKeyspaceEvent;
@@ -66,8 +61,8 @@ public class KeyValueTemplateUnitTests {
private static final Foo FOO_ONE = new Foo("one");
private static final Foo FOO_TWO = new Foo("two");
private static final ClassWithTypeAlias ALIASED = new ClassWithTypeAlias("super");
private static final SubclassOfAliasedType SUBCLASS_OF_ALIASED = new SubclassOfAliasedType("sub");
private static final TypeWithCustomComposedKeySpaceAnnotation ALIASED = new TypeWithCustomComposedKeySpaceAnnotation("super");
private static final SubclassOfTypeWithCustomComposedKeySpaceAnnotation SUBCLASS_OF_ALIASED = new SubclassOfTypeWithCustomComposedKeySpaceAnnotation("sub");
private static final KeyValueQuery<String> STRING_QUERY = new KeyValueQuery<String>("foo == 'two'");
@@ -683,7 +678,7 @@ public class KeyValueTemplateUnitTests {
}
static class Bar {
class Bar {
String bar;
@@ -753,94 +748,4 @@ public class KeyValueTemplateUnitTests {
}
}
@ExplicitKeySpace(name = "aliased")
static class ClassWithTypeAlias {
@Id String id;
String name;
public ClassWithTypeAlias(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ClassWithTypeAlias)) {
return false;
}
ClassWithTypeAlias other = (ClassWithTypeAlias) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.name, other.name)) {
return false;
}
return true;
}
}
static class SubclassOfAliasedType extends ClassWithTypeAlias {
public SubclassOfAliasedType(String name) {
super(name);
}
}
@Persistent
static class EntityWithPersistentAnnotation {
}
@TypeAlias("foo")
static class AliasedEntity {
}
@KeySpace("rhaegar")
static class ClassWithDirectKeySpaceAnnotation {
}
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
private static @interface ExplicitKeySpace {
@KeySpace
String name() default "";
}
}

View File

@@ -13,39 +13,45 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
package org.springframework.data.keyvalue.core.mapping;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.keyvalue.core.KeySpaceUtils.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.TypeWithDirectKeySpaceAnnotation;
import org.springframework.data.keyvalue.TypeWithInhteritedPersistentAnnotationNotHavingKeySpace;
import org.springframework.data.keyvalue.TypeWithPersistentAnnotationNotHavingKeySpace;
import org.springframework.data.keyvalue.annotation.KeySpace;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.AliasedEntity;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.ClassWithDirectKeySpaceAnnotation;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.EntityWithPersistentAnnotation;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.Foo;
/**
* Unit tests for {@link KeySpaceUtils}.
* Unit tests for {@link AnnotationBasedKeySpaceResolver}.
*
* @author Christoph Strobl
* @author Oliver Gierke
*/
public class KeySpaceUtilsUnitTests {
public class AnnotationBasedKeySpaceResolverUnitTests {
private AnnotationBasedKeySpaceResolver resolver;
@Before
public void setUp() {
resolver = AnnotationBasedKeySpaceResolver.INSTANCE;
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldResolveKeySpaceDefaultValueCorrectly() {
assertThat(getKeySpace(EntityWithDefaultKeySpace.class), is((Object) "daenerys"));
assertThat(resolver.resolveKeySpace(EntityWithDefaultKeySpace.class), is("daenerys"));
}
/**
@@ -53,31 +59,31 @@ public class KeySpaceUtilsUnitTests {
*/
@Test
public void shouldResolveKeySpaceCorrectly() {
assertThat(getKeySpace(EntityWithSetKeySpace.class), is((Object) "viserys"));
assertThat(resolver.resolveKeySpace(EntityWithSetKeySpace.class), is("viserys"));
}
/**
* @see DATACMNS-525
* @see DATAKV-105
*/
@Test
public void shouldReturnNullWhenNoKeySpaceFoundOnComposedPersistentAnnotation() {
assertThat(getKeySpace(AliasedEntity.class), nullValue());
assertThat(resolver.resolveKeySpace(TypeWithInhteritedPersistentAnnotationNotHavingKeySpace.class), nullValue());
}
/**
* @see DATACMNS-525
* @see DATAKV-105
*/
@Test
public void shouldReturnNullWhenPersistentIsFoundOnNonComposedAnnotation() {
assertThat(getKeySpace(EntityWithPersistentAnnotation.class), nullValue());
assertThat(resolver.resolveKeySpace(TypeWithPersistentAnnotationNotHavingKeySpace.class), nullValue());
}
/**
* @see DATACMNS-525
* @see DATAKV-105
*/
@Test
public void shouldReturnNullWhenPersistentIsNotFound() {
assertThat(getKeySpace(Foo.class), nullValue());
assertThat(resolver.resolveKeySpace(TypeWithoutKeySpace.class), nullValue());
}
/**
@@ -85,7 +91,7 @@ public class KeySpaceUtilsUnitTests {
*/
@Test
public void shouldResolveInheritedKeySpaceCorrectly() {
assertThat(getKeySpace(EntityWithInheritedKeySpace.class), is((Object) "viserys"));
assertThat(resolver.resolveKeySpace(EntityWithInheritedKeySpace.class), is("viserys"));
}
/**
@@ -93,7 +99,7 @@ public class KeySpaceUtilsUnitTests {
*/
@Test
public void shouldResolveDirectKeySpaceAnnotationCorrectly() {
assertThat(getKeySpace(ClassWithDirectKeySpaceAnnotation.class), is((Object) "rhaegar"));
assertThat(resolver.resolveKeySpace(TypeWithDirectKeySpaceAnnotation.class), is("rhaegar"));
}
@PersistentAnnotationWithExplicitKeySpace
@@ -121,12 +127,9 @@ public class KeySpaceUtilsUnitTests {
String lastnamne() default "targaryen";
}
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
static @interface ExplicitKeySpace {
static class TypeWithoutKeySpace {
String foo;
@KeySpace
String name() default "";
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2015 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 ort.springframework.data.keyvalue.core.mapping.context;
import org.junit.Test;
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
public class KeyValueMappingContextUnitTests {
/**
* @see DATAKV-105
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenKeySpaceResolverIsNull() {
new KeyValueMappingContext(null);
}
}