DATAKV-197 - Introduce usage of nullable annotations for API validation.

Annotate all packages with Spring Frameworks @NonNullApi and @NonNullFields. Add Spring's @Nullable to methods, parameters and fields that take or produce null values. Adapted using code to make sure the IDE can evaluate the null flow properly. Fix Javadoc in places where an invalid null handling policy was advertised. Strengthened null requirements for types that expose null-instances.

Original Pull Request: #28
This commit is contained in:
Mark Paluch
2017-09-22 16:53:31 +02:00
committed by Christoph Strobl
parent f1935f73b7
commit 83057f1288
44 changed files with 330 additions and 130 deletions

View File

@@ -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;

View File

@@ -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<? extends KeyValueAdapter, ?, ?> engine) {
protected AbstractKeyValueAdapter(@Nullable QueryEngine<? extends KeyValueAdapter, ?, ?> 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> T get(Object id, String keyspace, Class<T> 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> T delete(Object id, String keyspace, Class<T> type) {
return type.cast(delete(id, keyspace));

View File

@@ -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 <T>
*/
public interface CriteriaAccessor<T> {
@@ -29,7 +30,7 @@ public interface CriteriaAccessor<T> {
* 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}.

View File

@@ -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 <K>
* @param <V>
*/
public class ForwardingCloseableIterator<T> implements CloseableIterator<T> {
private final Iterator<? extends T> delegate;
private final Runnable closeHandler;
private final @Nullable Runnable closeHandler;
/**
* Creates a new {@link ForwardingCloseableIterator}.
@@ -50,7 +52,7 @@ public class ForwardingCloseableIterator<T> implements CloseableIterator<T> {
* @param delegate must not be {@literal null}.
* @param closeHandler may be {@literal null}.
*/
public ForwardingCloseableIterator(Iterator<? extends T> delegate, Runnable closeHandler) {
public ForwardingCloseableIterator(Iterator<? extends T> delegate, @Nullable Runnable closeHandler) {
Assert.notNull(delegate, "Delegate iterator must not be null!");

View File

@@ -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 <T> List<T> toList(Iterable<T> source) {
if (source == null) {
return Collections.emptyList();
}
if (source instanceof List) {
return (List<T>) source;
}

View File

@@ -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> T get(Object id, String keyspace, Class<T> 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> T delete(Object id, String keyspace, Class<T> 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<Map.Entry<Object, Object>> 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
*/
<T> Iterable<T> find(KeyValueQuery<?> query, String keyspace, Class<T> 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
*/

View File

@@ -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 <T>
*/
public interface KeyValueCallback<T> {
@@ -31,5 +34,6 @@ public interface KeyValueCallback<T> {
* @param adapter
* @return
*/
@Nullable
T doInKeyValue(KeyValueAdapter adapter);
}

View File

@@ -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> T execute(KeyValueCallback<T> action);
/**
@@ -139,6 +142,7 @@ public interface KeyValueOperations extends DisposableBean {
* @param objectToDelete must not be {@literal null}.
* @return
*/
@Nullable
<T> 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> T delete(Object id, Class<T> type);
/**

View File

@@ -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;
}
}

View File

@@ -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<Class<? extends KeyValueEvent>> 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<T> 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> T execute(KeyValueCallback<T> 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 <T>
* @return
*/
protected <T> T executeRequired(KeyValueCallback<T> 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 <T> Iterable<T> find(KeyValueQuery<?> query, Class<T> type) {
return execute((KeyValueCallback<Iterable<T>>) adapter -> {
return executeRequired((KeyValueCallback<Iterable<T>>) adapter -> {
Iterable<?> result = adapter.find(query, resolveKeySpace(type), type);
if (result == null) {
return Collections.emptySet();
}
List<T> 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());
}
}

View File

@@ -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 <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 final Optional<CriteriaAccessor<CRITERIA>> criteriaAccessor;
private final Optional<SortAccessor<SORT>> sortAccessor;
private ADAPTER adapter;
private @Nullable ADAPTER adapter;
public QueryEngine(CriteriaAccessor<CRITERIA> criteriaAccessor, SortAccessor<SORT> sortAccessor) {
public QueryEngine(@Nullable CriteriaAccessor<CRITERIA> criteriaAccessor, @Nullable SortAccessor<SORT> 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<ADAPTER extends KeyValueAdapter, CRITERIA, SOR
*/
public Collection<?> 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<ADAPTER extends KeyValueAdapter, CRITERIA, SOR
*/
public <T> Collection<T> execute(KeyValueQuery<?> query, String keyspace, Class<T> 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<ADAPTER extends KeyValueAdapter, CRITERIA, SOR
*/
public long count(KeyValueQuery<?> 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<ADAPTER extends KeyValueAdapter, CRITERIA, SOR
* @param keyspace
* @return
*/
public abstract Collection<?> 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<ADAPTER extends KeyValueAdapter, CRITERIA, SOR
* @since 1.1
*/
@SuppressWarnings("unchecked")
public <T> Collection<T> execute(CRITERIA criteria, SORT sort, long offset, int rows, String keyspace,
Class<T> type) {
public <T> Collection<T> execute(@Nullable CRITERIA criteria, @Nullable SORT sort, long offset, int rows,
String keyspace, Class<T> type) {
return (Collection<T>) execute(criteria, sort, offset, rows, keyspace);
}
@@ -114,17 +118,35 @@ public abstract class QueryEngine<ADAPTER extends KeyValueAdapter, CRITERIA, SOR
* @param keyspace
* @return
*/
public abstract long count(CRITERIA criteria, String keyspace);
public abstract long count(@Nullable CRITERIA criteria, String keyspace);
/**
* Get the {@link KeyValueAdapter} used.
*
* @return
*/
@Nullable
protected ADAPTER getAdapter() {
return this.adapter;
}
/**
* Get the required {@link KeyValueAdapter} used or throw {@link IllegalStateException} if the adapter is not set.
*
* @return the required {@link KeyValueAdapter}.
* @throws IllegalStateException if the adapter is not set.
*/
protected ADAPTER getRequiredAdapter() {
ADAPTER adapter = getAdapter();
if (adapter != null) {
return adapter;
}
throw new IllegalStateException("Required KeyValueAdapter is not set!");
}
/**
* @param adapter
*/

View File

@@ -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.
@@ -23,6 +23,7 @@ import org.springframework.data.keyvalue.core.query.KeyValueQuery;
* representation that can be used by the {@link QueryEngine} implementation.
*
* @author Christoph Strobl
* @author Mark Paluch
* @param <T>
*/
public interface SortAccessor<T> {
@@ -31,7 +32,7 @@ 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}.
* @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);

View File

@@ -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 <T>
*/
public class SpelPropertyComparator<T> implements Comparator<T> {
@@ -34,16 +37,19 @@ public class SpelPropertyComparator<T> implements Comparator<T> {
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<T> implements Comparator<T> {
*/
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<T> implements Comparator<T> {
* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@SuppressWarnings("ConstantConditions")
@Override
public int compare(T arg1, T arg2) {

View File

@@ -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<KeyValueAdapter, SpelCriteria, Compara
*/
@Override
public Collection<?> 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<KeyValueAdapter, SpelCriteria, Compara
*/
@Override
public long count(SpelCriteria criteria, String keyspace) {
return filterMatchingRange(IterableConverter.toList(getAdapter().getAllOf(keyspace)), criteria, -1, -1).size();
return filterMatchingRange(IterableConverter.toList(getRequiredAdapter().getAllOf(keyspace)), criteria, -1, -1)
.size();
}
@SuppressWarnings("unchecked")
private List<?> 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<KeyValueAdapter, SpelCriteria, Compara
return filterMatchingRange(tmp, criteria, offset, rows);
}
private static <S> List<S> filterMatchingRange(List<S> source, SpelCriteria criteria, long offset, int rows) {
private static <S> List<S> filterMatchingRange(List<S> source, @Nullable SpelCriteria criteria, long offset,
int rows) {
Stream<S> stream = source.stream();
@@ -93,6 +96,7 @@ class SpelQueryEngine extends QueryEngine<KeyValueAdapter, SpelCriteria, Compara
return stream.collect(Collectors.toList());
}
@SuppressWarnings("ConstantConditions")
private static boolean evaluateExpression(SpelCriteria criteria, Object candidate) {
try {

View File

@@ -53,7 +53,7 @@ class SpelSortAccessor implements SortAccessor<Comparator<?>> {
@Override
public Comparator<?> resolve(KeyValueQuery<?> query) {
if (query == null || query.getSort() == null || query.getSort().isUnsorted()) {
if (query.getSort().isUnsorted()) {
return null;
}

View File

@@ -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);
}

View File

@@ -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 <T>
*/
public class KeyValueEvent<T> extends ApplicationEvent {
@@ -71,7 +73,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
* @param value
* @return
*/
public static <T> AfterGetEvent<T> afterGet(Object id, String keyspace, Class<T> type, T value) {
public static <T> AfterGetEvent<T> afterGet(Object id, String keyspace, Class<T> type, @Nullable T value) {
return new AfterGetEvent<>(id, keyspace, type, value);
}
@@ -125,7 +127,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
* @return
*/
public static <T> AfterUpdateEvent<T> afterUpdate(Object id, String keyspace, Class<? extends T> type, T actualValue,
Object previousValue) {
@Nullable Object previousValue) {
return new AfterUpdateEvent<>(id, keyspace, type, actualValue, previousValue);
}
@@ -172,7 +174,8 @@ public class KeyValueEvent<T> extends ApplicationEvent {
* @param value
* @return
*/
public static <T> AfterDeleteEvent<T> afterDelete(Object id, String keyspace, Class<? extends T> type, T value) {
public static <T> AfterDeleteEvent<T> afterDelete(Object id, String keyspace, Class<? extends T> type,
@Nullable T value) {
return new AfterDeleteEvent<>(id, keyspace, type, value);
}
@@ -223,9 +226,9 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
abstract static class KeyBasedEventWithPayload<T> extends KeyBasedEvent<T> {
private final T payload;
private final @Nullable T payload;
KeyBasedEventWithPayload(Object key, String keyspace, Class<? extends T> type, T payload) {
KeyBasedEventWithPayload(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {
super(key, keyspace, type);
this.payload = payload;
}
@@ -235,6 +238,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
*
* @return
*/
@Nullable
public T getPayload() {
return payload;
}
@@ -264,7 +268,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
public static class AfterGetEvent<T> extends KeyBasedEventWithPayload<T> {
protected AfterGetEvent(Object key, String keyspace, Class<T> type, T payload) {
protected AfterGetEvent(Object key, String keyspace, Class<T> type, @Nullable T payload) {
super(key, keyspace, type, payload);
}
@@ -279,7 +283,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
public static class BeforeInsertEvent<T> extends KeyBasedEventWithPayload<T> {
public BeforeInsertEvent(Object key, String keyspace, Class<? extends T> type, T payload) {
public BeforeInsertEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {
super(key, keyspace, type, payload);
}
@@ -294,7 +298,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
public static class AfterInsertEvent<T> extends KeyBasedEventWithPayload<T> {
public AfterInsertEvent(Object key, String keyspace, Class<? extends T> type, T payload) {
public AfterInsertEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {
super(key, keyspace, type, payload);
}
}
@@ -308,7 +312,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
public static class BeforeUpdateEvent<T> extends KeyBasedEventWithPayload<T> {
public BeforeUpdateEvent(Object key, String keyspace, Class<? extends T> type, T payload) {
public BeforeUpdateEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {
super(key, keyspace, type, payload);
}
}
@@ -322,9 +326,10 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
public static class AfterUpdateEvent<T> extends KeyBasedEventWithPayload<T> {
private final Object existing;
private final @Nullable Object existing;
public AfterUpdateEvent(Object key, String keyspace, Class<? extends T> type, T payload, Object existing) {
public AfterUpdateEvent(Object key, String keyspace, Class<? extends T> type, T payload,
@Nullable Object existing) {
super(key, keyspace, type, payload);
this.existing = existing;
}
@@ -334,6 +339,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
*
* @return
*/
@Nullable
public Object before() {
return existing;
}
@@ -371,7 +377,7 @@ public class KeyValueEvent<T> extends ApplicationEvent {
@SuppressWarnings("serial")
public static class AfterDeleteEvent<T> extends KeyBasedEventWithPayload<T> {
public AfterDeleteEvent(Object key, String keyspace, Class<? extends T> type, T payload) {
public AfterDeleteEvent(Object key, String keyspace, Class<? extends T> type, @Nullable T payload) {
super(key, keyspace, type, payload);
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 <T>
*/
public class BasicKeyValuePersistentEntity<T, P extends KeyValuePersistentProperty<P>>
@@ -37,14 +39,15 @@ public class BasicKeyValuePersistentEntity<T, P extends KeyValuePersistentProper
* @param information must not be {@literal null}.
* @param keySpaceResolver can be {@literal null}.
*/
public BasicKeyValuePersistentEntity(TypeInformation<T> information, KeySpaceResolver fallbackKeySpaceResolver) {
public BasicKeyValuePersistentEntity(TypeInformation<T> 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);

View File

@@ -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. <br />
*
* @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);
}

View File

@@ -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<E extends KeyValuePersistentEntity<?, P>, P extends KeyValuePersistentProperty<P>>
extends AbstractMappingContext<E, P> {
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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,6 @@
/**
* Core key/value implementation.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.keyvalue.core;

View File

@@ -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<T> {
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<T> {
*
* @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<T> {
* @return
* @since 2.0
*/
@Nullable
public T getCriteria() {
return criteria;
}
@@ -113,26 +116,28 @@ public class KeyValueQuery<T> {
* @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<T> 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<T> {
* @return
*/
public KeyValueQuery<T> skip(long offset) {
setOffset(offset);
return this;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,6 @@
/**
* Key/value specific repository implementation.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.keyvalue.repository;

View File

@@ -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<? extends AbstractQueryCreator<?, ?>> queryCreator) {

View File

@@ -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<? extends AbstractQueryCreator<?, ?>> constructor = ClassUtils
.getConstructorIfAvailable(queryCreator, PartTree.class, ParameterAccessor.class);
Constructor<? extends AbstractQueryCreator<?, ?>> 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()) {

View File

@@ -89,7 +89,7 @@ public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExp
KeyValueQuery<SpelExpression> query = new KeyValueQuery<>(this.expression);
if (sort != null) {
if (sort.isSorted()) {
query.orderBy(sort);
}

View File

@@ -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;

View File

@@ -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<OrderSpecifier<?>> specifiers = null;

View File

@@ -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<? extends AbstractQueryCreator<?, ?>> 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<QueryLookupStrategy> getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) {
return Optional.of(new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, this.queryCreator,
this.repositoryQueryType));
protected Optional<QueryLookupStrategy> 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<? extends AbstractQueryCreator<?, ?>> queryCreator,
Class<? extends RepositoryQuery> 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);
}

View File

@@ -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<T extends Repository<S, ID>, S, ID>
extends RepositoryFactoryBeanSupport<T, S, ID> {
private KeyValueOperations operations;
private Class<? extends AbstractQueryCreator<?, ?>> queryCreator;
private Class<? extends RepositoryQuery> repositoryQueryType;
private @Nullable KeyValueOperations operations;
private @Nullable Class<? extends AbstractQueryCreator<?, ?>> queryCreator;
private @Nullable Class<? extends RepositoryQuery> repositoryQueryType;
/**
* Creates a new {@link KeyValueRepositoryFactoryBean} for the given repository interface.

View File

@@ -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 <T> the domain type to manage
* @param <ID> the identifier type of the domain type
*/
@@ -55,7 +57,6 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final PathBuilder<T> builder;
/**
@@ -84,7 +85,7 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
Assert.notNull(resolver, "EntityPathResolver must not be null!");
this.path = resolver.createPath(entityInformation.getJavaType());
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<>(path.getType(), path.getMetadata());
}
@@ -142,12 +143,12 @@ public class QuerydslKeyValueRepository<T, ID> extends SimpleKeyValueRepository<
AbstractCollQuery<T, ?> 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<T, ID> extends SimpleKeyValueRepository<
* @param predicate
* @return
*/
protected AbstractCollQuery<T, ?> prepareQuery(Predicate predicate) {
protected AbstractCollQuery<T, ?> prepareQuery(@Nullable Predicate predicate) {
CollQuery<T> query = new CollQuery<>();
query.from(builder, findAll());

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;