diff --git a/src/main/java/org/springframework/data/repository/query/ResultProcessor.java b/src/main/java/org/springframework/data/repository/query/ResultProcessor.java index abb196854..12419c02b 100644 --- a/src/main/java/org/springframework/data/repository/query/ResultProcessor.java +++ b/src/main/java/org/springframework/data/repository/query/ResultProcessor.java @@ -219,6 +219,10 @@ public class ResultProcessor { return new ChainingConverter(targetType, source -> { + if (source == null || targetType.isInstance(source)) { + return source; + } + Object intermediate = ChainingConverter.this.convert(source); return intermediate == null || targetType.isInstance(intermediate) ? intermediate diff --git a/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java b/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java index 39bfbcf7e..abbc08ab8 100755 --- a/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/ResultProcessorUnitTests.java @@ -19,8 +19,10 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import io.reactivex.Flowable; +import lombok.Getter; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; import rx.Observable; import rx.Single; @@ -308,6 +310,37 @@ class ResultProcessorUnitTests { assertThat(content.get(0)).isInstanceOf(SampleProjection.class); } + @Test // GH-2347 + void findByListSkipsConversionIfTypeAlreadyMatches() throws Exception { + + List result = getProcessor("findAllAbstractDtos") + .processResult(Collections.singletonList(new ConcreteDto("Walter", "White"))); + + assertThat(result.get(0)).isInstanceOf(ConcreteDto.class); + } + + @Test // GH-2347 + void streamBySkipsConversionIfTypeAlreadyMatches() throws Exception { + + Stream result = getProcessor("streamAllAbstractDtos") + .processResult(Stream.of(new ConcreteDto("Walter", "White"))); + + assertThat(result.findFirst().get()).isInstanceOf(ConcreteDto.class); + } + + @Test // GH-2347 + void findFluxSkipsConversionIfTypeAlreadyMatches() throws Exception { + + Flux result = getProcessor("findFluxOfAbstractDtos") + .processResult(Flux.just(new ConcreteDto("Walter", "White"))); + + result.as(StepVerifier::create) // + .consumeNextWith(actual -> { + + assertThat(actual).isInstanceOf(ConcreteDto.class); + }).verifyComplete(); + } + private static ResultProcessor getProcessor(String methodName, Class... parameters) throws Exception { return getQueryMethod(methodName, parameters).getResultProcessor(); } @@ -356,6 +389,12 @@ class ResultProcessorUnitTests { Observable findObservableProjection(); Flowable findFlowableProjection(); + + List findAllAbstractDtos(); + + Stream streamAllAbstractDtos(); + + Flux findFluxOfAbstractDtos(); } static class Sample { @@ -367,6 +406,23 @@ class ResultProcessorUnitTests { } } + @Getter + static abstract class AbstractDto { + final String firstname, lastname; + + public AbstractDto(String firstname, String lastname) { + this.firstname = firstname; + this.lastname = lastname; + } + } + + static class ConcreteDto extends AbstractDto { + + public ConcreteDto(String firstname, String lastname) { + super(firstname, lastname); + } + } + static class SampleDto {} @lombok.Value