diff --git a/src/main/asciidoc/key-value-repositories.adoc b/src/main/asciidoc/key-value-repositories.adoc index 38b5ca0..464da93 100644 --- a/src/main/asciidoc/key-value-repositories.adoc +++ b/src/main/asciidoc/key-value-repositories.adoc @@ -14,17 +14,17 @@ The Key/Value abstraction within Spring Data Commons requires an `Adapter` shiel ---- interface KeyValueOperations { - T insert(T objectToInsert); <1> + 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 findById(Serializable id, Class type); <4> + T findById(Serializable id, Class type); <4> - List findAllOf(Class type); <5> + Iterable findAllOf(Class type); <5> - List find(KeyValueQuery query, Class type); <6> + Iterable find(KeyValueQuery query, Class 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 diff --git a/src/main/java/org/springframework/data/keyvalue/core/ClassNameKeySpaceResolver.java b/src/main/java/org/springframework/data/keyvalue/core/ClassNameKeySpaceResolver.java new file mode 100644 index 0000000..631e555 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/ClassNameKeySpaceResolver.java @@ -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(); + } + +} diff --git a/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java b/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java index 924ed9d..efd8393 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java +++ b/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java @@ -53,16 +53,28 @@ public class ForwardingCloseableIterator implements CloseableIterator next() { return delegate.next(); } + /* + * (non-Javadoc) + * @see org.springframework.data.util.CloseableIterator#close() + */ @Override public void close() { if (closeHandler != null) { diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeySpaceResolver.java b/src/main/java/org/springframework/data/keyvalue/core/KeySpaceResolver.java new file mode 100644 index 0000000..0287071 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/KeySpaceResolver.java @@ -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.
+ * + * @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); +} diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java index 8903417..1f47f18 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java @@ -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); } diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java index 7218053..f56d44a 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java @@ -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, String> keySpaceCache = new ConcurrentHashMap, String>(); - private final MappingContext>, ? extends PersistentProperty> mappingContext; + + private final MappingContext, ? extends KeyValuePersistentProperty> mappingContext; private final IdentifierGenerator identifierGenerator; private ApplicationEventPublisher eventPublisher; private final Set eventTypesToPublish = new HashSet(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 PersistentProperty> mappingContext) { + public KeyValueTemplate(KeyValueAdapter adapter, + MappingContext, ? 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 List findAll(final Class type) { + public Iterable findAll(final Class type) { Assert.notNull(type, "Type to fetch must not be null!"); - return execute(new KeyValueCallback>() { + return execute(new KeyValueCallback>() { @SuppressWarnings("unchecked") @Override - public List doInKeyValue(KeyValueAdapter adapter) { + public Iterable doInKeyValue(KeyValueAdapter adapter) { Iterable values = adapter.getAllOf(resolveKeySpace(type)); - if (getKeySpace(type) == null) { - return new ArrayList(IterableConverter.toList((Iterable) values)); + if (values == null) { + return Collections.emptySet(); } ArrayList filtered = new ArrayList(); @@ -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 List find(final KeyValueQuery query, final Class type) { + public Iterable find(final KeyValueQuery query, final Class type) { - return execute(new KeyValueCallback>() { + return execute(new KeyValueCallback>() { @SuppressWarnings("unchecked") @Override - public List doInKeyValue(KeyValueAdapter adapter) { + public Iterable doInKeyValue(KeyValueAdapter adapter) { Iterable result = adapter.find(query, resolveKeySpace(type)); - - if (getKeySpace(type) == null) { - return new ArrayList(IterableConverter.toList((Iterable) result)); + if (result == null) { + return Collections.emptySet(); } List filtered = new ArrayList(); @@ -388,7 +383,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA */ @SuppressWarnings("rawtypes") @Override - public List findAll(Sort sort, Class type) { + public Iterable findAll(Sort sort, Class type) { return find(new KeyValueQuery(sort), type); } @@ -398,7 +393,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA */ @SuppressWarnings("rawtypes") @Override - public List findInRange(int offset, int rows, Class type) { + public Iterable findInRange(int offset, int rows, Class type) { return find(new KeyValueQuery().skip(offset).limit(rows), type); } @@ -408,7 +403,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA */ @SuppressWarnings("rawtypes") @Override - public List findInRange(int offset, int rows, Sort sort, Class type) { + public Iterable findInRange(int offset, int rows, Sort sort, Class 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); } } + } diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeySpaceUtils.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java similarity index 93% rename from src/main/java/org/springframework/data/keyvalue/core/KeySpaceUtils.java rename to src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java index c71944c..4a8004b 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeySpaceUtils.java +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java @@ -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 descriptor = KeySpaceUtils.MetaAnnotationUtils.findAnnotationDescriptor(type, - Persistent.class); + AnnotationDescriptor descriptor = MetaAnnotationUtils.findAnnotationDescriptor(type, Persistent.class); if (descriptor != null && descriptor.getComposedAnnotation() != null) { @@ -392,6 +406,6 @@ abstract class KeySpaceUtils { } } } - } + } diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java new file mode 100644 index 0000000..ddeaf27 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java @@ -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 + */ +public class BasicKeyValuePersistentEntity extends BasicPersistentEntity implements + KeyValuePersistentEntity { + + private final String keyspace; + + /** + * @param information must not be {@literal null}. + * @param keySpaceResolver must not be {@literal null}. + */ + public BasicKeyValuePersistentEntity(TypeInformation 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; + } +} diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/KeyValuePersistentEntity.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/KeyValuePersistentEntity.java new file mode 100644 index 0000000..abd3bc5 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/KeyValuePersistentEntity.java @@ -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 + */ +public interface KeyValuePersistentEntity extends MutablePersistentEntity { + + /** + * Get the {@literal keySpace} a given entity assigns to. + * + * @return never {@literal null}. + */ + String getKeySpace(); +} diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java index 4f9ab84..33cd91b 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java @@ -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, KeyValuePersistentProperty> { + AbstractMappingContext, 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 BasicPersistentEntity createPersistentEntity( - TypeInformation typeInformation) { - return new BasicPersistentEntity(typeInformation); + protected KeyValuePersistentEntity createPersistentEntity(TypeInformation typeInformation) { + return new BasicKeyValuePersistentEntity(typeInformation, fallbackKeySpaceResolver); } /* @@ -46,7 +74,7 @@ public class KeyValueMappingContext extends */ @Override protected KeyValuePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, - BasicPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + KeyValuePersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { return new KeyValuePersistentProperty(field, descriptor, owner, simpleTypeHolder); } } diff --git a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java index 38012c9..bf18e0a 100644 --- a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java @@ -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() diff --git a/src/test/java/org/springframework/data/keyvalue/CustomKeySpaceAnnotation.java b/src/test/java/org/springframework/data/keyvalue/CustomKeySpaceAnnotation.java new file mode 100644 index 0000000..3cdc121 --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/CustomKeySpaceAnnotation.java @@ -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 ""; +} diff --git a/src/test/java/org/springframework/data/keyvalue/SubclassOfTypeWithCustomComposedKeySpaceAnnotation.java b/src/test/java/org/springframework/data/keyvalue/SubclassOfTypeWithCustomComposedKeySpaceAnnotation.java new file mode 100644 index 0000000..67e507c --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/SubclassOfTypeWithCustomComposedKeySpaceAnnotation.java @@ -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); + } + +} diff --git a/src/test/java/org/springframework/data/keyvalue/TypeWithCustomComposedKeySpaceAnnotation.java b/src/test/java/org/springframework/data/keyvalue/TypeWithCustomComposedKeySpaceAnnotation.java new file mode 100644 index 0000000..0887983 --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/TypeWithCustomComposedKeySpaceAnnotation.java @@ -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; + } + +} diff --git a/src/test/java/org/springframework/data/keyvalue/TypeWithDirectKeySpaceAnnotation.java b/src/test/java/org/springframework/data/keyvalue/TypeWithDirectKeySpaceAnnotation.java new file mode 100644 index 0000000..6ec08fd --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/TypeWithDirectKeySpaceAnnotation.java @@ -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 { + +} diff --git a/src/test/java/org/springframework/data/keyvalue/TypeWithInhteritedPersistentAnnotationNotHavingKeySpace.java b/src/test/java/org/springframework/data/keyvalue/TypeWithInhteritedPersistentAnnotationNotHavingKeySpace.java new file mode 100644 index 0000000..4c592e2 --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/TypeWithInhteritedPersistentAnnotationNotHavingKeySpace.java @@ -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 { + +} diff --git a/src/test/java/org/springframework/data/keyvalue/TypeWithPersistentAnnotationNotHavingKeySpace.java b/src/test/java/org/springframework/data/keyvalue/TypeWithPersistentAnnotationNotHavingKeySpace.java new file mode 100644 index 0000000..69ab0b3 --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/TypeWithPersistentAnnotationNotHavingKeySpace.java @@ -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 { + +} diff --git a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateTests.java b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateTests.java index d359f71..4c02131 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateTests.java @@ -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()); } /** diff --git a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java index 80e183c..189f338 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java @@ -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_QUERY = new KeyValueQuery("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 ""; - } } diff --git a/src/test/java/org/springframework/data/keyvalue/core/KeySpaceUtilsUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolverUnitTests.java similarity index 62% rename from src/test/java/org/springframework/data/keyvalue/core/KeySpaceUtilsUnitTests.java rename to src/test/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolverUnitTests.java index ccbe4ea..2d4dd41 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeySpaceUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolverUnitTests.java @@ -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 ""; } } diff --git a/src/test/java/ort/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContextUnitTests.java b/src/test/java/ort/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContextUnitTests.java new file mode 100644 index 0000000..0267f5f --- /dev/null +++ b/src/test/java/ort/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContextUnitTests.java @@ -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); + } + +}