Browse Source

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.
pull/147/head
Oliver Gierke 11 years ago
parent
commit
1185e71214
  1. 37
      src/main/java/org/springframework/data/repository/query/ParametersParameterAccessor.java
  2. 76
      src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java
  3. 32
      src/test/java/org/springframework/data/repository/util/QueryExecutionConvertersUnitTests.java

37
src/main/java/org/springframework/data/repository/query/ParametersParameterAccessor.java

@ -1,5 +1,5 @@ @@ -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 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -164,7 +170,6 @@ public class ParametersParameterAccessor implements ParameterAccessor {
* @see java.util.Iterator#remove()
*/
public void remove() {
throw new UnsupportedOperationException();
}
}

76
src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java

@ -52,6 +52,7 @@ public abstract class QueryExecutionConverters { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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;
}
}
}

32
src/test/java/org/springframework/data/repository/util/QueryExecutionConvertersUnitTests.java

@ -109,4 +109,36 @@ public class QueryExecutionConvertersUnitTests { @@ -109,4 +109,36 @@ public class QueryExecutionConvertersUnitTests {
assertThat(result.isDone(), is(true));
assertThat(result.get(), is(nullValue()));
}
/**
* @see DATACMNS-768
*/
@Test
public void unwrapsJdk8Optional() {
assertThat(QueryExecutionConverters.unwrap(java.util.Optional.of("Foo")), is((Object) "Foo"));
}
/**
* @see DATACMNS-768
*/
@Test
public void unwrapsGuava8Optional() {
assertThat(QueryExecutionConverters.unwrap(Optional.of("Foo")), is((Object) "Foo"));
}
/**
* @see DATACMNS-768
*/
@Test
public void unwrapsNullToNull() {
assertThat(QueryExecutionConverters.unwrap(null), is(nullValue()));
}
/**
* @see DATACMNS-768
*/
@Test
public void unwrapsNonWrapperTypeToItself() {
assertThat(QueryExecutionConverters.unwrap("Foo"), is((Object) "Foo"));
}
}

Loading…
Cancel
Save