diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java index f832bebd..fd1e9a6d 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java @@ -29,6 +29,7 @@ import io.r2dbc.spi.test.MockRowMetadata; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import java.nio.ByteBuffer; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; @@ -43,6 +44,7 @@ import org.springframework.data.annotation.Id; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.annotation.Version; import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler; +import org.springframework.data.convert.ReadingConverter; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.callback.ReactiveEntityCallbacks; import org.springframework.data.mapping.context.PersistentEntities; @@ -91,7 +93,7 @@ public class R2dbcEntityTemplateUnitTests { .bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory()).build(); R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE, new MoneyConverter(), - new RowConverter(), new RowDocumentConverter()); + new RowConverter(), new RowDocumentConverter(), new PkConverter()); entityTemplate = new R2dbcEntityTemplate(client, PostgresDialect.INSTANCE, new MappingR2dbcConverter(new R2dbcMappingContext(), conversions)); @@ -632,6 +634,53 @@ public class R2dbcEntityTemplateUnitTests { }).verifyComplete(); } + @Test // GH-1725 + void projectDtoShouldReadPropertiesOnce() { + + MockRowMetadata metadata = MockRowMetadata.builder() + .columnMetadata(MockColumnMetadata.builder().name("number").type(R2dbcType.BINARY).build()).build(); + + ByteBuffer byteBuffer = ByteBuffer.allocate(8); + byteBuffer.putDouble(1.2); + byteBuffer.flip(); + + MockResult result = MockResult.builder() + .row(MockRow.builder().identified("number", Object.class, byteBuffer).metadata(metadata).build()).build(); + + recorder.addStubbing(s -> s.startsWith("SELECT"), result); + + entityTemplate.select(WithDoubleHolder.class).as(DoubleHolderProjection.class).all().as(StepVerifier::create) // + .assertNext(actual -> { + assertThat(actual.number.number).isCloseTo(1.2d, withinPercentage(1d)); + }).verifyComplete(); + } + + @ReadingConverter + static class PkConverter implements Converter { + + @Nullable + @Override + public DoubleHolder convert(ByteBuffer source) { + return new DoubleHolder(source.getDouble()); + } + } + + static class WithDoubleHolder { + DoubleHolder number; + } + + static class DoubleHolderProjection { + DoubleHolder number; + + public DoubleHolderProjection(DoubleHolder number) { + this.number = number; + } + } + + record DoubleHolder(double number) { + + } + @Test // GH-1696 void shouldConsiderRowDocumentConverter() { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java index 2c38f5bf..c239ef17 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java @@ -238,17 +238,8 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl EntityInstantiator instantiator = getEntityInstantiators().getInstantiatorFor(mappedEntity); R instance = instantiator.createInstance(mappedEntity, provider); - PersistentPropertyAccessor accessor = mappedEntity.getPropertyAccessor(instance); - populateProperties(context, mappedEntity, documentAccessor, evaluator, instance); - - PersistentPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor<>(accessor, - getConversionService()); - RelationalPropertyValueProvider valueProvider = newValueProvider(documentAccessor, evaluator, context); - - readProperties(context, mappedEntity, convertingAccessor, documentAccessor, valueProvider, Predicates.isTrue()); - - return accessor.getBean(); + return populateProperties(context, mappedEntity, documentAccessor, evaluator, instance); } private Object doReadOrProject(ConversionContext context, RowDocument source, TypeInformation typeHint, @@ -452,11 +443,7 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl EntityInstantiator instantiator = getEntityInstantiators().getInstantiatorFor(entity); S instance = instantiator.createInstance(entity, provider); - if (entity.requiresPropertyPopulation()) { - return populateProperties(context, entity, documentAccessor, evaluator, instance); - } - - return instance; + return populateProperties(context, entity, documentAccessor, evaluator, instance); } @Override @@ -509,6 +496,10 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl private S populateProperties(ConversionContext context, RelationalPersistentEntity entity, RowDocumentAccessor documentAccessor, SpELExpressionEvaluator evaluator, S instance) { + if (!entity.requiresPropertyPopulation()) { + return instance; + } + PersistentPropertyAccessor accessor = new ConvertingPropertyAccessor<>(entity.getPropertyAccessor(instance), getConversionService());