From e5aeae9033f0a0cd8fad26c1a4d6bed4cf741531 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 22 Jul 2021 09:00:17 +0200 Subject: [PATCH] Defer PersistentEntity lookup until actual DTO conversion. We now lazily instantiate DtoInstantiatingConverter to defer the entity lookup if needed. Closes #612 --- .../repository/query/R2dbcQueryExecution.java | 9 ++--- .../query/StringBasedR2dbcQueryUnitTests.java | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java b/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java index 269e5c3..35c6711 100644 --- a/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java +++ b/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java @@ -29,6 +29,7 @@ import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.repository.query.DtoInstantiatingConverter; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; +import org.springframework.data.util.Lazy; import org.springframework.data.util.ReflectionUtils; import org.springframework.r2dbc.core.FetchSpec; import org.springframework.util.ClassUtils; @@ -73,6 +74,7 @@ interface R2dbcQueryExecution { private final ResultProcessor processor; private final MappingContext, ? extends RelationalPersistentProperty> mappingContext; private final EntityInstantiators instantiators; + private final Lazy converter; ResultProcessingConverter(ResultProcessor processor, MappingContext, ? extends RelationalPersistentProperty> mappingContext, @@ -80,6 +82,8 @@ interface R2dbcQueryExecution { this.processor = processor; this.mappingContext = mappingContext; this.instantiators = instantiators; + this.converter = Lazy.of(() -> new DtoInstantiatingConverter(processor.getReturnedType().getReturnedType(), + this.mappingContext, this.instantiators)); } /* (non-Javadoc) @@ -109,10 +113,7 @@ interface R2dbcQueryExecution { } } - Converter converter = new DtoInstantiatingConverter(returnedType.getReturnedType(), - this.mappingContext, this.instantiators); - - return this.processor.processResult(source, converter); + return this.processor.processResult(source, it -> this.converter.get().convert(it)); } } } diff --git a/src/test/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQueryUnitTests.java b/src/test/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQueryUnitTests.java index b12a737..dbbd37b 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQueryUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQueryUnitTests.java @@ -19,7 +19,15 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import io.r2dbc.spi.test.MockColumnMetadata; +import io.r2dbc.spi.test.MockResult; +import io.r2dbc.spi.test.MockRow; +import io.r2dbc.spi.test.MockRowMetadata; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + import java.lang.reflect.Method; +import java.time.LocalDate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,6 +46,7 @@ import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy; import org.springframework.data.r2dbc.dialect.PostgresDialect; import org.springframework.data.r2dbc.mapping.R2dbcMappingContext; import org.springframework.data.r2dbc.repository.Query; +import org.springframework.data.r2dbc.testing.StatementRecorder; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; @@ -274,6 +283,29 @@ public class StringBasedR2dbcQueryUnitTests { assertThat(query.resolveResultType(query.getQueryMethod().getResultProcessor())).isEqualTo(PersonDto.class); } + @Test // gh-612 + void selectsSimpleType() { + + MockRowMetadata metadata = MockRowMetadata.builder() + .columnMetadata(MockColumnMetadata.builder().name("date").build()).build(); + LocalDate value = LocalDate.now(); + MockResult result = MockResult.builder().rowMetadata(metadata) + .row(MockRow.builder().identified(0, LocalDate.class, value).build()).build(); + + StatementRecorder recorder = StatementRecorder.newInstance(); + recorder.addStubbing(s -> s.equals("SELECT MAX(DATE)"), result); + + databaseClient = DatabaseClient.builder() // + .connectionFactory(recorder) // + .bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory()).build(); + + StringBasedR2dbcQuery query = getQueryMethod("findAllLocalDates"); + + Flux flux = (Flux) query.execute(new Object[0]); + + flux.as(StepVerifier::create).expectNext(value).verifyComplete(); + } + private StringBasedR2dbcQuery getQueryMethod(String name, Class... args) { Method method = ReflectionUtils.findMethod(SampleRepository.class, name, args); @@ -328,6 +360,9 @@ public class StringBasedR2dbcQueryUnitTests { @Query("SELECT * FROM person") PersonProjection findAsInterfaceProjection(); + + @Query("SELECT MAX(DATE)") + Flux findAllLocalDates(); } static class PersonDto {