Defer PersistentEntity lookup until actual DTO conversion.

We now lazily instantiate DtoInstantiatingConverter to defer the entity lookup if needed.

Closes #612
This commit is contained in:
Mark Paluch
2021-07-22 09:00:17 +02:00
parent 60fd8420a9
commit 9b12bcec6e
2 changed files with 40 additions and 4 deletions

View File

@@ -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 RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext;
private final EntityInstantiators instantiators;
private final Lazy<DtoInstantiatingConverter> converter;
ResultProcessingConverter(ResultProcessor processor,
MappingContext<? extends RelationalPersistentEntity<?>, ? 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<Object, Object> 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));
}
}
}

View File

@@ -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<Object> 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<LocalDate> findAllLocalDates();
}
static class PersonDto {