From 9b12bcec6ee4b384cf8408494c1268000b2910ea 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 e9c2b77a..d6731fd0 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 @@ -28,6 +28,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp 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.RowsFetchSpec; import org.springframework.util.ClassUtils; @@ -72,6 +73,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, @@ -79,6 +81,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) @@ -108,10 +112,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 eb1463e9..29e4a1e1 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 @@ -18,7 +18,15 @@ package org.springframework.data.r2dbc.repository.query; import static org.assertj.core.api.Assertions.*; 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; @@ -275,6 +284,29 @@ public class StringBasedR2dbcQueryUnitTests { assertThat(query.resolveResultType(query.getQueryMethod().getResultProcessor())).isEqualTo(PersonDto.class); } + @Test // gh-475 + 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); @@ -329,6 +361,9 @@ public class StringBasedR2dbcQueryUnitTests { @Query("SELECT * FROM person") PersonProjection findAsInterfaceProjection(); + + @Query("SELECT MAX(DATE)") + Flux findAllLocalDates(); } static class PersonDto {