diff --git a/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java b/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java index 142231bf39..5154d16090 100644 --- a/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java +++ b/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java @@ -46,6 +46,7 @@ * @author Oliver Gierke * @author Alessandro Nistico * @author Johannes Englmeier + * @author Seonwoo Jung * @since 1.10 */ class ReflectionRepositoryInvoker implements RepositoryInvoker { @@ -188,7 +189,13 @@ private Object[] prepareParameters(Method method, MultiValueMap rawPa Object value = unwrapSingleElement(rawParameters.get(parameterName)); - result[i] = targetType.isInstance(value) ? value : convert(value, param); + // Collection- and array-valued parameters must be converted even if the value already is an instance of + // the target type (e.g. a List), as the raw elements (typically String) still need to be converted to the + // declared element type (see GH-3502). + boolean elementConversionRequired = value != null + && (targetType.isArray() || Iterable.class.isAssignableFrom(targetType)); + + result[i] = (targetType.isInstance(value) && !elementConversionRequired) ? value : convert(value, param); } } diff --git a/src/test/java/org/springframework/data/repository/support/ReflectionRepositoryInvokerUnitTests.java b/src/test/java/org/springframework/data/repository/support/ReflectionRepositoryInvokerUnitTests.java index 5cdf97f32f..e0b04363f1 100755 --- a/src/test/java/org/springframework/data/repository/support/ReflectionRepositoryInvokerUnitTests.java +++ b/src/test/java/org/springframework/data/repository/support/ReflectionRepositoryInvokerUnitTests.java @@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; @@ -52,6 +53,7 @@ * Integration tests for {@link ReflectionRepositoryInvoker}. * * @author Oliver Gierke + * @author Seonwoo Jung */ @ExtendWith(MockitoExtension.class) class ReflectionRepositoryInvokerUnitTests { @@ -221,6 +223,24 @@ void translatesCollectionRequestParametersCorrectly() throws Exception { } } + @Test // GH-3502 + void convertsElementsOfCollectionValuedQueryParameter() throws Exception { + + MultiValueMap parameters = new LinkedMultiValueMap<>(); + parameters.put("colors", Arrays.asList("RED", "GREEN")); + + var method = ColorRepository.class.getMethod("findByColorIn", Collection.class); + var repository = mock(ColorRepository.class); + + getInvokerFor(repository, expectInvocationOf(method)).invokeQueryMethod(method, parameters, Pageable.unpaged(), + Sort.unsorted()); + + var captor = ArgumentCaptor.forClass(Collection.class); + verify(repository).findByColorIn(captor.capture()); + + assertThat(captor.getValue()).containsExactly(Color.RED, Color.GREEN); + } + @Test // DATACMNS-700 void failedParameterConversionCapturesContext() throws Exception { @@ -371,4 +391,13 @@ interface DeleteByIdOverrideRepository extends Repository { } interface DeleteByIdOverrideSubRepository extends DeleteByIdOverrideRepository {} + + // GH-3502 + enum Color { + RED, GREEN; + } + + interface ColorRepository extends Repository { + List findByColorIn(@Param("colors") Collection colors); + } }