Polishing.
Refine documentation. Introduce isLimited method to mirror isPresent/isSorted semantics. Introduce Pageable.toLimit() method to deduplicate code. See #2827 Original pull request: #2836
This commit is contained in:
@@ -28,25 +28,12 @@ import org.springframework.util.ClassUtils;
|
||||
* over using {@literal null} or {@link java.util.Optional#empty()} to indicate the absence of an actual {@link Limit}.
|
||||
* </p>
|
||||
* {@link Limit} itself does not make assumptions about the actual {@link #max()} value sign. This means that a negative
|
||||
* value may be valid within a defined context. Implementations should override {@link #isUnlimited()} to fit their
|
||||
* needs and enforce restrictions if needed.
|
||||
*
|
||||
* value may be valid within a defined context.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public sealed interface Limit permits Limited, Unlimited {
|
||||
|
||||
/**
|
||||
* @return the max number of potential results.
|
||||
*/
|
||||
int max();
|
||||
|
||||
/**
|
||||
* @return {@literal true} if no limiting (maximum value) should be applied.
|
||||
*/
|
||||
default boolean isUnlimited() {
|
||||
return Unlimited.INSTANCE.equals(this);
|
||||
}
|
||||
public sealed interface Limit permits Limited,Unlimited {
|
||||
|
||||
/**
|
||||
* @return a {@link Limit} instance that does not define {@link #max()} and answers {@link #isUnlimited()} with
|
||||
@@ -66,6 +53,23 @@ public sealed interface Limit permits Limited, Unlimited {
|
||||
return new Limited(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the max number of potential results.
|
||||
*/
|
||||
int max();
|
||||
|
||||
/**
|
||||
* @return {@literal true} if limiting (maximum value) should be applied.
|
||||
*/
|
||||
boolean isLimited();
|
||||
|
||||
/**
|
||||
* @return {@literal true} if no limiting (maximum value) should be applied.
|
||||
*/
|
||||
default boolean isUnlimited() {
|
||||
return !isLimited();
|
||||
}
|
||||
|
||||
final class Limited implements Limit {
|
||||
|
||||
private final int max;
|
||||
@@ -79,6 +83,11 @@ public sealed interface Limit permits Limited, Unlimited {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLimited() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
@@ -97,7 +106,7 @@ public sealed interface Limit permits Limited, Unlimited {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (max ^ (max >>> 32));
|
||||
return max ^ (max >>> 32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,12 +119,13 @@ public sealed interface Limit permits Limited, Unlimited {
|
||||
@Override
|
||||
public int max() {
|
||||
throw new IllegalStateException(
|
||||
"Unlimited does not define 'max'. Please check 'isUnlimited' before attempting to read 'max'");
|
||||
"Unlimited does not define 'max'. Please check 'isLimited' before attempting to read 'max'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnlimited() {
|
||||
return true;
|
||||
public boolean isLimited() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,22 @@ public interface Pageable {
|
||||
return isUnpaged() ? Optional.empty() : Optional.of(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Limit} from this pageable if the page request {@link #isPaged() is paged} or
|
||||
* {@link Limit#unlimited()} otherwise.
|
||||
*
|
||||
* @return
|
||||
* @since 3.2
|
||||
*/
|
||||
default Limit toLimit() {
|
||||
|
||||
if (isUnpaged()) {
|
||||
return Limit.unlimited();
|
||||
}
|
||||
|
||||
return Limit.of(getPageSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link OffsetScrollPosition} from this pageable if the page request {@link #isPaged() is paged}.
|
||||
*
|
||||
|
||||
@@ -60,15 +60,11 @@ public interface ParameterAccessor extends Iterable<Object> {
|
||||
* assignable to {@link Limit} can be found {@link Limit} will be created out of {@link Pageable#getPageSize()} if
|
||||
* present.
|
||||
*
|
||||
* @return {@link Limit#unlimited()} by default.
|
||||
* @return
|
||||
* @since 3.2
|
||||
*/
|
||||
default Limit getLimit() {
|
||||
|
||||
if (getPageable().isUnpaged()) {
|
||||
return Limit.unlimited();
|
||||
}
|
||||
return Limit.of(getPageable().getPageSize());
|
||||
return getPageable().toLimit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -157,7 +157,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
if (parameters.hasPageableParameter()) {
|
||||
|
||||
Pageable pageable = (Pageable) values[parameters.getPageableIndex()];
|
||||
return pageable.isPaged() ? Limit.of(pageable.getPageSize()) : Limit.unlimited();
|
||||
return pageable.toLimit();
|
||||
}
|
||||
|
||||
return Limit.unlimited();
|
||||
|
||||
@@ -103,6 +103,68 @@ public class QueryMethod {
|
||||
validate();
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
|
||||
QueryMethodValidator.validate(method);
|
||||
|
||||
if (hasParameterOfType(method, Pageable.class)) {
|
||||
|
||||
if (!isStreamQuery()) {
|
||||
assertReturnTypeAssignable(method, QueryExecutionConverters.getAllowedPageableTypes());
|
||||
}
|
||||
|
||||
if (hasParameterOfType(method, Sort.class)) {
|
||||
throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameters. "
|
||||
+ "Use sorting capabilities on Pageable instead; Offending method: %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParameterOfType(method, ScrollPosition.class)) {
|
||||
assertReturnTypeAssignable(method, Collections.singleton(Window.class));
|
||||
}
|
||||
|
||||
Assert.notNull(this.parameters,
|
||||
() -> String.format("Parameters extracted from method '%s' must not be null", method.getName()));
|
||||
|
||||
if (isPageQuery()) {
|
||||
Assert.isTrue(this.parameters.hasPageableParameter(),
|
||||
String.format("Paging query needs to have a Pageable parameter; Offending method: %s", method));
|
||||
}
|
||||
|
||||
if (isScrollQuery()) {
|
||||
|
||||
Assert.isTrue(this.parameters.hasScrollPositionParameter() || this.parameters.hasPageableParameter(),
|
||||
String.format("Scroll query needs to have a ScrollPosition parameter; Offending method: %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean calculateIsCollectionQuery() {
|
||||
|
||||
if (isPageQuery() || isSliceQuery() || isScrollQuery()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeInformation<?> returnTypeInformation = metadata.getReturnType(method);
|
||||
|
||||
// Check against simple wrapper types first
|
||||
if (metadata.getDomainTypeInformation()
|
||||
.isAssignableFrom(NullableWrapperConverters.unwrapActualType(returnTypeInformation))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?> returnType = returnTypeInformation.getType();
|
||||
|
||||
if (QueryExecutionConverters.supports(returnType) && !QueryExecutionConverters.isSingleValue(returnType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (QueryExecutionConverters.supports(unwrappedReturnType)) {
|
||||
return !QueryExecutionConverters.isSingleValue(unwrappedReturnType);
|
||||
}
|
||||
|
||||
return TypeInformation.of(unwrappedReturnType).isCollectionLike();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Parameters} instance.
|
||||
*
|
||||
@@ -265,68 +327,6 @@ public class QueryMethod {
|
||||
return method.toString();
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
|
||||
QueryMethodValidator.validate(method);
|
||||
|
||||
if (hasParameterOfType(method, Pageable.class)) {
|
||||
|
||||
if (!isStreamQuery()) {
|
||||
assertReturnTypeAssignable(method, QueryExecutionConverters.getAllowedPageableTypes());
|
||||
}
|
||||
|
||||
if (hasParameterOfType(method, Sort.class)) {
|
||||
throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameters. "
|
||||
+ "Use sorting capabilities on Pageable instead; Offending method: %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParameterOfType(method, ScrollPosition.class)) {
|
||||
assertReturnTypeAssignable(method, Collections.singleton(Window.class));
|
||||
}
|
||||
|
||||
Assert.notNull(this.parameters,
|
||||
() -> String.format("Parameters extracted from method '%s' must not be null", method.getName()));
|
||||
|
||||
if (isPageQuery()) {
|
||||
Assert.isTrue(this.parameters.hasPageableParameter(),
|
||||
String.format("Paging query needs to have a Pageable parameter; Offending method: %s", method));
|
||||
}
|
||||
|
||||
if (isScrollQuery()) {
|
||||
|
||||
Assert.isTrue(this.parameters.hasScrollPositionParameter() || this.parameters.hasPageableParameter(),
|
||||
String.format("Scroll query needs to have a ScrollPosition parameter; Offending method: %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean calculateIsCollectionQuery() {
|
||||
|
||||
if (isPageQuery() || isSliceQuery() || isScrollQuery()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeInformation<?> returnTypeInformation = metadata.getReturnType(method);
|
||||
|
||||
// Check against simple wrapper types first
|
||||
if (metadata.getDomainTypeInformation()
|
||||
.isAssignableFrom(NullableWrapperConverters.unwrapActualType(returnTypeInformation))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?> returnType = returnTypeInformation.getType();
|
||||
|
||||
if (QueryExecutionConverters.supports(returnType) && !QueryExecutionConverters.isSingleValue(returnType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (QueryExecutionConverters.supports(unwrappedReturnType)) {
|
||||
return !QueryExecutionConverters.isSingleValue(unwrappedReturnType);
|
||||
}
|
||||
|
||||
return TypeInformation.of(unwrappedReturnType).isCollectionLike();
|
||||
}
|
||||
|
||||
private static Class<? extends Object> potentiallyUnwrapReturnTypeFor(RepositoryMetadata metadata, Method method) {
|
||||
|
||||
TypeInformation<?> returnType = metadata.getReturnType(method);
|
||||
|
||||
Reference in New Issue
Block a user