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 69f1f1e530
commit e5aeae9033
2 changed files with 40 additions and 4 deletions

View File

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

@@ -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<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);
@@ -328,6 +360,9 @@ public class StringBasedR2dbcQueryUnitTests {
@Query("SELECT * FROM person")
PersonProjection findAsInterfaceProjection();
@Query("SELECT MAX(DATE)")
Flux<LocalDate> findAllLocalDates();
}
static class PersonDto {