diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java index 38aa4e075..e7aa131db 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java @@ -344,11 +344,15 @@ public class MappingCassandraConverter extends AbstractCassandraConverter EntityInstantiator instantiator = instantiators.getInstantiatorFor(mappedEntity); R instance = instantiator.createInstance(mappedEntity, provider); - PersistentPropertyAccessor accessor = mappedEntity.getPropertyAccessor(instance); - readProperties(context, mappedEntity, valueProviderToUse, accessor, Predicates.isTrue()); + if (mappedEntity.requiresPropertyPopulation()) { - return accessor.getBean(); + PersistentPropertyAccessor accessor = mappedEntity.getPropertyAccessor(instance); + readProperties(context, mappedEntity, valueProviderToUse, accessor, isConstructorArgument(mappedEntity).negate()); + return accessor.getBean(); + } + + return instance; } private Object doReadOrProject(ConversionContext context, Row row, TypeInformation typeHint, @@ -513,14 +517,19 @@ public class MappingCassandraConverter extends AbstractCassandraConverter EntityInstantiator instantiator = this.instantiators.getInstantiatorFor(entity); S instance = instantiator.createInstance(entity, provider); - if (entity.requiresPropertyPopulation()) { - ConvertingPropertyAccessor propertyAccessor = newConvertingPropertyAccessor(instance, entity); + return populateProperties(context, entity, valueProvider, instance); + } - readProperties(context, entity, valueProvider, propertyAccessor, isConstructorArgument(entity).negate()); - return propertyAccessor.getBean(); + private S populateProperties(ConversionContext context, CassandraPersistentEntity entity, + CassandraValueProvider valueProvider, S instance) { + + if (!entity.requiresPropertyPopulation()) { + return instance; } - return instance; + ConvertingPropertyAccessor propertyAccessor = newConvertingPropertyAccessor(instance, entity); + readProperties(context, entity, valueProvider, propertyAccessor, isConstructorArgument(entity).negate()); + return propertyAccessor.getBean(); } private void readProperties(ConversionContext context, CassandraPersistentEntity entity, @@ -1419,7 +1428,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } - private record PropertyTranslatingPropertyAccessor (PersistentPropertyAccessor delegate, + private record PropertyTranslatingPropertyAccessor(PersistentPropertyAccessor delegate, PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor { static PersistentPropertyAccessor create(PersistentPropertyAccessor delegate, diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java index f3a87dab6..c528dd137 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.*; import static org.springframework.data.cassandra.core.mapping.BasicMapId.*; import static org.springframework.data.cassandra.test.util.RowMockUtil.*; +import com.carrotsearch.hppc.mutables.DoubleHolder; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -26,11 +27,13 @@ import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.ToString; +import java.awt.print.Book; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.ByteBuffer; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -49,11 +52,14 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import org.assertj.core.data.Percentage; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + +import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; @@ -82,6 +88,9 @@ import org.springframework.data.cassandra.domain.User; import org.springframework.data.cassandra.domain.UserToken; import org.springframework.data.cassandra.support.UserDefinedTypeBuilder; import org.springframework.data.cassandra.test.util.RowMockUtil; +import org.springframework.data.convert.ReadingConverter; +import org.springframework.data.convert.SimplePropertyValueConversions; +import org.springframework.data.convert.ValueConverter; import org.springframework.data.projection.EntityProjection; import org.springframework.data.projection.EntityProjectionIntrospector; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -110,9 +119,13 @@ public class MappingCassandraConverterUnitTests { @BeforeEach void setUp() { + CassandraCustomConversions conversions = new CassandraCustomConversions( + List.of(new ByteBufferToDoubleHolderConverter())); this.mappingContext = new CassandraMappingContext(); + this.mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); this.mappingCassandraConverter = new MappingCassandraConverter(mappingContext); + this.mappingCassandraConverter.setCustomConversions(conversions); this.mappingCassandraConverter.afterPropertiesSet(); } @@ -1012,6 +1025,27 @@ public class MappingCassandraConverterUnitTests { assertThat(result.getTuple().one).isEqualTo("One"); } + @Test // GH-1472 + void projectShouldReadDtoProjectionPropertiesOnlyOnce() { + + ByteBuffer number = ByteBuffer.allocate(8); + number.putDouble(1.2d); + number.flip(); + + rowMock = RowMockUtil.newRowMock(RowMockUtil.column("number", number, DataTypes.BLOB)); + + EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create( + new SpelAwareProxyProjectionFactory(), EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy(), + this.mappingContext); + + EntityProjection projection = introspector.introspect(DoubleHolderDto.class, + WithDoubleHolder.class); + + DoubleHolderDto result = this.mappingCassandraConverter.project(projection, rowMock); + + assertThat(result.number.number).isCloseTo(1.2, Percentage.withPercentage(1)); + } + private static List getValues(Map statement) { return new ArrayList<>(statement.values()); } @@ -1522,4 +1556,29 @@ public class MappingCassandraConverterUnitTests { String lastName; } + + @ReadingConverter + static class ByteBufferToDoubleHolderConverter implements Converter { + + @Override + public DoubleHolder convert(ByteBuffer source) { + return new DoubleHolder(source.getDouble()); + } + } + + record DoubleHolder(double number) { + + } + + static class WithDoubleHolder { + DoubleHolder number; + } + + static class DoubleHolderDto { + DoubleHolder number; + + public DoubleHolderDto(DoubleHolder number) { + this.number = number; + } + } }