diff --git a/src/main/java/org/springframework/data/keyvalue/annotation/package-info.java b/src/main/java/org/springframework/data/keyvalue/annotation/package-info.java new file mode 100644 index 0000000..e4994ad --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/annotation/package-info.java @@ -0,0 +1,6 @@ +/** + * Key-Value annotations for declarative keyspace configuration. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.annotation; diff --git a/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java index 907d4a0..060b4f4 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/AbstractKeyValueAdapter.java @@ -18,6 +18,7 @@ package org.springframework.data.keyvalue.core; import java.util.Collection; import org.springframework.data.keyvalue.core.query.KeyValueQuery; +import org.springframework.lang.Nullable; /** * Base implementation of {@link KeyValueAdapter} holds {@link QueryEngine} to delegate {@literal find} and @@ -42,7 +43,7 @@ public abstract class AbstractKeyValueAdapter implements KeyValueAdapter { * * @param engine will be defaulted to {@link SpelQueryEngine} if {@literal null}. */ - protected AbstractKeyValueAdapter(QueryEngine engine) { + protected AbstractKeyValueAdapter(@Nullable QueryEngine engine) { this.engine = engine != null ? engine : new SpelQueryEngine(); this.engine.registerAdapter(this); @@ -61,6 +62,7 @@ public abstract class AbstractKeyValueAdapter implements KeyValueAdapter { * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueAdapter#get(java.lang.Object, java.lang.String, java.lang.Class) */ + @Nullable @Override public T get(Object id, String keyspace, Class type) { return type.cast(get(id, keyspace)); @@ -70,6 +72,7 @@ public abstract class AbstractKeyValueAdapter implements KeyValueAdapter { * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueAdapter#delete(java.lang.Object, java.lang.String, java.lang.Class) */ + @Nullable @Override public T delete(Object id, String keyspace, Class type) { return type.cast(delete(id, keyspace)); diff --git a/src/main/java/org/springframework/data/keyvalue/core/CriteriaAccessor.java b/src/main/java/org/springframework/data/keyvalue/core/CriteriaAccessor.java index 810cba3..b29cfd5 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/CriteriaAccessor.java +++ b/src/main/java/org/springframework/data/keyvalue/core/CriteriaAccessor.java @@ -21,6 +21,7 @@ import org.springframework.data.keyvalue.core.query.KeyValueQuery; * Resolves the criteria object from given {@link KeyValueQuery}. * * @author Christoph Strobl + * @author Mark Paluch * @param */ public interface CriteriaAccessor { @@ -29,7 +30,7 @@ public interface CriteriaAccessor { * Checks and reads {@link KeyValueQuery#getCriteria()} of given {@link KeyValueQuery}. Might also apply additional * transformation to match the desired type. * - * @param query can be {@literal null}. + * @param query must not 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}. diff --git a/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java b/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java index 0ecc15d..981d993 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java +++ b/src/main/java/org/springframework/data/keyvalue/core/ForwardingCloseableIterator.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.data.keyvalue.core; import java.util.Iterator; import org.springframework.data.util.CloseableIterator; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -26,13 +27,14 @@ import org.springframework.util.Assert; * @author Christoph Strobl * @author Thomas Darimont * @author Oliver Gierke + * @author Mark Paluch * @param * @param */ public class ForwardingCloseableIterator implements CloseableIterator { private final Iterator delegate; - private final Runnable closeHandler; + private final @Nullable Runnable closeHandler; /** * Creates a new {@link ForwardingCloseableIterator}. @@ -50,7 +52,7 @@ public class ForwardingCloseableIterator implements CloseableIterator { * @param delegate must not be {@literal null}. * @param closeHandler may be {@literal null}. */ - public ForwardingCloseableIterator(Iterator delegate, Runnable closeHandler) { + public ForwardingCloseableIterator(Iterator delegate, @Nullable Runnable closeHandler) { Assert.notNull(delegate, "Delegate iterator must not be null!"); diff --git a/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java b/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java index cb2ee81..4bb7ae8 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java @@ -24,6 +24,7 @@ import java.util.List; * Converter capable of transforming a given {@link Iterable} into a collection type. * * @author Christoph Strobl + * @author Mark Paluch */ public final class IterableConverter { @@ -37,10 +38,6 @@ public final class IterableConverter { */ public static List toList(Iterable source) { - if (source == null) { - return Collections.emptyList(); - } - if (source instanceof List) { return (List) source; } diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java index 15c906e..ca6f805 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java @@ -21,12 +21,14 @@ import java.util.Map; import org.springframework.beans.factory.DisposableBean; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.util.CloseableIterator; +import org.springframework.lang.Nullable; /** * {@link KeyValueAdapter} unifies access and shields the underlying key/value specific implementation. * * @author Christoph Strobl * @author Thomas Darimont + * @author Mark Paluch */ public interface KeyValueAdapter extends DisposableBean { @@ -55,33 +57,41 @@ public interface KeyValueAdapter extends DisposableBean { * @param keyspace must not be {@literal null}. * @return {@literal null} in case no matching item exists. */ + @Nullable Object get(Object id, String keyspace); /** - * @param id - * @param keyspace - * @param type - * @return + * Get the object with given id from keyspace. + * + * @param id must not be {@literal null}. + * @param keyspace must not be {@literal null}. + * @param type must not be {@literal null}. + * @return {@literal null} in case no matching item exists. * @since 1.1 */ + @Nullable T get(Object id, String keyspace, Class type); /** - * Delete and return the obect with given type and id. + * Delete and return the object 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 */ + @Nullable Object delete(Object id, String keyspace); /** - * @param id - * @param keyspace - * @param type - * @return + * Delete and return the object with given type and id. + * + * @param id must not be {@literal null}. + * @param keyspace must not be {@literal null}. + * @param type must not be {@literal null}. + * @return {@literal null} if object could not be found * @since 1.1 */ + @Nullable T delete(Object id, String keyspace, Class type); /** @@ -95,7 +105,7 @@ public interface KeyValueAdapter extends DisposableBean { /** * Returns a {@link CloseableIterator} that iterates over all entries. * - * @param keyspace + * @param keyspace must not be {@literal null}. * @return */ CloseableIterator> entries(String keyspace); @@ -115,17 +125,17 @@ public interface KeyValueAdapter extends DisposableBean { /** * Find all matching objects within {@literal keyspace}. * - * @param query + * @param query must not be {@literal null}. * @param keyspace must not be {@literal null}. * @return empty {@link Collection} if no match found. */ Iterable find(KeyValueQuery query, String keyspace); /** - * @param query - * @param keyspace - * @param type - * @return + * @param query must not be {@literal null}. + * @param keyspace must not be {@literal null}. + * @param type must not be {@literal null}. + * @return empty {@link Collection} if no match found. * @since 1.1 */ Iterable find(KeyValueQuery query, String keyspace, Class type); @@ -134,13 +144,14 @@ public interface KeyValueAdapter extends DisposableBean { * Count number of objects within {@literal keyspace}. * * @param keyspace must not be {@literal null}. + * @return */ long count(String keyspace); /** * Count all matching objects within {@literal keyspace}. * - * @param query + * @param query must not be {@literal null}. * @param keyspace must not be {@literal null}. * @return */ diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueCallback.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueCallback.java index 93f85da..b54c969 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueCallback.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2017 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. @@ -15,11 +15,14 @@ */ package org.springframework.data.keyvalue.core; +import org.springframework.lang.Nullable; + /** * 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 + * @author Mark Paluch * @param */ public interface KeyValueCallback { @@ -31,5 +34,6 @@ public interface KeyValueCallback { * @param adapter * @return */ + @Nullable T doInKeyValue(KeyValueAdapter adapter); } diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java index 98b7675..d534a36 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java @@ -22,11 +22,13 @@ 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; +import org.springframework.lang.Nullable; /** * Interface that specifies a basic set of key/value operations. Implemented by {@link KeyValueTemplate}. * * @author Christoph Strobl + * @author Mark Paluch */ public interface KeyValueOperations extends DisposableBean { @@ -81,6 +83,7 @@ public interface KeyValueOperations extends DisposableBean { * @param action must not be {@literal null}. * @return */ + @Nullable T execute(KeyValueCallback action); /** @@ -139,6 +142,7 @@ public interface KeyValueOperations extends DisposableBean { * @param objectToDelete must not be {@literal null}. * @return */ + @Nullable T delete(T objectToDelete); /** @@ -148,6 +152,7 @@ public interface KeyValueOperations extends DisposableBean { * @param type must not be {@literal null}. * @return the deleted item or {@literal null} if no match found. */ + @Nullable T delete(Object id, Class type); /** diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValuePersistenceExceptionTranslator.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValuePersistenceExceptionTranslator.java index 302631e..7341764 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValuePersistenceExceptionTranslator.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValuePersistenceExceptionTranslator.java @@ -20,6 +20,7 @@ import java.util.NoSuchElementException; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -27,6 +28,7 @@ import org.springframework.util.Assert; * exception to an appropriate exception from the {@code org.springframework.dao} hierarchy. * * @author Christoph Strobl + * @author Mark Paluch */ public class KeyValuePersistenceExceptionTranslator implements PersistenceExceptionTranslator { @@ -34,6 +36,7 @@ public class KeyValuePersistenceExceptionTranslator implements PersistenceExcept * (non-Javadoc) * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException) */ + @Nullable @Override public DataAccessException translateExceptionIfPossible(RuntimeException exception) { @@ -51,6 +54,7 @@ public class KeyValuePersistenceExceptionTranslator implements PersistenceExcept if (exception.getClass().getName().startsWith("java")) { return new UncategorizedKeyValueException(exception.getMessage(), exception); } + return null; } } diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java index cef7703..85755ac 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java @@ -34,6 +34,7 @@ import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -55,7 +56,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub private final IdentifierGenerator identifierGenerator; private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR; - private ApplicationEventPublisher eventPublisher; + private @Nullable ApplicationEventPublisher eventPublisher; private boolean publishEvents = true; private @SuppressWarnings("rawtypes") Set> eventTypesToPublish = Collections .emptySet(); @@ -215,14 +216,10 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub Assert.notNull(type, "Type to fetch must not be null!"); - return execute(adapter -> { + return executeRequired(adapter -> { Iterable values = adapter.getAllOf(resolveKeySpace(type)); - if (values == null) { - return Collections.emptySet(); - } - ArrayList filtered = new ArrayList<>(); for (Object candidate : values) { if (typeCheck(type, candidate)) { @@ -336,6 +333,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueOperations#execute(org.springframework.data.keyvalue.core.KeyValueCallback) */ + @Nullable @Override public T execute(KeyValueCallback action) { @@ -348,6 +346,24 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub } } + /** + * Execute {@link KeyValueCallback} and require a non-{@literal null} return value. + * + * @param action + * @param + * @return + */ + protected T executeRequired(KeyValueCallback action) { + + T result = execute(action); + + if (result != null) { + return result; + } + + throw new IllegalStateException(String.format("KeyValueCallback %s returned null value!", action)); + } + /* * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueOperations#find(org.springframework.data.keyvalue.core.query.KeyValueQuery, java.lang.Class) @@ -355,12 +371,9 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub @Override public Iterable find(KeyValueQuery query, Class type) { - return execute((KeyValueCallback>) adapter -> { + return executeRequired((KeyValueCallback>) adapter -> { Iterable result = adapter.find(query, resolveKeySpace(type), type); - if (result == null) { - return Collections.emptySet(); - } List filtered = new ArrayList<>(); @@ -410,8 +423,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub */ @Override public long count(KeyValueQuery query, Class type) { - - return execute(adapter -> adapter.count(query, resolveKeySpace(type))); + return executeRequired(adapter -> adapter.count(query, resolveKeySpace(type))); } /* @@ -460,7 +472,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub } } - private static boolean typeCheck(Class requiredType, Object candidate) { + private static boolean typeCheck(Class requiredType, @Nullable Object candidate) { return candidate == null || ClassUtils.isAssignable(requiredType, candidate.getClass()); } } diff --git a/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java b/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java index 0477608..b1d212b 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java +++ b/src/main/java/org/springframework/data/keyvalue/core/QueryEngine.java @@ -16,28 +16,31 @@ package org.springframework.data.keyvalue.core; import java.util.Collection; +import java.util.Optional; import org.springframework.data.keyvalue.core.query.KeyValueQuery; +import org.springframework.lang.Nullable; /** * Base implementation for accessing and executing {@link KeyValueQuery} against a {@link KeyValueAdapter}. * * @author Christoph Strobl + * @author Mark Paluch * @param * @param * @param */ public abstract class QueryEngine { - private final CriteriaAccessor criteriaAccessor; - private final SortAccessor sortAccessor; + private final Optional> criteriaAccessor; + private final Optional> sortAccessor; - private ADAPTER adapter; + private @Nullable ADAPTER adapter; - public QueryEngine(CriteriaAccessor criteriaAccessor, SortAccessor sortAccessor) { + public QueryEngine(@Nullable CriteriaAccessor criteriaAccessor, @Nullable SortAccessor sortAccessor) { - this.criteriaAccessor = criteriaAccessor; - this.sortAccessor = sortAccessor; + this.criteriaAccessor = Optional.ofNullable(criteriaAccessor); + this.sortAccessor = Optional.ofNullable(sortAccessor); } /** @@ -49,8 +52,8 @@ public abstract class QueryEngine execute(KeyValueQuery query, String keyspace) { - CRITERIA criteria = this.criteriaAccessor != null ? this.criteriaAccessor.resolve(query) : null; - SORT sort = this.sortAccessor != null ? this.sortAccessor.resolve(query) : null; + CRITERIA criteria = this.criteriaAccessor.map(it -> it.resolve(query)).orElse(null); + SORT sort = this.sortAccessor.map(it -> it.resolve(query)).orElse(null); return execute(criteria, sort, query.getOffset(), query.getRows(), keyspace); } @@ -64,8 +67,8 @@ public abstract class QueryEngine Collection execute(KeyValueQuery query, String keyspace, Class type) { - CRITERIA criteria = this.criteriaAccessor != null ? this.criteriaAccessor.resolve(query) : null; - SORT sort = this.sortAccessor != null ? this.sortAccessor.resolve(query) : null; + CRITERIA criteria = this.criteriaAccessor.map(it -> it.resolve(query)).orElse(null); + SORT sort = this.sortAccessor.map(it -> it.resolve(query)).orElse(null); return execute(criteria, sort, query.getOffset(), query.getRows(), keyspace, type); } @@ -79,7 +82,7 @@ public abstract class QueryEngine query, String keyspace) { - CRITERIA criteria = this.criteriaAccessor != null ? this.criteriaAccessor.resolve(query) : null; + CRITERIA criteria = this.criteriaAccessor.map(it -> it.resolve(query)).orElse(null); return count(criteria, keyspace); } @@ -91,7 +94,8 @@ public abstract class QueryEngine execute(CRITERIA criteria, SORT sort, long offset, int rows, String keyspace); + public abstract Collection execute(@Nullable CRITERIA criteria, @Nullable SORT sort, long offset, int rows, + String keyspace); /** * @param criteria @@ -104,8 +108,8 @@ public abstract class QueryEngine Collection execute(CRITERIA criteria, SORT sort, long offset, int rows, String keyspace, - Class type) { + public Collection execute(@Nullable CRITERIA criteria, @Nullable SORT sort, long offset, int rows, + String keyspace, Class type) { return (Collection) execute(criteria, sort, offset, rows, keyspace); } @@ -114,17 +118,35 @@ public abstract class QueryEngine */ public interface SortAccessor { @@ -31,7 +32,7 @@ public interface SortAccessor { * Reads {@link KeyValueQuery#getSort()} of given {@link KeyValueQuery} and applies required transformation to match * the desired type. * - * @param query can be {@literal null}. + * @param query must not be {@literal null}. * @return {@literal null} in case {@link Sort} has not been defined on {@link KeyValueQuery}. */ T resolve(KeyValueQuery query); diff --git a/src/main/java/org/springframework/data/keyvalue/core/SpelPropertyComparator.java b/src/main/java/org/springframework/data/keyvalue/core/SpelPropertyComparator.java index f5fe2ac..2d6c258 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/SpelPropertyComparator.java +++ b/src/main/java/org/springframework/data/keyvalue/core/SpelPropertyComparator.java @@ -19,12 +19,15 @@ import java.util.Comparator; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * {@link Comparator} implementation using {@link SpelExpression}. * * @author Christoph Strobl * @author Oliver Gierke + * @author Mark Paluch * @param */ public class SpelPropertyComparator implements Comparator { @@ -34,16 +37,19 @@ public class SpelPropertyComparator implements Comparator { private boolean asc = true; private boolean nullsFirst = true; - private SpelExpression expression; + private @Nullable SpelExpression expression; /** - * Create new {@link SpelPropertyComparator} for the given property path an {@link SpelExpressionParser}.. + * 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) { + Assert.hasText(path, "Path must not be null or empty!"); + Assert.notNull(parser, "SpelExpressionParser must not be null!"); + this.path = path; this.parser = parser; } @@ -109,9 +115,9 @@ public class SpelPropertyComparator implements Comparator { */ protected String buildExpressionForPath() { - String rawExpression = "new org.springframework.util.comparator.NullSafeComparator(new org.springframework.util.comparator.ComparableComparator(), " - + Boolean.toString(this.nullsFirst) + ").compare(" + "#arg1?." + (path != null ? path.replace(".", "?.") : "") - + "," + "#arg2?." + (path != null ? path.replace(".", "?.") : "") + ")"; + String rawExpression = String.format( + "new org.springframework.util.comparator.NullSafeComparator(new org.springframework.util.comparator.ComparableComparator(), %s).compare(#arg1?.%s,#arg2?.%s)", + Boolean.toString(this.nullsFirst), path.replace(".", "?."), path.replace(".", "?.")); return rawExpression; } @@ -120,6 +126,7 @@ public class SpelPropertyComparator implements Comparator { * (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ + @SuppressWarnings("ConstantConditions") @Override public int compare(T arg1, T arg2) { diff --git a/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java b/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java index d23c3d2..316057c 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java +++ b/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java @@ -25,6 +25,7 @@ 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; +import org.springframework.lang.Nullable; /** * {@link QueryEngine} implementation specific for executing {@link SpelExpression} based {@link KeyValueQuery} against @@ -52,7 +53,7 @@ class SpelQueryEngine extends QueryEngine execute(SpelCriteria criteria, Comparator sort, long offset, int rows, String keyspace) { - return sortAndFilterMatchingRange(getAdapter().getAllOf(keyspace), criteria, sort, offset, rows); + return sortAndFilterMatchingRange(getRequiredAdapter().getAllOf(keyspace), criteria, sort, offset, rows); } /* @@ -61,12 +62,13 @@ class SpelQueryEngine extends QueryEngine sortAndFilterMatchingRange(Iterable source, SpelCriteria criteria, Comparator sort, long offset, - int rows) { + private List sortAndFilterMatchingRange(Iterable source, SpelCriteria criteria, @Nullable Comparator sort, + long offset, int rows) { List tmp = IterableConverter.toList(source); if (sort != null) { @@ -76,7 +78,8 @@ class SpelQueryEngine extends QueryEngine List filterMatchingRange(List source, SpelCriteria criteria, long offset, int rows) { + private static List filterMatchingRange(List source, @Nullable SpelCriteria criteria, long offset, + int rows) { Stream stream = source.stream(); @@ -93,6 +96,7 @@ class SpelQueryEngine extends QueryEngine> { @Override public Comparator resolve(KeyValueQuery query) { - if (query == null || query.getSort() == null || query.getSort().isUnsorted()) { + if (query.getSort().isUnsorted()) { return null; } diff --git a/src/main/java/org/springframework/data/keyvalue/core/UncategorizedKeyValueException.java b/src/main/java/org/springframework/data/keyvalue/core/UncategorizedKeyValueException.java index 07f371f..05f63e7 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/UncategorizedKeyValueException.java +++ b/src/main/java/org/springframework/data/keyvalue/core/UncategorizedKeyValueException.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,22 @@ package org.springframework.data.keyvalue.core; import org.springframework.dao.UncategorizedDataAccessException; /** + * Normal superclass when we can't distinguish anything more specific than "something went wrong with the underlying + * resource". + * * @author Christoph Strobl + * @author Mark Paluch */ public class UncategorizedKeyValueException extends UncategorizedDataAccessException { private static final long serialVersionUID = -8087116071859122297L; + /** + * Creates a new {@link UncategorizedKeyValueException}. + * + * @param msg the detail message. + * @param cause the root cause (usually from using a underlying data access API). + */ public UncategorizedKeyValueException(String msg, Throwable cause) { super(msg, cause); } diff --git a/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java b/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java index 4377214..236116e 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java +++ b/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java @@ -16,6 +16,7 @@ package org.springframework.data.keyvalue.core.event; import org.springframework.context.ApplicationEvent; +import org.springframework.lang.Nullable; /** * {@link KeyValueEvent} gets published for operations executed by eg. @@ -24,6 +25,7 @@ import org.springframework.context.ApplicationEvent; * * @author Christoph Strobl * @author Thomas Darimont + * @author Mark Paluch * @param */ public class KeyValueEvent extends ApplicationEvent { @@ -71,7 +73,7 @@ public class KeyValueEvent extends ApplicationEvent { * @param value * @return */ - public static AfterGetEvent afterGet(Object id, String keyspace, Class type, T value) { + public static AfterGetEvent afterGet(Object id, String keyspace, Class type, @Nullable T value) { return new AfterGetEvent<>(id, keyspace, type, value); } @@ -125,7 +127,7 @@ public class KeyValueEvent extends ApplicationEvent { * @return */ public static AfterUpdateEvent afterUpdate(Object id, String keyspace, Class type, T actualValue, - Object previousValue) { + @Nullable Object previousValue) { return new AfterUpdateEvent<>(id, keyspace, type, actualValue, previousValue); } @@ -172,7 +174,8 @@ public class KeyValueEvent extends ApplicationEvent { * @param value * @return */ - public static AfterDeleteEvent afterDelete(Object id, String keyspace, Class type, T value) { + public static AfterDeleteEvent afterDelete(Object id, String keyspace, Class type, + @Nullable T value) { return new AfterDeleteEvent<>(id, keyspace, type, value); } @@ -223,9 +226,9 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") abstract static class KeyBasedEventWithPayload extends KeyBasedEvent { - private final T payload; + private final @Nullable T payload; - KeyBasedEventWithPayload(Object key, String keyspace, Class type, T payload) { + KeyBasedEventWithPayload(Object key, String keyspace, Class type, @Nullable T payload) { super(key, keyspace, type); this.payload = payload; } @@ -235,6 +238,7 @@ public class KeyValueEvent extends ApplicationEvent { * * @return */ + @Nullable public T getPayload() { return payload; } @@ -264,7 +268,7 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") public static class AfterGetEvent extends KeyBasedEventWithPayload { - protected AfterGetEvent(Object key, String keyspace, Class type, T payload) { + protected AfterGetEvent(Object key, String keyspace, Class type, @Nullable T payload) { super(key, keyspace, type, payload); } @@ -279,7 +283,7 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") public static class BeforeInsertEvent extends KeyBasedEventWithPayload { - public BeforeInsertEvent(Object key, String keyspace, Class type, T payload) { + public BeforeInsertEvent(Object key, String keyspace, Class type, @Nullable T payload) { super(key, keyspace, type, payload); } @@ -294,7 +298,7 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") public static class AfterInsertEvent extends KeyBasedEventWithPayload { - public AfterInsertEvent(Object key, String keyspace, Class type, T payload) { + public AfterInsertEvent(Object key, String keyspace, Class type, @Nullable T payload) { super(key, keyspace, type, payload); } } @@ -308,7 +312,7 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") public static class BeforeUpdateEvent extends KeyBasedEventWithPayload { - public BeforeUpdateEvent(Object key, String keyspace, Class type, T payload) { + public BeforeUpdateEvent(Object key, String keyspace, Class type, @Nullable T payload) { super(key, keyspace, type, payload); } } @@ -322,9 +326,10 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") public static class AfterUpdateEvent extends KeyBasedEventWithPayload { - private final Object existing; + private final @Nullable Object existing; - public AfterUpdateEvent(Object key, String keyspace, Class type, T payload, Object existing) { + public AfterUpdateEvent(Object key, String keyspace, Class type, T payload, + @Nullable Object existing) { super(key, keyspace, type, payload); this.existing = existing; } @@ -334,6 +339,7 @@ public class KeyValueEvent extends ApplicationEvent { * * @return */ + @Nullable public Object before() { return existing; } @@ -371,7 +377,7 @@ public class KeyValueEvent extends ApplicationEvent { @SuppressWarnings("serial") public static class AfterDeleteEvent extends KeyBasedEventWithPayload { - public AfterDeleteEvent(Object key, String keyspace, Class type, T payload) { + public AfterDeleteEvent(Object key, String keyspace, Class type, @Nullable T payload) { super(key, keyspace, type, payload); } } diff --git a/src/main/java/org/springframework/data/keyvalue/core/event/package-info.java b/src/main/java/org/springframework/data/keyvalue/core/event/package-info.java new file mode 100644 index 0000000..7ec69d8 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/event/package-info.java @@ -0,0 +1,6 @@ +/** + * Support classes for key-value events, like standard persistence lifecycle events. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.core.event; diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java index 6561b65..1dc9ea5 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/AnnotationBasedKeySpaceResolver.java @@ -19,6 +19,7 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.annotation.Persistent; import org.springframework.data.keyvalue.annotation.KeySpace; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -28,6 +29,7 @@ import org.springframework.util.ClassUtils; * * @author Christoph Strobl * @author Oliver Gierke + * @author Mark Paluch */ enum AnnotationBasedKeySpaceResolver implements KeySpaceResolver { @@ -38,6 +40,7 @@ enum AnnotationBasedKeySpaceResolver implements KeySpaceResolver { * @see org.springframework.data.keyvalue.core.KeySpaceResolver#resolveKeySpace(java.lang.Class) */ @Override + @Nullable public String resolveKeySpace(Class type) { Assert.notNull(type, "Type for keyspace for null!"); @@ -48,6 +51,7 @@ enum AnnotationBasedKeySpaceResolver implements KeySpaceResolver { return keySpace != null ? keySpace.toString() : null; } + @Nullable private static Object getKeySpace(Class type) { KeySpace keyspace = AnnotatedElementUtils.findMergedAnnotation(type, KeySpace.class); diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java index 5629d19..39aef43 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/BasicKeyValuePersistentEntity.java @@ -17,6 +17,7 @@ package org.springframework.data.keyvalue.core.mapping; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -24,6 +25,7 @@ import org.springframework.util.StringUtils; * * @author Christoph Strobl * @author Oliver Gierke + * @author Mark Paluch * @param */ public class BasicKeyValuePersistentEntity> @@ -37,14 +39,15 @@ public class BasicKeyValuePersistentEntity information, KeySpaceResolver fallbackKeySpaceResolver) { + public BasicKeyValuePersistentEntity(TypeInformation information, + @Nullable KeySpaceResolver fallbackKeySpaceResolver) { super(information); this.keyspace = detectKeySpace(information.getType(), fallbackKeySpaceResolver); } - private static String detectKeySpace(Class type, KeySpaceResolver fallback) { + private static String detectKeySpace(Class type, @Nullable KeySpaceResolver fallback) { String keySpace = AnnotationBasedKeySpaceResolver.INSTANCE.resolveKeySpace(type); diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/KeySpaceResolver.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/KeySpaceResolver.java index dae56ec..9f18852 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/mapping/KeySpaceResolver.java +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/KeySpaceResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2017 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. @@ -15,11 +15,14 @@ */ package org.springframework.data.keyvalue.core.mapping; +import org.springframework.lang.Nullable; + /** * {@link KeySpaceResolver} determines the {@literal keyspace} a given type is assigned to. A keyspace in this context * is a specific region/collection/grouping of elements sharing a common keyrange.
* * @author Christoph Strobl + * @author Mark Paluch */ public interface KeySpaceResolver { @@ -29,5 +32,6 @@ public interface KeySpaceResolver { * @param type must not be {@literal null}. * @return */ + @Nullable String resolveKeySpace(Class type); } diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java index 1eda7f3..f076c23 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/context/KeyValueMappingContext.java @@ -24,6 +24,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; /** * Default implementation of a {@link MappingContext} using {@link KeyValuePersistentEntity} and @@ -36,7 +37,7 @@ import org.springframework.data.util.TypeInformation; public class KeyValueMappingContext, P extends KeyValuePersistentProperty

> extends AbstractMappingContext { - private KeySpaceResolver fallbackKeySpaceResolver; + private @Nullable KeySpaceResolver fallbackKeySpaceResolver; /** * Configures the {@link KeySpaceResolver} to be used if not explicit key space is annotated to the domain type. diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/context/package-info.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/context/package-info.java new file mode 100644 index 0000000..fa07bdc --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/context/package-info.java @@ -0,0 +1,6 @@ +/** + * Infrastructure for the Key-Value mapping context. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.core.mapping.context; diff --git a/src/main/java/org/springframework/data/keyvalue/core/mapping/package-info.java b/src/main/java/org/springframework/data/keyvalue/core/mapping/package-info.java new file mode 100644 index 0000000..aa504fc --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/mapping/package-info.java @@ -0,0 +1,6 @@ +/** + * Infrastructure for the Key-Value mapping subsystem and keyspace resolution. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.core.mapping; diff --git a/src/main/java/org/springframework/data/keyvalue/core/package-info.java b/src/main/java/org/springframework/data/keyvalue/core/package-info.java new file mode 100644 index 0000000..e246194 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/package-info.java @@ -0,0 +1,6 @@ +/** + * Core key/value implementation. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.core; diff --git a/src/main/java/org/springframework/data/keyvalue/core/query/KeyValueQuery.java b/src/main/java/org/springframework/data/keyvalue/core/query/KeyValueQuery.java index 5a974e0..d893b21 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/query/KeyValueQuery.java +++ b/src/main/java/org/springframework/data/keyvalue/core/query/KeyValueQuery.java @@ -16,6 +16,8 @@ package org.springframework.data.keyvalue.core.query; import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * @author Christoph Strobl @@ -27,7 +29,7 @@ public class KeyValueQuery { private Sort sort = Sort.unsorted(); private long offset = -1; private int rows = -1; - private T criteria; + private @Nullable T criteria; /** * Creates new instance of {@link KeyValueQuery}. @@ -39,17 +41,17 @@ public class KeyValueQuery { * * @param criteria can be {@literal null}. */ - public KeyValueQuery(T criteria) { + public KeyValueQuery(@Nullable T criteria) { this.criteria = criteria; } /** * Creates new instance of {@link KeyValueQuery} with given {@link Sort}. * - * @param sort can be {@literal null}. + * @param sort must not be {@literal null}. */ public KeyValueQuery(Sort sort) { - this.sort = sort; + setSort(sort); } /** @@ -58,6 +60,7 @@ public class KeyValueQuery { * @return * @since 2.0 */ + @Nullable public T getCriteria() { return criteria; } @@ -113,26 +116,28 @@ public class KeyValueQuery { * @param sort */ public void setSort(Sort sort) { + + Assert.notNull(sort, "Sort must not be null!"); + this.sort = sort; } /** * Add given {@link Sort}. * - * @param sort {@literal null} {@link Sort} will be ignored. + * @param sort must not be {@literal null}. * @return */ public KeyValueQuery orderBy(Sort sort) { - if (sort == null) { - return this; - } + Assert.notNull(sort, "Sort must not be null!"); - if (this.sort != null) { + if (this.sort.isSorted()) { this.sort = this.sort.and(sort); } else { this.sort = sort; } + return this; } @@ -142,7 +147,9 @@ public class KeyValueQuery { * @return */ public KeyValueQuery skip(long offset) { + setOffset(offset); + return this; } diff --git a/src/main/java/org/springframework/data/keyvalue/core/query/package-info.java b/src/main/java/org/springframework/data/keyvalue/core/query/package-info.java new file mode 100644 index 0000000..b47343b --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/query/package-info.java @@ -0,0 +1,6 @@ +/** + * Key/value specific query and abstractions. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.core.query; diff --git a/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java index d1658a4..99088f2 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java @@ -35,6 +35,7 @@ import org.springframework.data.repository.config.AnnotationRepositoryConfigurat import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.RepositoryConfigurationSource; +import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; /** @@ -42,6 +43,7 @@ import org.springframework.util.CollectionUtils; * * @author Christoph Strobl * @author Oliver Gierke + * @author Mark Paluch */ public abstract class KeyValueRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { @@ -177,6 +179,7 @@ public abstract class KeyValueRepositoryConfigurationExtension extends Repositor * * @return {@literal null} to explicitly not register a template. */ + @Nullable protected AbstractBeanDefinition getDefaultKeyValueTemplateBeanDefinition( RepositoryConfigurationSource configurationSource) { return null; diff --git a/src/main/java/org/springframework/data/keyvalue/repository/config/package-info.java b/src/main/java/org/springframework/data/keyvalue/repository/config/package-info.java new file mode 100644 index 0000000..8257cd5 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/repository/config/package-info.java @@ -0,0 +1,6 @@ +/** + * Support infrastructure for the configuration of key/value specific repositories. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.repository.config; diff --git a/src/main/java/org/springframework/data/keyvalue/repository/package-info.java b/src/main/java/org/springframework/data/keyvalue/repository/package-info.java new file mode 100644 index 0000000..8212e37 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/repository/package-info.java @@ -0,0 +1,6 @@ +/** + * Key/value specific repository implementation. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.repository; diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java b/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java index 66fd098..aea9ee2 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/CachingKeyValuePartTreeQuery.java @@ -21,17 +21,19 @@ import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.Nullable; /** * {@link KeyValuePartTreeQuery} implementation deriving queries from {@link PartTree} using a predefined * {@link AbstractQueryCreator} that caches the once created query. * * @author Christoph Strobl + * @author Mark Paluch * @since 1.1 */ public class CachingKeyValuePartTreeQuery extends KeyValuePartTreeQuery { - private KeyValueQuery cachedQuery; + private @Nullable KeyValueQuery cachedQuery; public CachingKeyValuePartTreeQuery(QueryMethod queryMethod, EvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, Class> queryCreator) { diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java b/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java index dbd3504..a52f89f 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java @@ -35,6 +35,7 @@ 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.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -94,6 +95,7 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { * @param parameters * @param query */ + @Nullable @SuppressWarnings({ "unchecked", "rawtypes" }) protected Object doExecute(Object[] parameters, KeyValueQuery query) { @@ -179,8 +181,12 @@ public class KeyValuePartTreeQuery implements RepositoryQuery { PartTree tree = new PartTree(getQueryMethod().getName(), getQueryMethod().getEntityInformation().getJavaType()); - Constructor> constructor = ClassUtils - .getConstructorIfAvailable(queryCreator, PartTree.class, ParameterAccessor.class); + Constructor> constructor = ClassUtils.getConstructorIfAvailable(queryCreator, + PartTree.class, ParameterAccessor.class); + + Assert.state(constructor != null, String.format("Constructor %s(PartTree, ParameterAccessor) not available!", + ClassUtils.getShortName(queryCreator))); + KeyValueQuery query = (KeyValueQuery) BeanUtils.instantiateClass(constructor, tree, accessor).createQuery(); if (tree.isLimiting()) { diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreator.java b/src/main/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreator.java index 28bb9af..a004860 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreator.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/SpelQueryCreator.java @@ -89,7 +89,7 @@ public class SpelQueryCreator extends AbstractQueryCreator query = new KeyValueQuery<>(this.expression); - if (sort != null) { + if (sort.isSorted()) { query.orderBy(sort); } diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/package-info.java b/src/main/java/org/springframework/data/keyvalue/repository/query/package-info.java new file mode 100644 index 0000000..64ad179 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/package-info.java @@ -0,0 +1,6 @@ +/** + * Query derivation mechanism for key/value specific repositories providing a generic SpEL based implementation. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.repository.query; diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtils.java b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtils.java index 2e574e1..e1bc644 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtils.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtils.java @@ -37,6 +37,7 @@ import com.querydsl.core.types.dsl.PathBuilder; * @author Christoph Strobl * @author Thomas Darimont * @author Oliver Gierke + * @author Mark Paluch */ abstract class KeyValueQuerydslUtils { @@ -47,17 +48,14 @@ abstract class KeyValueQuerydslUtils { /** * Transforms a plain {@link Order} into a QueryDsl specific {@link OrderSpecifier}. * - * @param sort + * @param sort must not be {@literal null}. * @param builder must not be {@literal null}. * @return empty {@code OrderSpecifier[]} when sort is {@literal null}. */ static OrderSpecifier[] toOrderSpecifier(Sort sort, PathBuilder builder) { - Assert.notNull(builder, "Builder must not be 'null'."); - - if (sort == null) { - return new OrderSpecifier[0]; - } + Assert.notNull(sort, "Sort must not be null."); + Assert.notNull(builder, "Builder must not be null."); List> specifiers = null; diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java index 2df83f6..82caaf5 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactory.java @@ -15,7 +15,7 @@ */ package org.springframework.data.keyvalue.repository.support; -import static org.springframework.data.querydsl.QuerydslUtils.QUERY_DSL_PRESENT; +import static org.springframework.data.querydsl.QuerydslUtils.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -41,6 +41,7 @@ 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.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -75,7 +76,7 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { * {@link AbstractQueryCreator}-type. * * @param keyValueOperations must not be {@literal null}. - * @param queryCreator defaulted to {@link #DEFAULT_QUERY_CREATOR} if {@literal null}. + * @param queryCreator must not be {@literal null}. */ public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations, Class> queryCreator) { @@ -154,9 +155,10 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider) */ @Override - protected Optional getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) { - return Optional.of(new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, this.queryCreator, - this.repositoryQueryType)); + protected Optional getQueryLookupStrategy(@Nullable Key key, + EvaluationContextProvider evaluationContextProvider) { + return Optional.of(new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, + this.queryCreator, this.repositoryQueryType)); } /** @@ -194,7 +196,7 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { * @param queryCreator * @since 1.1 */ - public KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider, + public KeyValueQueryLookupStrategy(@Nullable Key key, EvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, Class> queryCreator, Class repositoryQueryType) { @@ -224,6 +226,11 @@ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { .getConstructorIfAvailable(this.repositoryQueryType, QueryMethod.class, EvaluationContextProvider.class, KeyValueOperations.class, Class.class); + Assert.state(constructor != null, + String.format( + "Constructor %s(QueryMethod, EvaluationContextProvider, KeyValueOperations, Class) not available!", + ClassUtils.getShortName(this.repositoryQueryType))); + return BeanUtils.instantiateClass(constructor, queryMethod, evaluationContextProvider, this.keyValueOperations, this.queryCreator); } diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java index 705dc03..231e1ba 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/KeyValueRepositoryFactoryBean.java @@ -24,6 +24,7 @@ import org.springframework.data.repository.core.support.RepositoryFactoryBeanSup import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.AbstractQueryCreator; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -36,9 +37,9 @@ import org.springframework.util.Assert; public class KeyValueRepositoryFactoryBean, S, ID> extends RepositoryFactoryBeanSupport { - private KeyValueOperations operations; - private Class> queryCreator; - private Class repositoryQueryType; + private @Nullable KeyValueOperations operations; + private @Nullable Class> queryCreator; + private @Nullable Class repositoryQueryType; /** * Creates a new {@link KeyValueRepositoryFactoryBean} for the given repository interface. diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/QuerydslKeyValueRepository.java b/src/main/java/org/springframework/data/keyvalue/repository/support/QuerydslKeyValueRepository.java index 84b242a..989c790 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/QuerydslKeyValueRepository.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/QuerydslKeyValueRepository.java @@ -30,6 +30,7 @@ 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.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -47,6 +48,7 @@ import com.querydsl.core.types.dsl.PathBuilder; * @author Christoph Strobl * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch * @param the domain type to manage * @param the identifier type of the domain type */ @@ -55,7 +57,6 @@ public class QuerydslKeyValueRepository extends SimpleKeyValueRepository< private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE; - private final EntityPath path; private final PathBuilder builder; /** @@ -84,7 +85,7 @@ public class QuerydslKeyValueRepository extends SimpleKeyValueRepository< Assert.notNull(resolver, "EntityPathResolver must not be null!"); - this.path = resolver.createPath(entityInformation.getJavaType()); + EntityPath path = resolver.createPath(entityInformation.getJavaType()); this.builder = new PathBuilder<>(path.getType(), path.getMetadata()); } @@ -142,12 +143,12 @@ public class QuerydslKeyValueRepository extends SimpleKeyValueRepository< AbstractCollQuery query = prepareQuery(predicate); - if (pageable != null) { + if (pageable.isPaged() || pageable.getSort().isSorted()) { query.offset(pageable.getOffset()); query.limit(pageable.getPageSize()); - if (pageable.getSort() != null) { + if (pageable.getSort().isSorted()) { query.orderBy(toOrderSpecifier(pageable.getSort(), builder)); } } @@ -196,7 +197,7 @@ public class QuerydslKeyValueRepository extends SimpleKeyValueRepository< * @param predicate * @return */ - protected AbstractCollQuery prepareQuery(Predicate predicate) { + protected AbstractCollQuery prepareQuery(@Nullable Predicate predicate) { CollQuery query = new CollQuery<>(); query.from(builder, findAll()); diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/package-info.java b/src/main/java/org/springframework/data/keyvalue/repository/support/package-info.java new file mode 100644 index 0000000..fd58c5a --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/package-info.java @@ -0,0 +1,6 @@ +/** + * Support infrastructure for query derivation of key/value specific repositories. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.keyvalue.repository.support; diff --git a/src/main/java/org/springframework/data/map/package-info.java b/src/main/java/org/springframework/data/map/package-info.java new file mode 100644 index 0000000..d184db6 --- /dev/null +++ b/src/main/java/org/springframework/data/map/package-info.java @@ -0,0 +1,6 @@ +/** + * Repository implementation backed by generic {@link java.util.Map} instances. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.map; diff --git a/src/main/java/org/springframework/data/map/repository/config/package-info.java b/src/main/java/org/springframework/data/map/repository/config/package-info.java new file mode 100644 index 0000000..346ace9 --- /dev/null +++ b/src/main/java/org/springframework/data/map/repository/config/package-info.java @@ -0,0 +1,6 @@ +/** + * Support infrastructure for the configuration of {@link java.util.Map} repositories. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.map.repository.config; diff --git a/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java index 2f9ebd7..0c5567f 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java @@ -30,14 +30,10 @@ import org.junit.Test; /** * @author Christoph Strobl + * @author Mark Paluch */ public class IterableConverterUnitTests { - @Test // DATAKV-101 - public void toListShouldReturnEmptyListWhenSourceIsNull() { - assertThat(toList(null), notNullValue()); - } - @Test // DATAKV-101 public void toListShouldReturnEmptyListWhenSourceEmpty() { assertThat(toList(Collections.emptySet()), empty()); diff --git a/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtilsUnitTests.java b/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtilsUnitTests.java index 647abc1..8ca8249 100644 --- a/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/repository/support/KeyValueQuerydslUtilsUnitTests.java @@ -39,6 +39,7 @@ import com.querydsl.core.types.dsl.PathBuilder; * @author Christoph Strobl * @author Thomas Darimont * @author Oliver Gierke + * @author Mark Paluch */ public class KeyValueQuerydslUtilsUnitTests { @@ -57,9 +58,9 @@ public class KeyValueQuerydslUtilsUnitTests { toOrderSpecifier(Sort.by("firstname"), null); } - @Test // DATACMNS-525 - public void toOrderSpecifierReturnsEmptyArrayWhenSortIsNull() { - assertThat(toOrderSpecifier(null, builder), arrayWithSize(0)); + @Test // DATACMNS-525, DATAKV-197 + public void toOrderSpecifierReturnsEmptyArrayWhenSortIsUnsorted() { + assertThat(toOrderSpecifier(Sort.unsorted(), builder), arrayWithSize(0)); } @Test // DATACMNS-525 diff --git a/src/test/java/org/springframework/data/map/QuerydslKeyValueRepositoryUnitTests.java b/src/test/java/org/springframework/data/map/QuerydslKeyValueRepositoryUnitTests.java index d7e9c79..9dd2497 100644 --- a/src/test/java/org/springframework/data/map/QuerydslKeyValueRepositoryUnitTests.java +++ b/src/test/java/org/springframework/data/map/QuerydslKeyValueRepositoryUnitTests.java @@ -45,6 +45,7 @@ import com.google.common.collect.Lists; * @author Christoph Strobl * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitTests { @@ -76,8 +77,7 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT assertThat(page1.getContent(), hasSize(1)); assertThat(page1.hasNext(), is(true)); - Page page2 = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()), - page1.nextPageable()); + Page page2 = repository.findAll(QPerson.person.age.eq(CERSEI.getAge()), page1.nextPageable()); assertThat(page2.getTotalElements(), is(2L)); assertThat(page2.getContent(), hasSize(1)); @@ -127,12 +127,17 @@ public class QuerydslKeyValueRepositoryUnitTests extends AbstractRepositoryUnitT assertThat(result, contains(TYRION, JAIME, CERSEI)); } - @Test // DATAKV-90 - public void findAllShouldIgnoreNullOrderSpecifier() { + @Test(expected = IllegalArgumentException.class) // DATAKV-90, DATAKV-197 + public void findAllShouldRequireSort() { + repository.findAll((QSort) null); + } + + @Test // DATAKV-90, DATAKV-197 + public void findAllShouldAllowUnsortedFindAll() { repository.saveAll(LENNISTERS); - Iterable result = repository.findAll((QSort) null); + Iterable result = repository.findAll(Sort.unsorted()); assertThat(result, containsInAnyOrder(TYRION, JAIME, CERSEI)); }