Skip to content

Commit de69842

Browse files
committed
Read DTO projection properties only once.
We ensure to not read DTO properties multiple times if these are already read by their persistence creator. Closes #1472
1 parent e68a047 commit de69842

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,15 @@ private <R> R doReadProjection(ConversionContext context, CassandraValueProvider
346346

347347
EntityInstantiator instantiator = instantiators.getInstantiatorFor(mappedEntity);
348348
R instance = instantiator.createInstance(mappedEntity, provider);
349-
PersistentPropertyAccessor<R> accessor = mappedEntity.getPropertyAccessor(instance);
350349

351-
readProperties(context, mappedEntity, valueProviderToUse, accessor, Predicates.isTrue());
350+
if (mappedEntity.requiresPropertyPopulation()) {
352351

353-
return accessor.getBean();
352+
PersistentPropertyAccessor<R> accessor = mappedEntity.getPropertyAccessor(instance);
353+
readProperties(context, mappedEntity, valueProviderToUse, accessor, isConstructorArgument(mappedEntity).negate());
354+
return accessor.getBean();
355+
}
356+
357+
return instance;
354358
}
355359

356360
private Object doReadOrProject(ConversionContext context, Row row, TypeInformation<?> typeHint,
@@ -515,14 +519,19 @@ private <S> S doReadEntity(ConversionContext context, CassandraValueProvider val
515519
EntityInstantiator instantiator = this.instantiators.getInstantiatorFor(entity);
516520
S instance = instantiator.createInstance(entity, provider);
517521

518-
if (entity.requiresPropertyPopulation()) {
519-
ConvertingPropertyAccessor<S> propertyAccessor = newConvertingPropertyAccessor(instance, entity);
522+
return populateProperties(context, entity, valueProvider, instance);
523+
}
524+
525+
private <S> S populateProperties(ConversionContext context, CassandraPersistentEntity<?> entity,
526+
CassandraValueProvider valueProvider, S instance) {
520527

521-
readProperties(context, entity, valueProvider, propertyAccessor, isConstructorArgument(entity).negate());
522-
return propertyAccessor.getBean();
528+
if (!entity.requiresPropertyPopulation()) {
529+
return instance;
523530
}
524531

525-
return instance;
532+
ConvertingPropertyAccessor<S> propertyAccessor = newConvertingPropertyAccessor(instance, entity);
533+
readProperties(context, entity, valueProvider, propertyAccessor, isConstructorArgument(entity).negate());
534+
return propertyAccessor.getBean();
526535
}
527536

528537
private void readProperties(ConversionContext context, CassandraPersistentEntity<?> entity,
@@ -1437,7 +1446,7 @@ public <T> T getParameterValue(Parameter<T, CassandraPersistentProperty> paramet
14371446

14381447
}
14391448

1440-
private record PropertyTranslatingPropertyAccessor<T> (PersistentPropertyAccessor<T> delegate,
1449+
private record PropertyTranslatingPropertyAccessor<T>(PersistentPropertyAccessor<T> delegate,
14411450
PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor<T> {
14421451

14431452
static <T> PersistentPropertyAccessor<T> create(PersistentPropertyAccessor<T> delegate,

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424
import java.math.BigInteger;
2525
import java.net.InetAddress;
2626
import java.net.UnknownHostException;
27+
import java.nio.ByteBuffer;
2728
import java.time.Instant;
2829
import java.time.LocalDate;
2930
import java.time.LocalDateTime;
3031
import java.time.ZoneId;
3132
import java.time.ZoneOffset;
3233
import java.util.*;
3334

35+
import org.assertj.core.data.Percentage;
3436
import org.json.simple.JSONObject;
3537
import org.json.simple.parser.JSONParser;
3638
import org.json.simple.parser.ParseException;
@@ -55,6 +57,7 @@
5557
import org.springframework.data.cassandra.domain.UserToken;
5658
import org.springframework.data.cassandra.support.UserDefinedTypeBuilder;
5759
import org.springframework.data.cassandra.test.util.RowMockUtil;
60+
import org.springframework.data.convert.ReadingConverter;
5861
import org.springframework.data.convert.SimplePropertyValueConversions;
5962
import org.springframework.data.convert.ValueConverter;
6063
import org.springframework.data.projection.EntityProjection;
@@ -88,9 +91,13 @@ public class MappingCassandraConverterUnitTests {
8891
@BeforeEach
8992
void setUp() {
9093

94+
CassandraCustomConversions conversions = new CassandraCustomConversions(
95+
List.of(new ByteBufferToDoubleHolderConverter()));
9196
this.mappingContext = new CassandraMappingContext();
97+
this.mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
9298

9399
this.converter = new MappingCassandraConverter(mappingContext);
100+
this.converter.setCustomConversions(conversions);
94101
this.converter.afterPropertiesSet();
95102
}
96103

@@ -1074,6 +1081,27 @@ void shouldCreateDtoProjectionsThroughConstructor() {
10741081
assertThat(result.tuple().one).isEqualTo("One");
10751082
}
10761083

1084+
@Test // GH-1472
1085+
void projectShouldReadDtoProjectionPropertiesOnlyOnce() {
1086+
1087+
ByteBuffer number = ByteBuffer.allocate(8);
1088+
number.putDouble(1.2d);
1089+
number.flip();
1090+
1091+
rowMock = RowMockUtil.newRowMock(RowMockUtil.column("number", number, DataTypes.BLOB));
1092+
1093+
EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(
1094+
new SpelAwareProxyProjectionFactory(), EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy(),
1095+
this.mappingContext);
1096+
1097+
EntityProjection<DoubleHolderDto, WithDoubleHolder> projection = introspector.introspect(DoubleHolderDto.class,
1098+
WithDoubleHolder.class);
1099+
1100+
DoubleHolderDto result = this.converter.project(projection, rowMock);
1101+
1102+
assertThat(result.number.number).isCloseTo(1.2, Percentage.withPercentage(1));
1103+
}
1104+
10771105
@Test // GH-1471
10781106
void propertyValueConversionsCacheShouldConsiderPropertyEquality() {
10791107

@@ -1770,4 +1798,29 @@ public void setLastName(String lastName) {
17701798
this.lastName = lastName;
17711799
}
17721800
}
1801+
1802+
@ReadingConverter
1803+
static class ByteBufferToDoubleHolderConverter implements Converter<ByteBuffer, DoubleHolder> {
1804+
1805+
@Override
1806+
public DoubleHolder convert(ByteBuffer source) {
1807+
return new DoubleHolder(source.getDouble());
1808+
}
1809+
}
1810+
1811+
record DoubleHolder(double number) {
1812+
1813+
}
1814+
1815+
static class WithDoubleHolder {
1816+
DoubleHolder number;
1817+
}
1818+
1819+
static class DoubleHolderDto {
1820+
DoubleHolder number;
1821+
1822+
public DoubleHolderDto(DoubleHolder number) {
1823+
this.number = number;
1824+
}
1825+
}
17731826
}

0 commit comments

Comments
 (0)