DATACMNS-768 - Added support for Optional as query method parameter type.
Query method parameter values of JDK 8 or Guava Optional type are now transparently unwrapped on parameter value access. ParametersParameterAccessor now unwraps them on instance creation.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2013 the original author or authors.
|
||||
* Copyright 2008-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.util.QueryExecutionConverters;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -29,13 +32,13 @@ import org.springframework.util.Assert;
|
||||
public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
|
||||
private final Parameters<?, ?> parameters;
|
||||
private final Object[] values;
|
||||
private final List<Object> values;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param parameters
|
||||
* @param values
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
public ParametersParameterAccessor(Parameters<?, ?> parameters, Object[] values) {
|
||||
|
||||
@@ -45,7 +48,14 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
Assert.isTrue(parameters.getNumberOfParameters() == values.length, "Invalid number of parameters given!");
|
||||
|
||||
this.parameters = parameters;
|
||||
this.values = values.clone();
|
||||
|
||||
List<Object> unwrapped = new ArrayList<Object>(values.length);
|
||||
|
||||
for (Object element : values.clone()) {
|
||||
unwrapped.add(QueryExecutionConverters.unwrap(element));
|
||||
}
|
||||
|
||||
this.values = unwrapped;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +77,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Pageable) values[parameters.getPageableIndex()];
|
||||
return (Pageable) values.get(parameters.getPageableIndex());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -77,7 +87,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
public Sort getSort() {
|
||||
|
||||
if (parameters.hasSortParameter()) {
|
||||
return (Sort) values[parameters.getSortIndex()];
|
||||
return (Sort) values.get(parameters.getSortIndex());
|
||||
}
|
||||
|
||||
if (parameters.hasPageableParameter() && getPageable() != null) {
|
||||
@@ -95,7 +105,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T getValue(int index) {
|
||||
return (T) values[index];
|
||||
return (T) values.get(index);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -103,8 +113,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
* @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int)
|
||||
*/
|
||||
public Object getBindableValue(int index) {
|
||||
|
||||
return values[parameters.getBindableParameter(index).getIndex()];
|
||||
return values.get(parameters.getBindableParameter(index).getIndex());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -114,7 +123,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
public boolean hasBindableNullValue() {
|
||||
|
||||
for (Parameter parameter : parameters.getBindableParameters()) {
|
||||
if (values[parameter.getIndex()] == null) {
|
||||
if (values.get(parameter.getIndex()) == null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +136,6 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
* @see org.springframework.data.repository.query.ParameterAccessor#iterator()
|
||||
*/
|
||||
public BindableParameterIterator iterator() {
|
||||
|
||||
return new BindableParameterIterator();
|
||||
}
|
||||
|
||||
@@ -146,7 +154,6 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
* @return
|
||||
*/
|
||||
public Object next() {
|
||||
|
||||
return getBindableValue(currentIndex++);
|
||||
}
|
||||
|
||||
@@ -155,8 +162,7 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
|
||||
return values.length > currentIndex;
|
||||
return values.size() > currentIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -164,7 +170,6 @@ public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
public void remove() {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ public abstract class QueryExecutionConverters {
|
||||
QueryExecutionConverters.class.getClassLoader());
|
||||
|
||||
private static final Set<Class<?>> WRAPPER_TYPES = new HashSet<Class<?>>();
|
||||
private static final Set<Converter<Object, Object>> UNWRAPPERS = new HashSet<Converter<Object, Object>>();
|
||||
|
||||
static {
|
||||
|
||||
@@ -60,10 +61,12 @@ public abstract class QueryExecutionConverters {
|
||||
|
||||
if (GUAVA_PRESENT) {
|
||||
WRAPPER_TYPES.add(NullableWrapperToGuavaOptionalConverter.getWrapperType());
|
||||
UNWRAPPERS.add(GuavaOptionalUnwrapper.INSTANCE);
|
||||
}
|
||||
|
||||
if (JDK_8_PRESENT) {
|
||||
WRAPPER_TYPES.add(NullableWrapperToJdk8OptionalConverter.getWrapperType());
|
||||
UNWRAPPERS.add(Jdk8OptionalUnwrapper.INSTANCE);
|
||||
}
|
||||
|
||||
if (JDK_8_PRESENT && SPRING_4_2_PRESENT) {
|
||||
@@ -82,7 +85,14 @@ public abstract class QueryExecutionConverters {
|
||||
public static boolean supports(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
return WRAPPER_TYPES.contains(type);
|
||||
|
||||
for (Class<?> candidate : WRAPPER_TYPES) {
|
||||
if (candidate.isAssignableFrom(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,6 +116,30 @@ public abstract class QueryExecutionConverters {
|
||||
conversionService.addConverter(new NullableWrapperToFutureConverter(conversionService));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps the given source value in case it's one of the currently supported wrapper types detected at runtime.
|
||||
*
|
||||
* @param source can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Object unwrap(Object source) {
|
||||
|
||||
if (source == null || !supports(source.getClass())) {
|
||||
return source;
|
||||
}
|
||||
|
||||
for (Converter<Object, Object> converter : UNWRAPPERS) {
|
||||
|
||||
Object result = converter.convert(source);
|
||||
|
||||
if (result != source) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for converters that create instances of wrapper types such as Google Guava's and JDK 8's
|
||||
* {@code Optional} types.
|
||||
@@ -334,4 +368,44 @@ public abstract class QueryExecutionConverters {
|
||||
return CompletableFuture.class;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Converter} to unwrap Guava {@link Optional} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.12
|
||||
*/
|
||||
private static enum GuavaOptionalUnwrapper implements Converter<Object, Object> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
return source instanceof Optional ? ((Optional<?>) source).orNull() : source;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Converter} to unwrap JDK 8 {@link java.util.Optional} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.12
|
||||
*/
|
||||
private static enum Jdk8OptionalUnwrapper implements Converter<Object, Object> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object source) {
|
||||
return source instanceof java.util.Optional ? ((java.util.Optional<?>) source).orElse(null) : source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user