DATAKV-85 - Ported key-value infrastructure from Spring Data Commons.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2014 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.annotation;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
|
||||
/**
|
||||
* Marker interface for methods with {@link Persistent} annotations indicating the presence of a dedicated keyspace the
|
||||
* entity should reside in. If present the value will be picked up for resolving the keyspace.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @Persistent
|
||||
* @Documented
|
||||
* @Retention(RetentionPolicy.RUNTIME)
|
||||
* @Target({ ElementType.TYPE })
|
||||
* public @interface Document {
|
||||
*
|
||||
* @KeySpace
|
||||
* String collection() default "person";
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* Can also be directly used on types to indicate the keyspace.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* @KeySpace("persons")
|
||||
* public class Foo {
|
||||
*
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = { METHOD, TYPE })
|
||||
public @interface KeySpace {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link KeyValueAdapter} holds {@link QueryEngine} to delegate {@literal find} and
|
||||
* {@literal count} execution to.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public abstract class AbstractKeyValueAdapter implements KeyValueAdapter {
|
||||
|
||||
private final QueryEngine<? extends KeyValueAdapter, ?, ?> engine;
|
||||
|
||||
/**
|
||||
* Creates new {@link AbstractKeyValueAdapter} with using the default query engine.
|
||||
*/
|
||||
protected AbstractKeyValueAdapter() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AbstractKeyValueAdapter} with using the default query engine.
|
||||
*
|
||||
* @param engine will be defaulted to {@link SpelQueryEngine} if {@literal null}.
|
||||
*/
|
||||
protected AbstractKeyValueAdapter(QueryEngine<? extends KeyValueAdapter, ?, ?> engine) {
|
||||
|
||||
this.engine = engine != null ? engine : new SpelQueryEngine<KeyValueAdapter>();
|
||||
this.engine.registerAdapter(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link QueryEngine} used.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected QueryEngine<? extends KeyValueAdapter, ?, ?> getQueryEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#find(org.springframework.data.keyvalue.core.query.KeyValueQuery, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Collection<?> find(KeyValueQuery<?> query, Serializable keyspace) {
|
||||
return engine.execute(query, keyspace);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#count(org.springframework.data.keyvalue.core.query.KeyValueQuery, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public long count(KeyValueQuery<?> query, Serializable keyspace) {
|
||||
return engine.count(query, keyspace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2014 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.data.keyvalue.core.query.KeyValueQuery;
|
||||
|
||||
/**
|
||||
* Resolves the criteria object from given {@link KeyValueQuery}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
*/
|
||||
public interface CriteriaAccessor<T> {
|
||||
|
||||
/**
|
||||
* Checks and reads {@link KeyValueQuery#getCritieria()} of given {@link KeyValueQuery}. Might also apply additional
|
||||
* transformation to match the desired type.
|
||||
*
|
||||
* @param query can be {@literal null}.
|
||||
* @return the criteria extracted from the query.
|
||||
* @throws IllegalArgumentException in case the criteria is not valid for usage with specific {@link CriteriaAccessor}
|
||||
* .
|
||||
*/
|
||||
T resolve(KeyValueQuery<?> query);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link IdentifierGenerator} to generate identifiers of types {@link UUID}, String,
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
enum DefaultIdentifierGenerator implements IdentifierGenerator {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private static final String ALGORITHM = "NativePRNGBlocking";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.IdentifierGenerator#newIdForType(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T generateIdentifierOfType(TypeInformation<T> identifierType) {
|
||||
|
||||
Class<?> type = identifierType.getType();
|
||||
|
||||
if (ClassUtils.isAssignable(UUID.class, type)) {
|
||||
return (T) UUID.randomUUID();
|
||||
} else if (ClassUtils.isAssignable(String.class, type)) {
|
||||
return (T) UUID.randomUUID().toString();
|
||||
} else if (ClassUtils.isAssignable(Integer.class, type)) {
|
||||
|
||||
try {
|
||||
return (T) SecureRandom.getInstance(ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new InvalidDataAccessApiUsageException("Could not create SecureRandom instance.", e);
|
||||
}
|
||||
|
||||
} else if (ClassUtils.isAssignable(Long.class, type)) {
|
||||
|
||||
try {
|
||||
return (T) Long.valueOf(SecureRandom.getInstance(ALGORITHM).nextLong());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new InvalidDataAccessApiUsageException("Could not create SecureRandom instance.", e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidDataAccessApiUsageException("Non gereratable id type....");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.io.Serializable;
|
||||
|
||||
import org.springframework.data.mapping.IdentifierAccessor;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link IdentifierAccessor} adding a {@link #getOrGenerateIdentifier()} to automatically generate an identifier and
|
||||
* set it on the underling bean instance.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see #getOrGenerateIdentifier()
|
||||
*/
|
||||
class GeneratingIdAccessor implements IdentifierAccessor {
|
||||
|
||||
private final PersistentPropertyAccessor accessor;
|
||||
private final PersistentProperty<?> identifierProperty;
|
||||
private final IdentifierGenerator generator;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GeneratingIdAccessor} using the given {@link PersistentPropertyAccessor}, identifier property
|
||||
* and {@link IdentifierGenerator}.
|
||||
*
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @param identifierProperty must not be {@literal null}.
|
||||
* @param generator must not be {@literal null}.
|
||||
*/
|
||||
public GeneratingIdAccessor(PersistentPropertyAccessor accessor, PersistentProperty<?> identifierProperty,
|
||||
IdentifierGenerator generator) {
|
||||
|
||||
Assert.notNull(accessor, "PersistentPropertyAccessor must not be null!");
|
||||
Assert.notNull(identifierProperty, "Identifier property must not be null!");
|
||||
Assert.notNull(generator, "IdentifierGenerator must not be null!");
|
||||
|
||||
this.accessor = accessor;
|
||||
this.identifierProperty = identifierProperty;
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.IdentifierAccessor#getIdentifier()
|
||||
*/
|
||||
@Override
|
||||
public Object getIdentifier() {
|
||||
return accessor.getProperty(identifierProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier value of the backing bean or generates a new one using the configured
|
||||
* {@link IdentifierGenerator}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Object getOrGenerateIdentifier() {
|
||||
|
||||
Serializable existingIdentifier = (Serializable) getIdentifier();
|
||||
|
||||
if (existingIdentifier != null) {
|
||||
return existingIdentifier;
|
||||
}
|
||||
|
||||
Object generatedIdentifier = generator.generateIdentifierOfType(identifierProperty.getTypeInformation());
|
||||
accessor.setProperty(identifierProperty, generatedIdentifier);
|
||||
|
||||
return generatedIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014 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.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* API for components generating identifiers.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
public interface IdentifierGenerator {
|
||||
|
||||
/**
|
||||
* Creates an identifier of the given type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return an identifier of the given type.
|
||||
*/
|
||||
<T> T generateIdentifierOfType(TypeInformation<T> type);
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
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.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
abstract class KeySpaceUtils {
|
||||
|
||||
private KeySpaceUtils() {}
|
||||
|
||||
/**
|
||||
* Looks up {@link Persistent} when used as meta annotation to find the {@link KeySpace} attribute.
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public static Object getKeySpace(Class<?> type) {
|
||||
|
||||
KeySpace keyspace = AnnotationUtils.findAnnotation(type, KeySpace.class);
|
||||
|
||||
if (keyspace != null) {
|
||||
return AnnotationUtils.getValue(keyspace);
|
||||
}
|
||||
|
||||
AnnotationDescriptor<Persistent> descriptor = KeySpaceUtils.MetaAnnotationUtils.findAnnotationDescriptor(type,
|
||||
Persistent.class);
|
||||
|
||||
if (descriptor != null && descriptor.getComposedAnnotation() != null) {
|
||||
|
||||
Annotation composed = descriptor.getComposedAnnotation();
|
||||
|
||||
for (Method method : descriptor.getComposedAnnotationType().getDeclaredMethods()) {
|
||||
|
||||
keyspace = AnnotationUtils.findAnnotation(method, KeySpace.class);
|
||||
|
||||
if (keyspace != null) {
|
||||
return AnnotationUtils.getValue(composed, method.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code MetaAnnotationUtils} is a collection of utility methods that complements the standard support already
|
||||
* available in {@link AnnotationUtils}.
|
||||
* <p>
|
||||
* Whereas {@code AnnotationUtils} provides utilities for <em>getting</em> or <em>finding</em> an annotation,
|
||||
* {@code MetaAnnotationUtils} goes a step further by providing support for determining the <em>root class</em> on
|
||||
* which an annotation is declared, either directly or indirectly via a <em>composed
|
||||
* annotation</em>. This additional information is encapsulated in an {@link AnnotationDescriptor}.
|
||||
* <p>
|
||||
* The additional information provided by an {@code AnnotationDescriptor} is required by the
|
||||
* <em>Spring TestContext Framework</em> in order to be able to support class hierarchy traversals for annotations
|
||||
* such as {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration},
|
||||
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}, and
|
||||
* {@link org.springframework.test.context.ActiveProfiles @ActiveProfiles} which offer support for merging and
|
||||
* overriding various <em>inherited</em> annotation attributes (e.g.,
|
||||
* {@link org.springframework.test.context.ContextConfiguration#inheritLocations}).
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
* @see AnnotationUtils
|
||||
* @see AnnotationDescriptor
|
||||
*/
|
||||
static abstract class MetaAnnotationUtils {
|
||||
|
||||
private MetaAnnotationUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the {@link AnnotationDescriptor} for the supplied {@code annotationType} on the supplied {@link Class},
|
||||
* traversing its annotations and superclasses if no annotation can be found on the given class itself.
|
||||
* <p>
|
||||
* This method explicitly handles class-level annotations which are not declared as
|
||||
* {@linkplain java.lang.annotation.Inherited inherited} <em>as
|
||||
* well as meta-annotations</em>.
|
||||
* <p>
|
||||
* The algorithm operates as follows:
|
||||
* <ol>
|
||||
* <li>Search for the annotation on the given class and return a corresponding {@code AnnotationDescriptor} if
|
||||
* found.
|
||||
* <li>Recursively search through all annotations that the given class declares.
|
||||
* <li>Recursively search through the superclass hierarchy of the given class.
|
||||
* </ol>
|
||||
* <p>
|
||||
* In this context, the term <em>recursively</em> means that the search process continues by returning to step #1
|
||||
* with the current annotation or superclass as the class to look for annotations on.
|
||||
* <p>
|
||||
* If the supplied {@code clazz} is an interface, only the interface itself will be checked; the inheritance
|
||||
* hierarchy for interfaces will not be traversed.
|
||||
*
|
||||
* @param clazz the class to look for annotations on
|
||||
* @param annotationType the type of annotation to look for
|
||||
* @return the corresponding annotation descriptor if the annotation was found; otherwise {@code null}
|
||||
* @see AnnotationUtils#findAnnotationDeclaringClass(Class, Class)
|
||||
* @see #findAnnotationDescriptorForTypes(Class, Class...)
|
||||
*/
|
||||
public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor(Class<?> clazz,
|
||||
Class<T> annotationType) {
|
||||
return findAnnotationDescriptor(clazz, new HashSet<Annotation>(), annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the search algorithm for {@link #findAnnotationDescriptor(Class, Class)}, avoiding endless recursion by
|
||||
* tracking which annotations have already been <em>visited</em>.
|
||||
*
|
||||
* @param clazz the class to look for annotations on
|
||||
* @param visited the set of annotations that have already been visited
|
||||
* @param annotationType the type of annotation to look for
|
||||
* @return the corresponding annotation descriptor if the annotation was found; otherwise {@code null}
|
||||
*/
|
||||
private static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor(Class<?> clazz,
|
||||
Set<Annotation> visited, Class<T> annotationType) {
|
||||
|
||||
Assert.notNull(annotationType, "Annotation type must not be null");
|
||||
|
||||
if (clazz == null || clazz.equals(Object.class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Declared locally?
|
||||
if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
return new AnnotationDescriptor<T>(clazz, clazz.getAnnotation(annotationType));
|
||||
}
|
||||
|
||||
// Declared on a composed annotation (i.e., as a meta-annotation)?
|
||||
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
|
||||
AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(composedAnnotation.annotationType(), visited,
|
||||
annotationType);
|
||||
if (descriptor != null) {
|
||||
return new AnnotationDescriptor<T>(clazz, descriptor.getDeclaringClass(), composedAnnotation,
|
||||
descriptor.getAnnotation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Declared on a superclass?
|
||||
return findAnnotationDescriptor(clazz.getSuperclass(), visited, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the {@link UntypedAnnotationDescriptor} for the first {@link Class} in the inheritance hierarchy of the
|
||||
* specified {@code clazz} (including the specified {@code clazz} itself) which declares at least one of the
|
||||
* specified {@code annotationTypes}.
|
||||
* <p>
|
||||
* This method traverses the annotations and superclasses of the specified {@code clazz} if no annotation can be
|
||||
* found on the given class itself.
|
||||
* <p>
|
||||
* This method explicitly handles class-level annotations which are not declared as
|
||||
* {@linkplain java.lang.annotation.Inherited inherited} <em>as
|
||||
* well as meta-annotations</em>.
|
||||
* <p>
|
||||
* The algorithm operates as follows:
|
||||
* <ol>
|
||||
* <li>Search for a local declaration of one of the annotation types on the given class and return a corresponding
|
||||
* {@code UntypedAnnotationDescriptor} if found.
|
||||
* <li>Recursively search through all annotations that the given class declares.
|
||||
* <li>Recursively search through the superclass hierarchy of the given class.
|
||||
* </ol>
|
||||
* <p>
|
||||
* In this context, the term <em>recursively</em> means that the search process continues by returning to step #1
|
||||
* with the current annotation or superclass as the class to look for annotations on.
|
||||
* <p>
|
||||
* If the supplied {@code clazz} is an interface, only the interface itself will be checked; the inheritance
|
||||
* hierarchy for interfaces will not be traversed.
|
||||
*
|
||||
* @param clazz the class to look for annotations on
|
||||
* @param annotationTypes the types of annotations to look for
|
||||
* @return the corresponding annotation descriptor if one of the annotations was found; otherwise {@code null}
|
||||
* @see AnnotationUtils#findAnnotationDeclaringClassForTypes(java.util.List, Class)
|
||||
* @see #findAnnotationDescriptor(Class, Class)
|
||||
*/
|
||||
public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class<?> clazz,
|
||||
Class<? extends Annotation>... annotationTypes) {
|
||||
return findAnnotationDescriptorForTypes(clazz, new HashSet<Annotation>(), annotationTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the search algorithm for {@link #findAnnotationDescriptorForTypes(Class, Class...)}, avoiding endless
|
||||
* recursion by tracking which annotations have already been <em>visited</em>.
|
||||
*
|
||||
* @param clazz the class to look for annotations on
|
||||
* @param visited the set of annotations that have already been visited
|
||||
* @param annotationTypes the types of annotations to look for
|
||||
* @return the corresponding annotation descriptor if one of the annotations was found; otherwise {@code null}
|
||||
*/
|
||||
private static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class<?> clazz,
|
||||
Set<Annotation> visited, Class<? extends Annotation>... annotationTypes) {
|
||||
|
||||
assertNonEmptyAnnotationTypeArray(annotationTypes, "The list of annotation types must not be empty");
|
||||
|
||||
if (clazz == null || clazz.equals(Object.class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Declared locally?
|
||||
for (Class<? extends Annotation> annotationType : annotationTypes) {
|
||||
if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
return new UntypedAnnotationDescriptor(clazz, clazz.getAnnotation(annotationType));
|
||||
}
|
||||
}
|
||||
|
||||
// Declared on a composed annotation (i.e., as a meta-annotation)?
|
||||
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
|
||||
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(
|
||||
composedAnnotation.annotationType(), visited, annotationTypes);
|
||||
if (descriptor != null) {
|
||||
return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), composedAnnotation,
|
||||
descriptor.getAnnotation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Declared on a superclass?
|
||||
return findAnnotationDescriptorForTypes(clazz.getSuperclass(), visited, annotationTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptor for an {@link Annotation}, including the {@linkplain #getDeclaringClass() class} on which the
|
||||
* annotation is <em>declared</em> as well as the actual {@linkplain #getAnnotation() annotation} instance.
|
||||
* <p>
|
||||
* If the annotation is used as a meta-annotation, the descriptor also includes the
|
||||
* {@linkplain #getComposedAnnotation() composed annotation} on which the annotation is present. In such cases, the
|
||||
* <em>root declaring class</em> is not directly annotated with the annotation but rather indirectly via the
|
||||
* composed annotation.
|
||||
* <p>
|
||||
* Given the following example, if we are searching for the {@code @Transactional} annotation <em>on</em> the
|
||||
* {@code TransactionalTests} class, then the properties of the {@code AnnotationDescriptor} would be as follows.
|
||||
* <ul>
|
||||
* <li>rootDeclaringClass: {@code TransactionalTests} class object</li>
|
||||
* <li>declaringClass: {@code TransactionalTests} class object</li>
|
||||
* <li>composedAnnotation: {@code null}</li>
|
||||
* <li>annotation: instance of the {@code Transactional} annotation</li>
|
||||
* </ul>
|
||||
*
|
||||
* <pre style="code">
|
||||
* @Transactional
|
||||
* @ContextConfiguration({ "/test-datasource.xml", "/repository-config.xml" })
|
||||
* public class TransactionalTests {}
|
||||
* </pre>
|
||||
* <p>
|
||||
* Given the following example, if we are searching for the {@code @Transactional} annotation <em>on</em> the
|
||||
* {@code UserRepositoryTests} class, then the properties of the {@code AnnotationDescriptor} would be as follows.
|
||||
* <ul>
|
||||
* <li>rootDeclaringClass: {@code UserRepositoryTests} class object</li>
|
||||
* <li>declaringClass: {@code RepositoryTests} class object</li>
|
||||
* <li>composedAnnotation: instance of the {@code RepositoryTests} annotation</li>
|
||||
* <li>annotation: instance of the {@code Transactional} annotation</li>
|
||||
* </ul>
|
||||
*
|
||||
* <pre style="code">
|
||||
* @Transactional
|
||||
* @ContextConfiguration({ "/test-datasource.xml", "/repository-config.xml" })
|
||||
* @Retention(RetentionPolicy.RUNTIME)
|
||||
* public @interface RepositoryTests {
|
||||
* }
|
||||
*
|
||||
* @RepositoryTests
|
||||
* public class UserRepositoryTests {}
|
||||
* </pre>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
*/
|
||||
public static class AnnotationDescriptor<T extends Annotation> {
|
||||
|
||||
private final Class<?> rootDeclaringClass;
|
||||
private final Class<?> declaringClass;
|
||||
private final Annotation composedAnnotation;
|
||||
private final T annotation;
|
||||
private final AnnotationAttributes annotationAttributes;
|
||||
|
||||
public AnnotationDescriptor(Class<?> rootDeclaringClass, T annotation) {
|
||||
this(rootDeclaringClass, rootDeclaringClass, null, annotation);
|
||||
}
|
||||
|
||||
public AnnotationDescriptor(Class<?> rootDeclaringClass, Class<?> declaringClass, Annotation composedAnnotation,
|
||||
T annotation) {
|
||||
Assert.notNull(rootDeclaringClass, "rootDeclaringClass must not be null");
|
||||
Assert.notNull(annotation, "annotation must not be null");
|
||||
|
||||
this.rootDeclaringClass = rootDeclaringClass;
|
||||
this.declaringClass = declaringClass;
|
||||
this.composedAnnotation = composedAnnotation;
|
||||
this.annotation = annotation;
|
||||
this.annotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass, annotation
|
||||
.annotationType().getName());
|
||||
}
|
||||
|
||||
public Class<?> getRootDeclaringClass() {
|
||||
return this.rootDeclaringClass;
|
||||
}
|
||||
|
||||
public Class<?> getDeclaringClass() {
|
||||
return this.declaringClass;
|
||||
}
|
||||
|
||||
public T getAnnotation() {
|
||||
return this.annotation;
|
||||
}
|
||||
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return this.annotation.annotationType();
|
||||
}
|
||||
|
||||
public AnnotationAttributes getAnnotationAttributes() {
|
||||
return this.annotationAttributes;
|
||||
}
|
||||
|
||||
public Annotation getComposedAnnotation() {
|
||||
return this.composedAnnotation;
|
||||
}
|
||||
|
||||
public Class<? extends Annotation> getComposedAnnotationType() {
|
||||
return this.composedAnnotation == null ? null : this.composedAnnotation.annotationType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a textual representation of this {@code AnnotationDescriptor}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("rootDeclaringClass", rootDeclaringClass)//
|
||||
.append("declaringClass", declaringClass)//
|
||||
.append("composedAnnotation", composedAnnotation)//
|
||||
.append("annotation", annotation)//
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>Untyped</em> extension of {@code AnnotationDescriptor} that is used to describe the declaration of one of
|
||||
* several candidate annotation types where the actual annotation type cannot be predetermined.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
*/
|
||||
public static class UntypedAnnotationDescriptor extends AnnotationDescriptor<Annotation> {
|
||||
|
||||
public UntypedAnnotationDescriptor(Class<?> rootDeclaringClass, Annotation annotation) {
|
||||
this(rootDeclaringClass, rootDeclaringClass, null, annotation);
|
||||
}
|
||||
|
||||
public UntypedAnnotationDescriptor(Class<?> rootDeclaringClass, Class<?> declaringClass,
|
||||
Annotation composedAnnotation, Annotation annotation) {
|
||||
super(rootDeclaringClass, declaringClass, composedAnnotation, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNonEmptyAnnotationTypeArray(Class<?>[] annotationTypes, String message) {
|
||||
if (ObjectUtils.isEmpty(annotationTypes)) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
for (Class<?> clazz : annotationTypes) {
|
||||
if (!Annotation.class.isAssignableFrom(clazz)) {
|
||||
throw new IllegalArgumentException("Array elements must be of type Annotation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
|
||||
/**
|
||||
* {@link KeyValueAdapter} unifies access and shields the underlying key/value specific implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public interface KeyValueAdapter extends DisposableBean {
|
||||
|
||||
/**
|
||||
* Add object with given id to keyspace.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return the item previously associated with the id.
|
||||
*/
|
||||
Object put(Serializable id, Object item, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Check if a object with given id exists in keyspace.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return true if item of type with id exists.
|
||||
*/
|
||||
boolean contains(Serializable id, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Get the object with given id from keyspace.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return {@literal null} in case no matching item exists.
|
||||
*/
|
||||
Object get(Serializable id, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Delete and return the obect with given type and id.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return {@literal null} if object could not be found
|
||||
*/
|
||||
Object delete(Serializable id, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Get all elements for given keyspace.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return empty {@link Collection} if nothing found.
|
||||
*/
|
||||
Collection<?> getAllOf(Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Remove all objects of given type.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
*/
|
||||
void deleteAllOf(Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Removes all objects.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Find all matching objects within {@literal keyspace}.
|
||||
*
|
||||
* @param query
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return empty {@link Collection} if no match found.
|
||||
*/
|
||||
Collection<?> find(KeyValueQuery<?> query, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Count all matching objects within {@literal keyspace}.
|
||||
*
|
||||
* @param query
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
long count(KeyValueQuery<?> query, Serializable keyspace);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014 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;
|
||||
|
||||
/**
|
||||
* Generic callback interface for code that operates on a {@link KeyValueAdapter}. This is particularly useful for
|
||||
* delegating code that needs to work closely on the underlying key/value store implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
*/
|
||||
public interface KeyValueCallback<T> {
|
||||
|
||||
/**
|
||||
* Gets called by {@code KeyValueTemplate#execute(KeyValueCallback)}. Allows for returning a result object created
|
||||
* within the callback, i.e. a domain object or a collection of domain objects.
|
||||
*
|
||||
* @param adapter
|
||||
* @return
|
||||
*/
|
||||
T doInKeyValue(KeyValueAdapter adapter);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.annotation.KeySpace;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
|
||||
/**
|
||||
* Interface that specifies a basic set of key/value operations. Implemented by {@link KeyValueTemplate}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public interface KeyValueOperations extends DisposableBean {
|
||||
|
||||
/**
|
||||
* Add given object. Object needs to have id property to which a generated value will be assigned.
|
||||
*
|
||||
* @param objectToInsert
|
||||
* @return
|
||||
*/
|
||||
<T> T insert(T objectToInsert);
|
||||
|
||||
/**
|
||||
* Add object with given id.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param objectToInsert must not be {@literal null}.
|
||||
*/
|
||||
void insert(Serializable id, Object objectToInsert);
|
||||
|
||||
/**
|
||||
* Get all elements of given type. Respects {@link KeySpace} if present and therefore returns all elements that can be
|
||||
* assigned to requested type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return empty collection if no elements found.
|
||||
*/
|
||||
<T> List<T> findAll(Class<T> type);
|
||||
|
||||
/**
|
||||
* Get all elements ordered by sort. Respects {@link KeySpace} if present and therefore returns all elements that can
|
||||
* be assigned to requested type.
|
||||
*
|
||||
* @param sort must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> findAll(Sort sort, Class<T> type);
|
||||
|
||||
/**
|
||||
* Get element of given type with given id. Respects {@link KeySpace} if present and therefore returns all elements
|
||||
* that can be assigned to requested type.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return null if not found.
|
||||
*/
|
||||
<T> T findById(Serializable id, Class<T> type);
|
||||
|
||||
/**
|
||||
* Execute operation against underlying store.
|
||||
*
|
||||
* @param action must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T> T execute(KeyValueCallback<T> action);
|
||||
|
||||
/**
|
||||
* Get all elements matching the given query. <br />
|
||||
* Respects {@link KeySpace} if present and therefore returns all elements that can be assigned to requested type..
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return empty collection if no match found.
|
||||
*/
|
||||
<T> List<T> find(KeyValueQuery<?> query, Class<T> type);
|
||||
|
||||
/**
|
||||
* Get all elements in given range. Respects {@link KeySpace} if present and therefore returns all elements that can
|
||||
* be assigned to requested type.
|
||||
*
|
||||
* @param offset
|
||||
* @param rows
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> findInRange(int offset, int rows, Class<T> type);
|
||||
|
||||
/**
|
||||
* Get all elements in given range ordered by sort. Respects {@link KeySpace} if present and therefore returns all
|
||||
* elements that can be assigned to requested type.
|
||||
*
|
||||
* @param offset
|
||||
* @param rows
|
||||
* @param sort
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> findInRange(int offset, int rows, Sort sort, Class<T> type);
|
||||
|
||||
/**
|
||||
* @param objectToUpdate must not be {@literal null}.
|
||||
*/
|
||||
void update(Object objectToUpdate);
|
||||
|
||||
/**
|
||||
* @param id must not be {@literal null}.
|
||||
* @param objectToUpdate must not be {@literal null}.
|
||||
*/
|
||||
void update(Serializable id, Object objectToUpdate);
|
||||
|
||||
/**
|
||||
* Remove all elements of type. Respects {@link KeySpace} if present and therefore removes all elements that can be
|
||||
* assigned to requested type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
void delete(Class<?> type);
|
||||
|
||||
/**
|
||||
* @param objectToDelete must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T> T delete(T objectToDelete);
|
||||
|
||||
/**
|
||||
* Delete item of type with given id.
|
||||
*
|
||||
* @param id must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return the deleted item or {@literal null} if no match found.
|
||||
*/
|
||||
<T> T delete(Serializable id, Class<T> type);
|
||||
|
||||
/**
|
||||
* Total number of elements with given type available. Respects {@link KeySpace} if present and therefore counts all
|
||||
* elements that can be assigned to requested type.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
long count(Class<?> type);
|
||||
|
||||
/**
|
||||
* Total number of elements matching given query. Respects {@link KeySpace} if present and therefore counts all
|
||||
* elements that can be assigned to requested type.
|
||||
*
|
||||
* @param query
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
long count(KeyValueQuery<?> query, Class<?> type);
|
||||
|
||||
/**
|
||||
* @return mapping context in use.
|
||||
*/
|
||||
MappingContext<?, ?> getMappingContext();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.util.NoSuchElementException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
|
||||
/**
|
||||
* Simple {@link PersistenceExceptionTranslator} implementation for key/value stores that converts the given runtime
|
||||
* exception to an appropriate exception from the {@code org.springframework.dao} hierarchy.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValuePersistenceExceptionTranslator implements PersistenceExceptionTranslator {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
*/
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException e) {
|
||||
|
||||
if (e == null || e instanceof DataAccessException) {
|
||||
return (DataAccessException) e;
|
||||
}
|
||||
|
||||
if (e instanceof NoSuchElementException || e instanceof IndexOutOfBoundsException
|
||||
|| e instanceof IllegalStateException) {
|
||||
return new DataRetrievalFailureException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (e.getClass().getName().startsWith("java")) {
|
||||
return new UncategorizedKeyValueException(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright 2014 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 static org.springframework.data.keyvalue.core.KeySpaceUtils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Basic implementation of {@link KeyValueOperations}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValueTemplate implements KeyValueOperations {
|
||||
|
||||
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 IdentifierGenerator identifierGenerator;
|
||||
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR;
|
||||
|
||||
/**
|
||||
* Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} with a default
|
||||
* {@link KeyValueMappingContext}.
|
||||
*
|
||||
* @param adapter must not be {@literal null}.
|
||||
*/
|
||||
public KeyValueTemplate(KeyValueAdapter adapter) {
|
||||
this(adapter, new KeyValueMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} and {@link MappingContext}.
|
||||
*
|
||||
* @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) {
|
||||
|
||||
Assert.notNull(adapter, "Adapter must not be null!");
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
|
||||
this.adapter = adapter;
|
||||
this.mappingContext = mappingContext;
|
||||
this.identifierGenerator = DefaultIdentifierGenerator.INSTANCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#insert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public <T> T insert(T objectToInsert) {
|
||||
|
||||
PersistentEntity<?, ?> entity = this.mappingContext.getPersistentEntity(ClassUtils.getUserClass(objectToInsert));
|
||||
|
||||
GeneratingIdAccessor generatingIdAccessor = new GeneratingIdAccessor(entity.getPropertyAccessor(objectToInsert),
|
||||
entity.getIdProperty(), identifierGenerator);
|
||||
Object id = generatingIdAccessor.getOrGenerateIdentifier();
|
||||
|
||||
insert((Serializable) id, objectToInsert);
|
||||
return objectToInsert;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#insert(java.io.Serializable, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void insert(final Serializable id, final Object objectToInsert) {
|
||||
|
||||
Assert.notNull(id, "Id for object to be inserted must not be null!");
|
||||
Assert.notNull(objectToInsert, "Object to be inserted must not be null!");
|
||||
|
||||
execute(new KeyValueCallback<Void>() {
|
||||
|
||||
@Override
|
||||
public Void doInKeyValue(KeyValueAdapter adapter) {
|
||||
|
||||
String typeKey = resolveKeySpace(objectToInsert.getClass());
|
||||
|
||||
if (adapter.contains(id, typeKey)) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot insert existing object. Please use update.");
|
||||
}
|
||||
|
||||
adapter.put(id, objectToInsert, typeKey);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#update(java.lang.Object)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void update(Object objectToUpdate) {
|
||||
|
||||
PersistentEntity<?, ? extends PersistentProperty> entity = this.mappingContext.getPersistentEntity(ClassUtils
|
||||
.getUserClass(objectToUpdate));
|
||||
|
||||
if (!entity.hasIdProperty()) {
|
||||
throw new InvalidDataAccessApiUsageException(String.format("Cannot determine id for type %s",
|
||||
ClassUtils.getUserClass(objectToUpdate)));
|
||||
}
|
||||
|
||||
update((Serializable) entity.getIdentifierAccessor(objectToUpdate).getIdentifier(), objectToUpdate);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#update(java.io.Serializable, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void update(final Serializable id, final Object objectToUpdate) {
|
||||
|
||||
Assert.notNull(id, "Id for object to be inserted must not be null!");
|
||||
Assert.notNull(objectToUpdate, "Object to be updated must not be null!");
|
||||
|
||||
execute(new KeyValueCallback<Void>() {
|
||||
|
||||
@Override
|
||||
public Void doInKeyValue(KeyValueAdapter adapter) {
|
||||
adapter.put(id, objectToUpdate, resolveKeySpace(objectToUpdate.getClass()));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#findAllOf(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> findAll(final Class<T> type) {
|
||||
|
||||
Assert.notNull(type, "Type to fetch must not be null!");
|
||||
|
||||
return execute(new KeyValueCallback<List<T>>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<T> doInKeyValue(KeyValueAdapter adapter) {
|
||||
|
||||
Collection<?> x = adapter.getAllOf(resolveKeySpace(type));
|
||||
|
||||
if (getKeySpace(type) == null) {
|
||||
return new ArrayList<T>((Collection<T>) x);
|
||||
}
|
||||
|
||||
ArrayList<T> filtered = new ArrayList<T>();
|
||||
for (Object candidate : x) {
|
||||
if (typeCheck(type, candidate)) {
|
||||
filtered.add((T) candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#findById(java.io.Serializable, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> T findById(final Serializable id, final Class<T> type) {
|
||||
|
||||
Assert.notNull(id, "Id for object to be inserted must not be null!");
|
||||
Assert.notNull(type, "Type to fetch must not be null!");
|
||||
|
||||
return execute(new KeyValueCallback<T>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T doInKeyValue(KeyValueAdapter adapter) {
|
||||
|
||||
Object result = adapter.get(id, resolveKeySpace(type));
|
||||
|
||||
if (result == null || getKeySpace(type) == null || typeCheck(type, result)) {
|
||||
return (T) result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#delete(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public void delete(final Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type to delete must not be null!");
|
||||
|
||||
final String typeKey = resolveKeySpace(type);
|
||||
|
||||
execute(new KeyValueCallback<Void>() {
|
||||
|
||||
@Override
|
||||
public Void doInKeyValue(KeyValueAdapter adapter) {
|
||||
|
||||
adapter.deleteAllOf(typeKey);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#delete(java.lang.Object)
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public <T> T delete(T objectToDelete) {
|
||||
|
||||
Class<T> type = (Class<T>) ClassUtils.getUserClass(objectToDelete);
|
||||
PersistentEntity<?, ? extends PersistentProperty> entity = this.mappingContext.getPersistentEntity(type);
|
||||
|
||||
return delete((Serializable) entity.getIdentifierAccessor(objectToDelete).getIdentifier(), type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#delete(java.io.Serializable, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> T delete(final Serializable id, final Class<T> type) {
|
||||
|
||||
Assert.notNull(id, "Id for object to be inserted must not be null!");
|
||||
Assert.notNull(type, "Type to delete must not be null!");
|
||||
|
||||
return execute(new KeyValueCallback<T>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T doInKeyValue(KeyValueAdapter adapter) {
|
||||
return (T) adapter.delete(id, resolveKeySpace(type));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#count(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public long count(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type for count must not be null!");
|
||||
return findAll(type).size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#execute(org.springframework.data.keyvalue.core.KeyValueCallback)
|
||||
*/
|
||||
@Override
|
||||
public <T> T execute(KeyValueCallback<T> action) {
|
||||
|
||||
Assert.notNull(action, "KeyValueCallback must not be null!");
|
||||
|
||||
try {
|
||||
return action.doInKeyValue(this.adapter);
|
||||
} catch (RuntimeException e) {
|
||||
throw resolveExceptionIfPossible(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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) {
|
||||
|
||||
return execute(new KeyValueCallback<List<T>>() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<T> doInKeyValue(KeyValueAdapter adapter) {
|
||||
|
||||
Collection<?> result = adapter.find(query, resolveKeySpace(type));
|
||||
|
||||
if (getKeySpace(type) == null) {
|
||||
return new ArrayList<T>((Collection<T>) result);
|
||||
}
|
||||
|
||||
List<T> filtered = new ArrayList<T>();
|
||||
|
||||
for (Object candidate : result) {
|
||||
if (typeCheck(type, candidate)) {
|
||||
filtered.add((T) candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#findAllOf(org.springframework.data.domain.Sort, java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public <T> List<T> findAll(Sort sort, Class<T> type) {
|
||||
return find(new KeyValueQuery(sort), type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#findInRange(int, int, java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public <T> List<T> findInRange(int offset, int rows, Class<T> type) {
|
||||
return find(new KeyValueQuery().skip(offset).limit(rows), type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#findInRange(int, int, org.springframework.data.domain.Sort, java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public <T> List<T> findInRange(int offset, int rows, Sort sort, Class<T> type) {
|
||||
return find(new KeyValueQuery(sort).skip(offset).limit(rows), type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#count(org.springframework.data.keyvalue.core.query.KeyValueQuery, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public long count(final KeyValueQuery<?> query, final Class<?> type) {
|
||||
|
||||
return execute(new KeyValueCallback<Long>() {
|
||||
|
||||
@Override
|
||||
public Long doInKeyValue(KeyValueAdapter adapter) {
|
||||
return adapter.count(query, resolveKeySpace(type));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueOperations#getMappingContext()
|
||||
*/
|
||||
@Override
|
||||
public MappingContext<?, ?> getMappingContext() {
|
||||
return this.mappingContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.DisposableBean#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
this.adapter.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link PersistenceExceptionTranslator} used for converting {@link RuntimeException}.
|
||||
*
|
||||
* @param exceptionTranslator must not be {@literal null}.
|
||||
*/
|
||||
public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTranslator) {
|
||||
|
||||
Assert.notNull(exceptionTranslator, "ExceptionTranslator must not be null.");
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
}
|
||||
|
||||
protected String resolveKeySpace(Class<?> type) {
|
||||
|
||||
Class<?> userClass = ClassUtils.getUserClass(type);
|
||||
|
||||
String potentialAlias = keySpaceCache.get(userClass);
|
||||
|
||||
if (potentialAlias != null) {
|
||||
return potentialAlias;
|
||||
}
|
||||
|
||||
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 static boolean typeCheck(Class<?> requiredType, Object candidate) {
|
||||
return candidate == null ? true : ClassUtils.isAssignable(requiredType, candidate.getClass());
|
||||
}
|
||||
|
||||
private RuntimeException resolveExceptionIfPossible(RuntimeException e) {
|
||||
|
||||
DataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(e);
|
||||
return translatedException != null ? translatedException : e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
|
||||
/**
|
||||
* Base implementation for accessing and executing {@link KeyValueQuery} against a {@link KeyValueAdapter}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <ADAPTER>
|
||||
* @param <CRITERIA>
|
||||
* @param <SORT>
|
||||
*/
|
||||
public abstract class QueryEngine<ADAPTER extends KeyValueAdapter, CRITERIA, SORT> {
|
||||
|
||||
private final CriteriaAccessor<CRITERIA> criteriaAccessor;
|
||||
private final SortAccessor<SORT> sortAccessor;
|
||||
|
||||
private ADAPTER adapter;
|
||||
|
||||
public QueryEngine(CriteriaAccessor<CRITERIA> criteriaAccessor, SortAccessor<SORT> sortAccessor) {
|
||||
|
||||
this.criteriaAccessor = criteriaAccessor;
|
||||
this.sortAccessor = sortAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract query attributes and delegate to concrete execution.
|
||||
*
|
||||
* @param query
|
||||
* @param keyspace
|
||||
* @return
|
||||
*/
|
||||
public Collection<?> execute(KeyValueQuery<?> query, Serializable keyspace) {
|
||||
|
||||
CRITERIA criteria = this.criteriaAccessor != null ? this.criteriaAccessor.resolve(query) : null;
|
||||
SORT sort = this.sortAccessor != null ? this.sortAccessor.resolve(query) : null;
|
||||
|
||||
return execute(criteria, sort, query.getOffset(), query.getRows(), keyspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract query attributes and delegate to concrete execution.
|
||||
*
|
||||
* @param query
|
||||
* @param keyspace
|
||||
* @return
|
||||
*/
|
||||
public long count(KeyValueQuery<?> query, Serializable keyspace) {
|
||||
|
||||
CRITERIA criteria = this.criteriaAccessor != null ? this.criteriaAccessor.resolve(query) : null;
|
||||
return count(criteria, keyspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param criteria
|
||||
* @param sort
|
||||
* @param offset
|
||||
* @param rows
|
||||
* @param keyspace
|
||||
* @return
|
||||
*/
|
||||
public abstract Collection<?> execute(CRITERIA criteria, SORT sort, int offset, int rows, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* @param criteria
|
||||
* @param keyspace
|
||||
* @return
|
||||
*/
|
||||
public abstract long count(CRITERIA criteria, Serializable keyspace);
|
||||
|
||||
/**
|
||||
* Get the {@link KeyValueAdapter} used.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected ADAPTER getAdapter() {
|
||||
return this.adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adapter
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void registerAdapter(KeyValueAdapter adapter) {
|
||||
|
||||
if (this.adapter == null) {
|
||||
this.adapter = (ADAPTER) adapter;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot register more than one adapter for this QueryEngine.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2014 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.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
|
||||
/**
|
||||
* Resolves the {@link Sort} object from given {@link KeyValueQuery} and potentially converts it into a store specific
|
||||
* representation that can be used by the {@link QueryEngine} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
*/
|
||||
public interface SortAccessor<T> {
|
||||
|
||||
/**
|
||||
* Reads {@link KeyValueQuery#getSort()} of given {@link KeyValueQuery} and applies required transformation to match
|
||||
* the desired type.
|
||||
*
|
||||
* @param query can be {@literal null}.
|
||||
* @return {@literal null} in case {@link Sort} has not been defined on {@link KeyValueQuery}.
|
||||
*/
|
||||
T resolve(KeyValueQuery<?> query);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014 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.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link CriteriaAccessor} implementation capable of {@link SpelExpression}s.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
class SpelCriteriaAccessor implements CriteriaAccessor<SpelExpression> {
|
||||
|
||||
private final SpelExpressionParser parser;
|
||||
|
||||
/**
|
||||
* @param parser must not be {@literal null}.
|
||||
*/
|
||||
public SpelCriteriaAccessor(SpelExpressionParser parser) {
|
||||
|
||||
Assert.notNull(parser, "SpelExpressionParser must not be null!");
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpelExpression resolve(KeyValueQuery<?> query) {
|
||||
|
||||
if (query.getCritieria() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (query.getCritieria() instanceof SpelExpression) {
|
||||
return (SpelExpression) query.getCritieria();
|
||||
}
|
||||
|
||||
if (query.getCritieria() instanceof String) {
|
||||
return parser.parseRaw((String) query.getCritieria());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Cannot create SpelCriteria for " + query.getCritieria());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.util.Comparator;
|
||||
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* {@link Comparator} implementation using {@link SpelExpression}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
*/
|
||||
public class SpelPropertyComparator<T> implements Comparator<T> {
|
||||
|
||||
private final String path;
|
||||
private final SpelExpressionParser parser;
|
||||
|
||||
private boolean asc = true;
|
||||
private boolean nullsFirst = true;
|
||||
private SpelExpression expression;
|
||||
|
||||
/**
|
||||
* Create new {@link SpelPropertyComparator} for the given property path an {@link SpelExpressionParser}..
|
||||
*
|
||||
* @param path must not be {@literal null} or empty.
|
||||
* @param parser must not be {@literal null}.
|
||||
*/
|
||||
public SpelPropertyComparator(String path, SpelExpressionParser parser) {
|
||||
|
||||
this.path = path;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort {@literal ascending}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public SpelPropertyComparator<T> asc() {
|
||||
this.asc = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort {@literal descending}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public SpelPropertyComparator<T> desc() {
|
||||
this.asc = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort {@literal null} values first.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public SpelPropertyComparator<T> nullsFirst() {
|
||||
this.nullsFirst = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort {@literal null} values last.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public SpelPropertyComparator<T> nullsLast() {
|
||||
this.nullsFirst = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse values to {@link SpelExpression}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected SpelExpression getExpression() {
|
||||
|
||||
if (this.expression == null) {
|
||||
this.expression = parser.parseRaw(buildExpressionForPath());
|
||||
}
|
||||
|
||||
return this.expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the expression raw value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String buildExpressionForPath() {
|
||||
|
||||
StringBuilder rawExpression = new StringBuilder(
|
||||
"new org.springframework.util.comparator.NullSafeComparator(new org.springframework.util.comparator.ComparableComparator(), "
|
||||
+ Boolean.toString(this.nullsFirst) + ").compare(");
|
||||
|
||||
rawExpression.append("#arg1?.");
|
||||
rawExpression.append(path != null ? path.replace(".", "?.") : "");
|
||||
rawExpression.append(",");
|
||||
rawExpression.append("#arg2?.");
|
||||
rawExpression.append(path != null ? path.replace(".", "?.") : "");
|
||||
rawExpression.append(")");
|
||||
|
||||
return rawExpression.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public int compare(T arg1, T arg2) {
|
||||
|
||||
SpelExpression expressionToUse = getExpression();
|
||||
|
||||
expressionToUse.getEvaluationContext().setVariable("arg1", arg1);
|
||||
expressionToUse.getEvaluationContext().setVariable("arg2", arg2);
|
||||
|
||||
return expressionToUse.getValue(Integer.class) * (asc ? 1 : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dot path to property.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* {@link QueryEngine} implementation specific for executing {@link SpelExpression} based {@link KeyValueQuery} against
|
||||
* {@link KeyValueAdapter}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
*/
|
||||
class SpelQueryEngine<T extends KeyValueAdapter> extends QueryEngine<KeyValueAdapter, SpelExpression, Comparator<?>> {
|
||||
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
/**
|
||||
* Creates a new {@link SpelQueryEngine}.
|
||||
*/
|
||||
public SpelQueryEngine() {
|
||||
super(new SpelCriteriaAccessor(PARSER), new SpelSortAccessor(PARSER));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.QueryEngine#execute(java.lang.Object, java.lang.Object, int, int, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Collection<?> execute(SpelExpression criteria, Comparator<?> sort, int offset, int rows, Serializable keyspace) {
|
||||
return sortAndFilterMatchingRange(getAdapter().getAllOf(keyspace), criteria, sort, offset, rows);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.QueryEngine#count(java.lang.Object, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public long count(SpelExpression criteria, Serializable keyspace) {
|
||||
return filterMatchingRange(getAdapter().getAllOf(keyspace), criteria, -1, -1).size();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private List<?> sortAndFilterMatchingRange(Collection<?> source, SpelExpression criteria, Comparator sort,
|
||||
int offset, int rows) {
|
||||
|
||||
List<?> tmp = new ArrayList(source);
|
||||
if (sort != null) {
|
||||
Collections.sort(tmp, sort);
|
||||
}
|
||||
|
||||
return filterMatchingRange(tmp, criteria, offset, rows);
|
||||
}
|
||||
|
||||
private static <S> List<S> filterMatchingRange(Iterable<S> source, SpelExpression criteria, int offset, int rows) {
|
||||
|
||||
List<S> result = new ArrayList<S>();
|
||||
|
||||
boolean compareOffsetAndRows = 0 < offset || 0 <= rows;
|
||||
int remainingRows = rows;
|
||||
int curPos = 0;
|
||||
|
||||
for (S candidate : source) {
|
||||
|
||||
boolean matches = criteria == null;
|
||||
|
||||
if (!matches) {
|
||||
try {
|
||||
matches = criteria.getValue(candidate, Boolean.class);
|
||||
} catch (SpelEvaluationException e) {
|
||||
criteria.getEvaluationContext().setVariable("it", candidate);
|
||||
matches = criteria.getValue(Boolean.class);
|
||||
}
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
if (compareOffsetAndRows) {
|
||||
if (curPos >= offset && rows > 0) {
|
||||
result.add(candidate);
|
||||
remainingRows--;
|
||||
if (remainingRows <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
curPos++;
|
||||
} else {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.util.Comparator;
|
||||
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.domain.Sort.NullHandling;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.comparator.CompoundComparator;
|
||||
|
||||
/**
|
||||
* {@link SortAccessor} implementation capable of creating {@link SpelPropertyComparator}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
class SpelSortAccessor implements SortAccessor<Comparator<?>> {
|
||||
|
||||
private final SpelExpressionParser parser;
|
||||
|
||||
/**
|
||||
* @param parser must not be {@literal null}.
|
||||
*/
|
||||
public SpelSortAccessor(SpelExpressionParser parser) {
|
||||
|
||||
Assert.notNull(parser, "SpelExpressionParser must not be null!");
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.SortAccessor#resolve(org.springframework.data.keyvalue.core.query.KeyValueQuery)
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public Comparator<?> resolve(KeyValueQuery<?> query) {
|
||||
|
||||
if (query == null || query.getSort() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompoundComparator compoundComperator = new CompoundComparator();
|
||||
for (Order order : query.getSort()) {
|
||||
|
||||
SpelPropertyComparator<?> spelSort = new SpelPropertyComparator(order.getProperty(), parser);
|
||||
|
||||
if (Direction.DESC.equals(order.getDirection())) {
|
||||
|
||||
spelSort.desc();
|
||||
|
||||
if (order.getNullHandling() != null && !NullHandling.NATIVE.equals(order.getNullHandling())) {
|
||||
spelSort = NullHandling.NULLS_FIRST.equals(order.getNullHandling()) ? spelSort.nullsFirst() : spelSort
|
||||
.nullsLast();
|
||||
}
|
||||
}
|
||||
compoundComperator.addComparator(spelSort);
|
||||
}
|
||||
|
||||
return compoundComperator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2014 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.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class UncategorizedKeyValueException extends UncategorizedDataAccessException {
|
||||
|
||||
private static final long serialVersionUID = -8087116071859122297L;
|
||||
|
||||
public UncategorizedKeyValueException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2014 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 java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
|
||||
/**
|
||||
* Most trivial implementation of {@link PersistentProperty}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValuePersistentProperty extends AnnotationBasedPersistentProperty<KeyValuePersistentProperty> {
|
||||
|
||||
public KeyValuePersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
|
||||
PersistentEntity<?, KeyValuePersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
super(field, propertyDescriptor, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
|
||||
*/
|
||||
@Override
|
||||
protected Association<KeyValuePersistentProperty> createAssociation() {
|
||||
return new Association<KeyValuePersistentProperty>(this, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014 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.context;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
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.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValueMappingContext extends
|
||||
AbstractMappingContext<BasicPersistentEntity<?, KeyValuePersistentProperty>, KeyValuePersistentProperty> {
|
||||
|
||||
/*
|
||||
* (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);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
|
||||
*/
|
||||
@Override
|
||||
protected KeyValuePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
|
||||
BasicPersistentEntity<?, KeyValuePersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
return new KeyValuePersistentProperty(field, descriptor, owner, simpleTypeHolder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2014 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.query;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T> Criteria type
|
||||
*/
|
||||
public class KeyValueQuery<T> {
|
||||
|
||||
private Sort sort;
|
||||
private int offset = -1;
|
||||
private int rows = -1;
|
||||
private T criteria;
|
||||
|
||||
/**
|
||||
* Creates new instance of {@link KeyValueQuery}.
|
||||
*/
|
||||
public KeyValueQuery() {}
|
||||
|
||||
/**
|
||||
* Creates new instance of {@link KeyValueQuery} with given criteria.
|
||||
*
|
||||
* @param criteria can be {@literal null}.
|
||||
*/
|
||||
public KeyValueQuery(T criteria) {
|
||||
this.criteria = criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new instance of {@link KeyValueQuery} with given {@link Sort}.
|
||||
*
|
||||
* @param sort can be {@literal null}.
|
||||
*/
|
||||
public KeyValueQuery(Sort sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the criteria object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public T getCritieria() {
|
||||
return criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link Sort}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of elements to skip.
|
||||
*
|
||||
* @return negative value if not set.
|
||||
*/
|
||||
public int getOffset() {
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of elements to read.
|
||||
*
|
||||
* @return negative value if not set.
|
||||
*/
|
||||
public int getRows() {
|
||||
return this.rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of elements to skip.
|
||||
*
|
||||
* @param offset use negative value for none.
|
||||
*/
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of elements to read.
|
||||
*
|
||||
* @param offset use negative value for all.
|
||||
*/
|
||||
public void setRows(int rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link Sort} to be applied.
|
||||
*
|
||||
* @param sort
|
||||
*/
|
||||
public void setSort(Sort sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given {@link Sort}.
|
||||
*
|
||||
* @param sort {@literal null} {@link Sort} will be ignored.
|
||||
* @return
|
||||
*/
|
||||
public KeyValueQuery<T> orderBy(Sort sort) {
|
||||
|
||||
if (sort == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.sort != null) {
|
||||
this.sort.and(sort);
|
||||
} else {
|
||||
this.sort = sort;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see KeyValueQuery#setOffset(int)
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
public KeyValueQuery<T> skip(int offset) {
|
||||
setOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see KeyValueQuery#setRows(int)
|
||||
* @param rows
|
||||
* @return
|
||||
*/
|
||||
public KeyValueQuery<T> limit(int rows) {
|
||||
setRows(rows);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
public class BasicKeyValueRepository<T, ID extends Serializable> implements KeyValueRepository<T, ID> {
|
||||
|
||||
private final KeyValueOperations operations;
|
||||
private final EntityInformation<T, ID> entityInformation;
|
||||
|
||||
public BasicKeyValueRepository(EntityInformation<T, ID> metadata, KeyValueOperations operations) {
|
||||
|
||||
Assert.notNull(metadata, "Cannot initialize repository for 'null' metadata");
|
||||
Assert.notNull(operations, "Cannot initialize repository for 'null' operations");
|
||||
|
||||
this.entityInformation = metadata;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Sort sort) {
|
||||
return operations.findAll(sort, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@Override
|
||||
public Page<T> findAll(Pageable pageable) {
|
||||
|
||||
List<T> content = null;
|
||||
if (pageable.getSort() != null) {
|
||||
content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(),
|
||||
entityInformation.getJavaType());
|
||||
} else {
|
||||
content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
return new PageImpl<T>(content, pageable, this.operations.count(entityInformation.getJavaType()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> S save(S entity) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be 'null' for save.");
|
||||
|
||||
if (entityInformation.isNew(entity)) {
|
||||
operations.insert(entity);
|
||||
} else {
|
||||
operations.update(entityInformation.getId(entity), entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> Iterable<S> save(Iterable<S> entities) {
|
||||
|
||||
for (S entity : entities) {
|
||||
save(entity);
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public T findOne(ID id) {
|
||||
return operations.findById(id, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public boolean exists(ID id) {
|
||||
return findOne(id) != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll()
|
||||
*/
|
||||
@Override
|
||||
<<<<<<< Updated upstream:src/main/java/org/springframework/data/keyvalue/repository/BasicKeyValueRepository.java
|
||||
public Iterable<T> findAll() {
|
||||
return operations.findAllOf(entityInformation.getJavaType());
|
||||
=======
|
||||
public List<T> findAll() {
|
||||
return operations.findAll(entityInformation.getJavaType());
|
||||
>>>>>>> Stashed changes:src/main/java/org/springframework/data/keyvalue/repository/support/SimpleKeyValueRepository.java
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Iterable<ID> ids) {
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
|
||||
for (ID id : ids) {
|
||||
T candidate = findOne(id);
|
||||
if (candidate != null) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#count()
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
return operations.count(entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public void delete(ID id) {
|
||||
operations.delete(id, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void delete(T entity) {
|
||||
delete(entityInformation.getId(entity));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public void delete(Iterable<? extends T> entities) {
|
||||
|
||||
for (T entity : entities) {
|
||||
delete(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#deleteAll()
|
||||
*/
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
operations.delete(entityInformation.getJavaType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
public interface KeyValueRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
|
||||
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
|
||||
/**
|
||||
* Annotation to activate KeyValue repositories. If no base package is configured through either {@link #value()},
|
||||
* {@link #basePackages()} or {@link #basePackageClasses()} it will trigger scanning of the package of annotated class.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(KeyValueRepositoriesRegistrar.class)
|
||||
@QueryCreatorType(SpelQueryCreator.class)
|
||||
public @interface EnableKeyValueRepositories {
|
||||
|
||||
/**
|
||||
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
|
||||
* {@code @EnableJpaRepositories("org.my.pkg")} instead of {@code @EnableJpaRepositories(basePackages="org.my.pkg")}.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this
|
||||
* attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
|
||||
*/
|
||||
String[] basePackages() default {};
|
||||
|
||||
/**
|
||||
* Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
|
||||
* package of each class specified will be scanned. Consider creating a special no-op marker class or interface in
|
||||
* each package that serves no purpose other than being referenced by this attribute.
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
/**
|
||||
* Specifies which types are not eligible for component scanning.
|
||||
*/
|
||||
Filter[] excludeFilters() default {};
|
||||
|
||||
/**
|
||||
* Specifies which types are eligible for component scanning. Further narrows the set of candidate components from
|
||||
* everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters.
|
||||
*/
|
||||
Filter[] includeFilters() default {};
|
||||
|
||||
/**
|
||||
* Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So
|
||||
* for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning
|
||||
* for {@code PersonRepositoryImpl}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String repositoryImplementationPostfix() default "Impl";
|
||||
|
||||
/**
|
||||
* Configures the location of where to find the Spring Data named queries properties file.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String namedQueriesLocation() default "";
|
||||
|
||||
/**
|
||||
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
|
||||
* {@link Key#CREATE_IF_NOT_FOUND}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* Returns the {@link FactoryBean} class to be used for each repository instance. Defaults to
|
||||
* {@link KeyValueRepositoryFactoryBean}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> repositoryFactoryBeanClass() default KeyValueRepositoryFactoryBean.class;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link KeyValueOperations} bean to be used with the repositories detected.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String keyValueTemplateRef() default "keyValueTemplate";
|
||||
|
||||
/**
|
||||
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
|
||||
* repositories infrastructure.
|
||||
*/
|
||||
boolean considerNestedRepositories() default false;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
/**
|
||||
* KeyValue specific {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValueRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation()
|
||||
*/
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableKeyValueRepositories.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryConfigurationExtension getExtension() {
|
||||
return new KeyValueRepositoryConfigurationExtension();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
|
||||
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
||||
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
|
||||
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
|
||||
/**
|
||||
* {@link RepositoryConfigurationExtension} for {@link KeyValueRepository}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValueRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
|
||||
|
||||
private static final String MAPPING_CONTEXT_BEAN_NAME = "keyValueMappingContext";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtension#getRepositoryFactoryClassName()
|
||||
*/
|
||||
@Override
|
||||
public String getRepositoryFactoryClassName() {
|
||||
return KeyValueRepositoryFactoryBean.class.getName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModuleName()
|
||||
*/
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return "KeyValue";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModulePrefix()
|
||||
*/
|
||||
@Override
|
||||
protected String getModulePrefix() {
|
||||
return "keyvalue";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingTypes()
|
||||
*/
|
||||
@Override
|
||||
protected Collection<Class<?>> getIdentifyingTypes() {
|
||||
return Collections.<Class<?>> singleton(KeyValueRepository.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {
|
||||
|
||||
AnnotationAttributes attributes = config.getAttributes();
|
||||
|
||||
builder.addPropertyReference("keyValueOperations", attributes.getString("keyValueTemplateRef"));
|
||||
builder.addPropertyValue("queryCreator", getQueryCreatorType(config));
|
||||
builder.addPropertyReference("mappingContext", MAPPING_CONTEXT_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the query creator type to be used for the factory to set. Will lookup a {@link QueryCreatorType} annotation
|
||||
* on the {@code @Enable}-annotation or use {@link SpelQueryCreator} if not found.
|
||||
*
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
private static Class<?> getQueryCreatorType(AnnotationRepositoryConfigurationSource config) {
|
||||
|
||||
AnnotationMetadata metadata = config.getEnableAnnotationMetadata();
|
||||
|
||||
Map<String, Object> queryCreatorFoo = metadata.getAnnotationAttributes(QueryCreatorType.class.getName());
|
||||
|
||||
if (queryCreatorFoo == null) {
|
||||
return SpelQueryCreator.class;
|
||||
}
|
||||
|
||||
AnnotationAttributes queryCreatorAttributes = new AnnotationAttributes(queryCreatorFoo);
|
||||
return queryCreatorAttributes.getClass("value");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) {
|
||||
|
||||
super.registerBeansForRoot(registry, configurationSource);
|
||||
|
||||
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
|
||||
|
||||
RootBeanDefinition mappingContextDefinition = new RootBeanDefinition(KeyValueMappingContext.class);
|
||||
mappingContextDefinition.setSource(configurationSource.getSource());
|
||||
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, mappingContextDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.config;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
|
||||
/**
|
||||
* Annotation to customize the query creator type to be used for a specific store.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface QueryCreatorType {
|
||||
|
||||
Class<? extends AbstractQueryCreator<?, ?>> value();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012 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.repository.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
|
||||
|
||||
/**
|
||||
* {@link NamespaceHandler} to register {@link BeanDefinitionParser}s for key-value repositories.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
* @since 1.4
|
||||
*/
|
||||
public class RepositoryNameSpaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
|
||||
*/
|
||||
public void init() {
|
||||
|
||||
KeyValueRepositoryConfigurationExtension extension = new KeyValueRepositoryConfigurationExtension();
|
||||
RepositoryBeanDefinitionParser repositoryBeanDefinitionParser = new RepositoryBeanDefinitionParser(extension);
|
||||
registerBeanDefinitionParser("repositories", repositoryBeanDefinitionParser);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.query;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link RepositoryQuery} implementation deriving queries from {@link PartTree} using a predefined
|
||||
* {@link AbstractQueryCreator}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValuePartTreeQuery implements RepositoryQuery {
|
||||
|
||||
private final EvaluationContextProvider evaluationContextProvider;
|
||||
private final QueryMethod queryMethod;
|
||||
private final KeyValueOperations keyValueOperations;
|
||||
private final Class<? extends AbstractQueryCreator<?, ?>> queryCreator;
|
||||
|
||||
private KeyValueQuery<?> query;
|
||||
|
||||
public KeyValuePartTreeQuery(QueryMethod queryMethod, EvaluationContextProvider evalContextProvider,
|
||||
KeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {
|
||||
|
||||
this.queryMethod = queryMethod;
|
||||
this.keyValueOperations = keyValueOperations;
|
||||
this.evaluationContextProvider = evalContextProvider;
|
||||
this.queryCreator = queryCreator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryMethod getQueryMethod() {
|
||||
return queryMethod;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public Object execute(Object[] parameters) {
|
||||
|
||||
KeyValueQuery<?> query = prepareQuery(parameters);
|
||||
|
||||
if (queryMethod.isPageQuery() || queryMethod.isSliceQuery()) {
|
||||
|
||||
Pageable page = (Pageable) parameters[queryMethod.getParameters().getPageableIndex()];
|
||||
query.setOffset(page.getOffset());
|
||||
query.setRows(page.getPageSize());
|
||||
|
||||
List<?> result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType());
|
||||
|
||||
long count = queryMethod.isSliceQuery() ? 0 : keyValueOperations.count(query, queryMethod.getEntityInformation()
|
||||
.getJavaType());
|
||||
|
||||
return new PageImpl(result, page, count);
|
||||
|
||||
} else if (queryMethod.isCollectionQuery()) {
|
||||
|
||||
return this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType());
|
||||
|
||||
} else if (queryMethod.isQueryForEntity()) {
|
||||
|
||||
List<?> result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType());
|
||||
return CollectionUtils.isEmpty(result) ? null : result.get(0);
|
||||
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Query method not supported.");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private KeyValueQuery<?> prepareQuery(Object[] parameters) {
|
||||
|
||||
ParametersParameterAccessor accessor = new ParametersParameterAccessor(getQueryMethod().getParameters(), parameters);
|
||||
|
||||
if (this.query == null) {
|
||||
this.query = createQuery(accessor);
|
||||
}
|
||||
|
||||
KeyValueQuery<?> q = new KeyValueQuery(this.query.getCritieria());
|
||||
|
||||
if (accessor.getPageable() != null) {
|
||||
q.setOffset(accessor.getPageable().getOffset());
|
||||
q.setRows(accessor.getPageable().getPageSize());
|
||||
} else {
|
||||
q.setOffset(-1);
|
||||
q.setRows(-1);
|
||||
}
|
||||
|
||||
if (accessor.getSort() != null) {
|
||||
q.setSort(accessor.getSort());
|
||||
} else {
|
||||
q.setSort(this.query.getSort());
|
||||
}
|
||||
|
||||
if (q.getCritieria() instanceof SpelExpression) {
|
||||
EvaluationContext context = this.evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(),
|
||||
parameters);
|
||||
((SpelExpression) q.getCritieria()).setEvaluationContext(context);
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
public KeyValueQuery<?> createQuery(ParametersParameterAccessor accessor) {
|
||||
|
||||
PartTree tree = new PartTree(getQueryMethod().getName(), getQueryMethod().getEntityInformation().getJavaType());
|
||||
|
||||
Constructor<? extends AbstractQueryCreator<?, ?>> constructor = (Constructor<? extends AbstractQueryCreator<?, ?>>) ClassUtils
|
||||
.getConstructorIfAvailable(queryCreator, PartTree.class, ParameterAccessor.class);
|
||||
return (KeyValueQuery<?>) BeanUtils.instantiateClass(constructor, tree, accessor).createQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.query;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.data.repository.query.parser.PartTree.OrPart;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* {@link AbstractQueryCreator} to create {@link SpelExpression} based {@link KeyValueQuery}s.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExpression>, String> {
|
||||
|
||||
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
private final SpelExpression expression;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SpelQueryCreator} for the given {@link PartTree} and {@link ParameterAccessor}.
|
||||
*
|
||||
* @param tree must not be {@literal null}.
|
||||
* @param parameters must not be {@literal null}.
|
||||
*/
|
||||
public SpelQueryCreator(PartTree tree, ParameterAccessor parameters) {
|
||||
|
||||
super(tree, parameters);
|
||||
|
||||
this.expression = toPredicateExpression(tree);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator)
|
||||
*/
|
||||
@Override
|
||||
protected String create(Part part, Iterator<Object> iterator) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
|
||||
*/
|
||||
@Override
|
||||
protected String and(Part part, String base, Iterator<Object> iterator) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected String or(String base, String criteria) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyValueQuery<SpelExpression> complete(String criteria, Sort sort) {
|
||||
|
||||
KeyValueQuery<SpelExpression> query = new KeyValueQuery<SpelExpression>(this.expression);
|
||||
|
||||
if (sort != null) {
|
||||
query.orderBy(sort);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
protected SpelExpression toPredicateExpression(PartTree tree) {
|
||||
|
||||
int parameterIndex = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Iterator<OrPart> orPartIter = tree.iterator(); orPartIter.hasNext();) {
|
||||
|
||||
int partCnt = 0;
|
||||
StringBuilder partBuilder = new StringBuilder();
|
||||
OrPart orPart = orPartIter.next();
|
||||
|
||||
for (Iterator<Part> partIter = orPart.iterator(); partIter.hasNext();) {
|
||||
|
||||
Part part = partIter.next();
|
||||
partBuilder.append("#it?.");
|
||||
partBuilder.append(part.getProperty().toDotPath().replace(".", "?."));
|
||||
|
||||
// TODO: check if we can have caseinsensitive search
|
||||
if (!part.shouldIgnoreCase().equals(IgnoreCaseType.NEVER)) {
|
||||
throw new InvalidDataAccessApiUsageException("Ignore case not supported!");
|
||||
}
|
||||
|
||||
switch (part.getType()) {
|
||||
case TRUE:
|
||||
partBuilder.append("?.equals(true)");
|
||||
break;
|
||||
case FALSE:
|
||||
partBuilder.append("?.equals(false)");
|
||||
break;
|
||||
case SIMPLE_PROPERTY:
|
||||
partBuilder.append("?.equals(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
case IS_NULL:
|
||||
partBuilder.append(" == null");
|
||||
break;
|
||||
case IS_NOT_NULL:
|
||||
partBuilder.append(" != null");
|
||||
break;
|
||||
case LIKE:
|
||||
partBuilder.append("?.contains(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
case STARTING_WITH:
|
||||
partBuilder.append("?.startsWith(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
case AFTER:
|
||||
case GREATER_THAN:
|
||||
partBuilder.append(">").append("[").append(parameterIndex++).append("]");
|
||||
break;
|
||||
case GREATER_THAN_EQUAL:
|
||||
partBuilder.append(">=").append("[").append(parameterIndex++).append("]");
|
||||
break;
|
||||
case BEFORE:
|
||||
case LESS_THAN:
|
||||
partBuilder.append("<").append("[").append(parameterIndex++).append("]");
|
||||
break;
|
||||
case LESS_THAN_EQUAL:
|
||||
partBuilder.append("<=").append("[").append(parameterIndex++).append("]");
|
||||
break;
|
||||
case ENDING_WITH:
|
||||
partBuilder.append("?.endsWith(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
case BETWEEN:
|
||||
|
||||
int index = partBuilder.lastIndexOf("#it?.");
|
||||
|
||||
partBuilder.insert(index, "(");
|
||||
partBuilder.append(">").append("[").append(parameterIndex++).append("]");
|
||||
partBuilder.append("&&");
|
||||
partBuilder.append("#it?.");
|
||||
partBuilder.append(part.getProperty().toDotPath().replace(".", "?."));
|
||||
partBuilder.append("<").append("[").append(parameterIndex++).append("]");
|
||||
partBuilder.append(")");
|
||||
|
||||
break;
|
||||
|
||||
case REGEX:
|
||||
|
||||
partBuilder.append(" matches ").append("[").append(parameterIndex++).append("]");
|
||||
break;
|
||||
case IN:
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
case EXISTS:
|
||||
default:
|
||||
throw new InvalidDataAccessApiUsageException(String.format("Found invalid part '%s' in query",
|
||||
part.getType()));
|
||||
}
|
||||
|
||||
if (partIter.hasNext()) {
|
||||
partBuilder.append("&&");
|
||||
}
|
||||
|
||||
partCnt++;
|
||||
}
|
||||
|
||||
if (partCnt > 1) {
|
||||
sb.append("(").append(partBuilder).append(")");
|
||||
} else {
|
||||
sb.append(partBuilder);
|
||||
}
|
||||
|
||||
if (orPartIter.hasNext()) {
|
||||
sb.append("||");
|
||||
}
|
||||
}
|
||||
|
||||
return PARSER.parseRaw(sb.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mysema.query.support.Expressions;
|
||||
import com.mysema.query.types.Expression;
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.OrderSpecifier.NullHandling;
|
||||
import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.path.PathBuilder;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
abstract class KeyValueQueryDslUtils {
|
||||
|
||||
private KeyValueQueryDslUtils() {
|
||||
// prevent instantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a plain {@link Order} into a QueryDsl specific {@link OrderSpecifier}.
|
||||
*
|
||||
* @param sort
|
||||
* @param builder must not be {@literal null}.
|
||||
* @return empty {@code OrderSpecifier<?>[]} when sort is {@literal null}.
|
||||
*/
|
||||
public static OrderSpecifier<?>[] toOrderSpecifier(Sort sort, PathBuilder<?> builder) {
|
||||
|
||||
Assert.notNull(builder, "Builder must not be 'null'.");
|
||||
|
||||
if (sort == null) {
|
||||
return new OrderSpecifier<?>[0];
|
||||
}
|
||||
|
||||
List<OrderSpecifier<?>> specifiers = null;
|
||||
|
||||
if (sort instanceof QSort) {
|
||||
specifiers = ((QSort) sort).getOrderSpecifiers();
|
||||
} else {
|
||||
|
||||
specifiers = new ArrayList<OrderSpecifier<?>>();
|
||||
for (Order order : sort) {
|
||||
specifiers.add(toOrderSpecifier(order, builder));
|
||||
}
|
||||
}
|
||||
|
||||
return specifiers.toArray(new OrderSpecifier<?>[specifiers.size()]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static OrderSpecifier<?> toOrderSpecifier(Order order, PathBuilder<?> builder) {
|
||||
return new OrderSpecifier(order.isAscending() ? com.mysema.query.types.Order.ASC
|
||||
: com.mysema.query.types.Order.DESC, buildOrderPropertyPathFrom(order, builder),
|
||||
toQueryDslNullHandling(order.getNullHandling()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link Expression} for the given {@link Order} property.
|
||||
*
|
||||
* @param order must not be {@literal null}.
|
||||
* @param builder must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static Expression<?> buildOrderPropertyPathFrom(Order order, PathBuilder<?> builder) {
|
||||
|
||||
Assert.notNull(order, "Order must not be null!");
|
||||
Assert.notNull(builder, "Builder must not be null!");
|
||||
|
||||
PropertyPath path = PropertyPath.from(order.getProperty(), builder.getType());
|
||||
Expression<?> sortPropertyExpression = builder;
|
||||
|
||||
while (path != null) {
|
||||
|
||||
if (!path.hasNext() && order.isIgnoreCase()) {
|
||||
// if order is ignore-case we have to treat the last path segment as a String.
|
||||
sortPropertyExpression = Expressions.stringPath((Path<?>) sortPropertyExpression, path.getSegment()).lower();
|
||||
} else {
|
||||
sortPropertyExpression = Expressions.path(path.getType(), (Path<?>) sortPropertyExpression, path.getSegment());
|
||||
}
|
||||
|
||||
path = path.next();
|
||||
}
|
||||
|
||||
return sortPropertyExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given {@link org.springframework.data.domain.Sort.NullHandling} to the appropriate Querydsl
|
||||
* {@link NullHandling}.
|
||||
*
|
||||
* @param nullHandling must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static NullHandling toQueryDslNullHandling(org.springframework.data.domain.Sort.NullHandling nullHandling) {
|
||||
|
||||
Assert.notNull(nullHandling, "NullHandling must not be null!");
|
||||
|
||||
switch (nullHandling) {
|
||||
|
||||
case NULLS_FIRST:
|
||||
return NullHandling.NullsFirst;
|
||||
|
||||
case NULLS_LAST:
|
||||
return NullHandling.NullsLast;
|
||||
|
||||
case NATIVE:
|
||||
default:
|
||||
return NullHandling.Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.support;
|
||||
|
||||
import static org.springframework.data.querydsl.QueryDslUtils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery;
|
||||
import org.springframework.data.keyvalue.repository.query.SpelQueryCreator;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.PersistentEntityInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RepositoryFactorySupport} specific of handing
|
||||
* {@link org.springframework.data.keyvalue.repository.KeyValueRepository}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValueRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private static final Class<SpelQueryCreator> DEFAULT_QUERY_CREATOR = SpelQueryCreator.class;
|
||||
|
||||
private final KeyValueOperations keyValueOperations;
|
||||
private final MappingContext<?, ?> context;
|
||||
private final Class<? extends AbstractQueryCreator<?, ?>> queryCreator;
|
||||
|
||||
/**
|
||||
* Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations}.
|
||||
*
|
||||
* @param keyValueOperations must not be {@literal null}.
|
||||
*/
|
||||
public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations) {
|
||||
this(keyValueOperations, DEFAULT_QUERY_CREATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations} and
|
||||
* {@link AbstractQueryCreator}-type.
|
||||
*
|
||||
* @param keyValueOperations must not be {@literal null}.
|
||||
* @param queryCreator defaulted to {@link #DEFAULT_QUERY_CREATOR} if {@literal null}.
|
||||
*/
|
||||
public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations,
|
||||
Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {
|
||||
|
||||
Assert.notNull(keyValueOperations, "KeyValueOperations must not be null!");
|
||||
Assert.notNull(queryCreator, "Query creator type must not be null!");
|
||||
|
||||
this.queryCreator = queryCreator;
|
||||
this.keyValueOperations = keyValueOperations;
|
||||
this.context = keyValueOperations.getMappingContext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
|
||||
|
||||
PersistentEntity<T, ?> entity = (PersistentEntity<T, ?>) context.getPersistentEntity(domainClass);
|
||||
PersistentEntityInformation<T, ID> entityInformation = new PersistentEntityInformation<T, ID>(entity);
|
||||
|
||||
return entityInformation;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected Object getTargetRepository(RepositoryMetadata metadata) {
|
||||
|
||||
EntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
|
||||
|
||||
if (ClassUtils.isAssignable(QueryDslPredicateExecutor.class, metadata.getRepositoryInterface())) {
|
||||
return new QueryDslKeyValueRepository(entityInformation, keyValueOperations);
|
||||
}
|
||||
|
||||
return new SimpleKeyValueRepository(entityInformation, keyValueOperations);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
|
||||
return isQueryDslRepository(metadata.getRepositoryInterface()) ? QueryDslKeyValueRepository.class
|
||||
: SimpleKeyValueRepository.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given repository interface requires a QueryDsl specific implementation to be chosen.
|
||||
*
|
||||
* @param repositoryInterface must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static boolean isQueryDslRepository(Class<?> repositoryInterface) {
|
||||
return QUERY_DSL_PRESENT && QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
|
||||
*/
|
||||
@Override
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) {
|
||||
return new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, this.queryCreator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
*/
|
||||
private static class KeyValueQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private EvaluationContextProvider evaluationContextProvider;
|
||||
private KeyValueOperations keyValueOperations;
|
||||
|
||||
private Class<? extends AbstractQueryCreator<?, ?>> queryCreator;
|
||||
|
||||
/**
|
||||
* Creates a new {@link KeyValueQueryLookupStrategy} for the given {@link Key}, {@link EvaluationContextProvider},
|
||||
* {@link KeyValueOperations} and query creator type.
|
||||
* <p>
|
||||
* TODO: Key is not considered. Should it?
|
||||
*
|
||||
* @param key
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
* @param keyValueOperations must not be {@literal null}.
|
||||
* @param queryCreator must not be {@literal null}.
|
||||
*/
|
||||
public KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider,
|
||||
KeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {
|
||||
|
||||
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!");
|
||||
Assert.notNull(keyValueOperations, "KeyValueOperations must not be null!");
|
||||
Assert.notNull(queryCreator, "Query creator type must not be null!");
|
||||
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.keyValueOperations = keyValueOperations;
|
||||
this.queryCreator = queryCreator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.repository.core.NamedQueries)
|
||||
*/
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
|
||||
|
||||
QueryMethod queryMethod = new QueryMethod(method, metadata);
|
||||
return new KeyValuePartTreeQuery(queryMethod, evaluationContextProvider, this.keyValueOperations,
|
||||
this.queryCreator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} to create {@link KeyValueRepository}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class KeyValueRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends
|
||||
RepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
private KeyValueOperations operations;
|
||||
private Class<? extends AbstractQueryCreator<?, ?>> queryCreator;
|
||||
|
||||
public void setKeyValueOperations(KeyValueOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#setMappingContext(org.springframework.data.mapping.context.MappingContext)
|
||||
*/
|
||||
@Override
|
||||
public void setMappingContext(MappingContext<?, ?> mappingContext) {
|
||||
super.setMappingContext(mappingContext);
|
||||
}
|
||||
|
||||
public void setQueryCreator(Class<? extends AbstractQueryCreator<?, ?>> queryCreator) {
|
||||
this.queryCreator = queryCreator;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory()
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory() {
|
||||
return new KeyValueRepositoryFactory(this.operations, this.queryCreator);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.support;
|
||||
|
||||
import static org.springframework.data.keyvalue.repository.support.KeyValueQueryDslUtils.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.querydsl.SimpleEntityPathResolver;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mysema.query.collections.CollQuery;
|
||||
import com.mysema.query.support.ProjectableQuery;
|
||||
import com.mysema.query.types.EntityPath;
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.Predicate;
|
||||
import com.mysema.query.types.path.PathBuilder;
|
||||
|
||||
/**
|
||||
* {@link KeyValueRepository} implementation capable of executing {@link Predicate}s using {@link CollQuery}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
public class QueryDslKeyValueRepository<T, ID extends Serializable> extends SimpleKeyValueRepository<T, ID> implements
|
||||
QueryDslPredicateExecutor<T> {
|
||||
|
||||
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
|
||||
|
||||
private final EntityPath<T> path;
|
||||
private final PathBuilder<T> builder;
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryDslKeyValueRepository} for the given {@link EntityInformation} and
|
||||
* {@link KeyValueOperations}.
|
||||
*
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
*/
|
||||
public QueryDslKeyValueRepository(EntityInformation<T, ID> entityInformation, KeyValueOperations operations) {
|
||||
this(entityInformation, operations, DEFAULT_ENTITY_PATH_RESOLVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryDslKeyValueRepository} for the given {@link EntityInformation},
|
||||
* {@link KeyValueOperations} and {@link EntityPathResolver}.
|
||||
*
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
* @param resolver must not be {@literal null}.
|
||||
*/
|
||||
public QueryDslKeyValueRepository(EntityInformation<T, ID> entityInformation, KeyValueOperations operations,
|
||||
EntityPathResolver resolver) {
|
||||
|
||||
super(entityInformation, operations);
|
||||
|
||||
Assert.notNull(resolver, "EntityPathResolver must not be null!");
|
||||
|
||||
this.path = resolver.createPath(entityInformation.getJavaType());
|
||||
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findOne(com.mysema.query.types.Predicate)
|
||||
*/
|
||||
@Override
|
||||
public T findOne(Predicate predicate) {
|
||||
return prepareQuery(predicate).uniqueResult(builder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Predicate predicate) {
|
||||
return prepareQuery(predicate).list(builder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier[])
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
|
||||
|
||||
ProjectableQuery<?> query = prepareQuery(predicate);
|
||||
query.orderBy(orders);
|
||||
|
||||
return query.list(builder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@Override
|
||||
public Page<T> findAll(Predicate predicate, Pageable pageable) {
|
||||
|
||||
ProjectableQuery<?> query = prepareQuery(predicate);
|
||||
|
||||
if (pageable != null) {
|
||||
|
||||
query.offset(pageable.getOffset());
|
||||
query.limit(pageable.getPageSize());
|
||||
|
||||
if (pageable.getSort() != null) {
|
||||
query.orderBy(toOrderSpecifier(pageable.getSort(), builder));
|
||||
}
|
||||
}
|
||||
|
||||
return new PageImpl<T>(query.list(builder), pageable, count(predicate));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#count(com.mysema.query.types.Predicate)
|
||||
*/
|
||||
@Override
|
||||
public long count(Predicate predicate) {
|
||||
return prepareQuery(predicate).count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates executable query for given {@link Predicate}.
|
||||
*
|
||||
* @param predicate
|
||||
* @return
|
||||
*/
|
||||
protected ProjectableQuery<?> prepareQuery(Predicate predicate) {
|
||||
|
||||
CollQuery query = new CollQuery();
|
||||
|
||||
query.from(builder, findAll());
|
||||
query.where(predicate);
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2014 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.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.keyvalue.core.KeyValueOperations;
|
||||
import org.springframework.data.keyvalue.repository.KeyValueRepository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @since 1.10
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
public class SimpleKeyValueRepository<T, ID extends Serializable> implements KeyValueRepository<T, ID> {
|
||||
|
||||
private final KeyValueOperations operations;
|
||||
private final EntityInformation<T, ID> entityInformation;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleKeyValueRepository} for the given {@link EntityInformation} and
|
||||
* {@link KeyValueOperations}.
|
||||
*
|
||||
* @param metadata must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
*/
|
||||
public SimpleKeyValueRepository(EntityInformation<T, ID> metadata, KeyValueOperations operations) {
|
||||
|
||||
Assert.notNull(metadata, "EntityInformation must not be null!");
|
||||
Assert.notNull(operations, "KeyValueOperations must not be null!");
|
||||
|
||||
this.entityInformation = metadata;
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Sort sort) {
|
||||
return operations.findAll(sort, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@Override
|
||||
public Page<T> findAll(Pageable pageable) {
|
||||
|
||||
if (pageable == null) {
|
||||
List<T> result = findAll();
|
||||
return new PageImpl<T>(result, null, result.size());
|
||||
}
|
||||
|
||||
List<T> content = null;
|
||||
|
||||
content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(),
|
||||
entityInformation.getJavaType());
|
||||
|
||||
return new PageImpl<T>(content, pageable, this.operations.count(entityInformation.getJavaType()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> S save(S entity) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
|
||||
if (entityInformation.isNew(entity)) {
|
||||
operations.insert(entity);
|
||||
} else {
|
||||
operations.update(entityInformation.getId(entity), entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> Iterable<S> save(Iterable<S> entities) {
|
||||
|
||||
for (S entity : entities) {
|
||||
save(entity);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public T findOne(ID id) {
|
||||
return operations.findById(id, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public boolean exists(ID id) {
|
||||
return findOne(id) != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll()
|
||||
*/
|
||||
@Override
|
||||
public List<T> findAll() {
|
||||
return operations.findAll(entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Iterable<ID> ids) {
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
|
||||
for (ID id : ids) {
|
||||
|
||||
T candidate = findOne(id);
|
||||
|
||||
if (candidate != null) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#count()
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
return operations.count(entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public void delete(ID id) {
|
||||
operations.delete(id, entityInformation.getJavaType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void delete(T entity) {
|
||||
delete(entityInformation.getId(entity));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public void delete(Iterable<? extends T> entities) {
|
||||
|
||||
for (T entity : entities) {
|
||||
delete(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#deleteAll()
|
||||
*/
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
operations.delete(entityInformation.getJavaType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2014 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.map;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.data.keyvalue.core.AbstractKeyValueAdapter;
|
||||
import org.springframework.data.keyvalue.core.KeyValueAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link KeyValueAdapter} implementation for {@link Map}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class MapKeyValueAdapter extends AbstractKeyValueAdapter {
|
||||
|
||||
private final Map<Serializable, Map<Serializable, Object>> data;
|
||||
|
||||
@SuppressWarnings("rawtypes")//
|
||||
private final Class<? extends Map> mapType;
|
||||
|
||||
/**
|
||||
* Create new instance of {@link MapKeyValueAdapter} using {@link ConcurrentHashMap}.
|
||||
*/
|
||||
public MapKeyValueAdapter() {
|
||||
this(new ConcurrentHashMap<Serializable, Map<Serializable, Object>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new instance of {@link MapKeyValueAdapter} using given dataStore for persistence.
|
||||
*
|
||||
* @param dataStore must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public MapKeyValueAdapter(Map<Serializable, Map<Serializable, Object>> dataStore) {
|
||||
|
||||
Assert.notNull(dataStore, "Cannot initilalize adapter with 'null' datastore.");
|
||||
|
||||
this.data = dataStore;
|
||||
this.mapType = (Class<? extends Map>) ClassUtils.getUserClass(dataStore);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#put(java.io.Serializable, java.lang.Object, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Object put(Serializable id, Object item, Serializable keyspace) {
|
||||
|
||||
Assert.notNull(id, "Cannot add item with 'null' id.");
|
||||
Assert.notNull(keyspace, "Cannot add item for 'null' collection.");
|
||||
|
||||
return getKeySpaceMap(keyspace).put(id, item);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#contains(java.io.Serializable, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Serializable id, Serializable keyspace) {
|
||||
return get(id, keyspace) != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#get(java.io.Serializable, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Object get(Serializable id, Serializable keyspace) {
|
||||
|
||||
Assert.notNull(id, "Cannot get item with 'null' id.");
|
||||
return getKeySpaceMap(keyspace).get(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#delete(java.io.Serializable, java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Object delete(Serializable id, Serializable keyspace) {
|
||||
|
||||
Assert.notNull(id, "Cannot delete item with 'null' id.");
|
||||
return getKeySpaceMap(keyspace).remove(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#getAllOf(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Collection<?> getAllOf(Serializable keyspace) {
|
||||
return getKeySpaceMap(keyspace).values();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#deleteAllOf(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public void deleteAllOf(Serializable keyspace) {
|
||||
getKeySpaceMap(keyspace).clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.keyvalue.core.KeyValueAdapter#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.DisposableBean#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get map associated with given keyspace.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected Map<Serializable, Object> getKeySpaceMap(Serializable keyspace) {
|
||||
|
||||
Assert.notNull(keyspace, "Collection must not be 'null' for lookup.");
|
||||
|
||||
Map<Serializable, Object> map = data.get(keyspace);
|
||||
|
||||
if (map != null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
addMapForKeySpace(keyspace);
|
||||
return data.get(keyspace);
|
||||
}
|
||||
|
||||
private void addMapForKeySpace(Serializable keyspace) {
|
||||
data.put(keyspace, CollectionFactory.<Serializable, Object> createMap(mapType, 1000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2014 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.map;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class MapKeyValueAdapterFactory {
|
||||
|
||||
@SuppressWarnings("rawtypes")//
|
||||
private static final Class<? extends Map> DEFAULT_MAP_TYPE = ConcurrentHashMap.class;
|
||||
|
||||
@SuppressWarnings("rawtypes")//
|
||||
private Class<? extends Map> mapType;
|
||||
private Map<Serializable, Map<? extends Serializable, ?>> initialValues;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MapKeyValueAdapterFactory}.
|
||||
*
|
||||
* @see MapKeyValueAdapterFactory#MapKeyValueAdapterFactory(Class)
|
||||
*/
|
||||
public MapKeyValueAdapterFactory() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MKVAF with the given {@link Map} type to be used to hold the values in.
|
||||
*
|
||||
* @param type any {@link Class} of type {@link Map}. Can be {@literal null} and will be defaulted to
|
||||
* {@link ConcurrentHashMap}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public MapKeyValueAdapterFactory(Class<? extends Map> type) {
|
||||
|
||||
this.mapType = type;
|
||||
this.initialValues = new HashMap<Serializable, Map<? extends Serializable, ?>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set values for a given {@literal keyspace} that to populate the adapter after creation.
|
||||
*
|
||||
* @param keyspace must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
public void setInitialValuesForKeyspace(Serializable keyspace, Map<? extends Serializable, ?> values) {
|
||||
|
||||
Assert.notNull(keyspace, "KeySpace must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
initialValues.put(keyspace, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link Map} type to be used as backing store.
|
||||
*
|
||||
* @param mapType must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setMapType(Class<? extends Map> mapType) {
|
||||
|
||||
Assert.notNull(mapType, "May type must not be null!");
|
||||
|
||||
this.mapType = mapType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and populates the adapter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public MapKeyValueAdapter getAdapter() {
|
||||
|
||||
MapKeyValueAdapter adapter = createAdapter();
|
||||
populateAdapter(adapter);
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
private MapKeyValueAdapter createAdapter() {
|
||||
|
||||
Class<?> type = this.mapType == null ? DEFAULT_MAP_TYPE : this.mapType;
|
||||
|
||||
MapKeyValueAdapter adapter = new MapKeyValueAdapter(
|
||||
CollectionFactory.<Serializable, Map<Serializable, Object>> createMap(type, 100));
|
||||
return adapter;
|
||||
}
|
||||
|
||||
private void populateAdapter(MapKeyValueAdapter adapter) {
|
||||
|
||||
if (!initialValues.isEmpty()) {
|
||||
for (Entry<Serializable, Map<? extends Serializable, ?>> entry : initialValues.entrySet()) {
|
||||
for (Entry<? extends Serializable, ?> obj : entry.getValue().entrySet()) {
|
||||
adapter.put(obj.getKey(), obj.getValue(), entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user