From d27ac797712f2ba06d1301ec83bef1612e74338f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 6 Apr 2021 13:29:59 +0200 Subject: [PATCH] Back-off in ResultProcessor if the result object is an instance of the target type. We now do not attempt to convert the object if it is an instance of the declared target type. Closes #2347. --- .../repository/query/ResultProcessor.java | 4 ++ .../query/ResultProcessorUnitTests.java | 56 +++++++++++++++++++ 2 files changed, 60 insertions(+) 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 abb1968540..12419c02bb 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 ChainingConverter and(final Converter converter) { 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 39bfbcf7e3..abbc08ab8a 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.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 @@ void supportsFlowableProjections() throws Exception { 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 @@ interface SampleRepository extends Repository { Observable findObservableProjection(); Flowable findFlowableProjection(); + + List findAllAbstractDtos(); + + Stream streamAllAbstractDtos(); + + Flux findFluxOfAbstractDtos(); } static class Sample { @@ -367,6 +406,23 @@ public Sample(String firstname, String lastname) { } } + @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