From e5ce4a226fcd10550f0bc44737ee4173a4df9961 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 1 Jul 2019 12:25:54 +0200 Subject: [PATCH] DATACASS-672 - Fix composite key property population when creating the enclosing entity through constructor. We now properly populate composite key properties when the key object is set through the entity constructor. Previously, key properties where left uninitialized (properties were not populated) when the key object was created in the code path to be injected as entity constructor argument. Key properties were only set in the case where the key also was be constructed with constructor arguments. --- .../convert/MappingCassandraConverter.java | 69 +++++++------------ .../MappingCassandraConverterUnitTests.java | 28 ++++++++ 2 files changed, 53 insertions(+), 44 deletions(-) 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 b64fcff53..8d6919c5c 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 @@ -173,13 +173,15 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * {@link CassandraPersistentEntity entity}. * @see org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity */ - private ConvertingPropertyAccessor newConvertingPropertyAccessor(Object source, CassandraPersistentEntity entity) { + @SuppressWarnings("unchecked") + private ConvertingPropertyAccessor newConvertingPropertyAccessor(S source, + CassandraPersistentEntity entity) { PersistentPropertyAccessor propertyAccessor = source instanceof PersistentPropertyAccessor ? (PersistentPropertyAccessor) source : entity.getPropertyAccessor(source); - return new ConvertingPropertyAccessor(propertyAccessor, getConversionService()); + return new ConvertingPropertyAccessor<>(propertyAccessor, getConversionService()); } private PersistentEntityParameterValueProvider newParameterValueProvider( @@ -247,28 +249,33 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } private S readEntityFromRow(CassandraPersistentEntity entity, Row row) { - return doRead(entity, row, expressionEvaluator -> new BasicCassandraRowValueProvider(row, expressionEvaluator)); + return doReadEntity(entity, row, + expressionEvaluator -> new BasicCassandraRowValueProvider(row, expressionEvaluator)); } private S readEntityFromTuple(CassandraPersistentEntity entity, TupleValue tupleValue) { - return doRead(entity, tupleValue, + return doReadEntity(entity, tupleValue, expressionEvaluator -> new CassandraTupleValueProvider(tupleValue, getCodecRegistry(), expressionEvaluator)); } private S readEntityFromUdt(CassandraPersistentEntity entity, UDTValue udtValue) { - return doRead(entity, udtValue, + return doReadEntity(entity, udtValue, expressionEvaluator -> new CassandraUDTValueProvider(udtValue, getCodecRegistry(), expressionEvaluator)); } - private S doRead(CassandraPersistentEntity entity, V value, + private S doReadEntity(CassandraPersistentEntity entity, Object value, Function valueProviderSupplier) { SpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(value, this.spELContext); - CassandraValueProvider valueProvider = valueProviderSupplier.apply(expressionEvaluator); + return doReadEntity(entity, valueProvider); + } + + private S doReadEntity(CassandraPersistentEntity entity, CassandraValueProvider valueProvider) { + PersistentEntityParameterValueProvider parameterValueProvider = newParameterValueProvider( entity, valueProvider); @@ -276,10 +283,14 @@ public class MappingCassandraConverter extends AbstractCassandraConverter S instance = instantiator.createInstance(entity, parameterValueProvider); - ConvertingPropertyAccessor propertyAccessor = newConvertingPropertyAccessor(instance, entity); - readProperties(entity, valueProvider, propertyAccessor); + if (entity.requiresPropertyPopulation()) { + ConvertingPropertyAccessor propertyAccessor = newConvertingPropertyAccessor(instance, entity); - return (S) propertyAccessor.getBean(); + readProperties(entity, valueProvider, propertyAccessor); + return propertyAccessor.getBean(); + } + + return instance; } private void readProperties(CassandraPersistentEntity entity, CassandraValueProvider valueProvider, @@ -298,38 +309,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return; } - if (property.isCompositePrimaryKey()) { - - CassandraPersistentEntity keyEntity = getMappingContext().getRequiredPersistentEntity(property); - - Object key = propertyAccessor.getProperty(property); - - if (key == null) { - key = instantiatePrimaryKey(keyEntity, property, valueProvider); - } - - // now recurse on using the key this time - ConvertingPropertyAccessor pkPropertyAccessor = newConvertingPropertyAccessor(key, keyEntity); - readProperties(keyEntity, valueProvider, pkPropertyAccessor); - - // now that the key's properties have been populated, set the key property on the entity - propertyAccessor.setProperty(property, pkPropertyAccessor.getBean()); - - return; + if (property.isCompositePrimaryKey() || valueProvider.hasProperty(property)) { + propertyAccessor.setProperty(property, getReadValue(valueProvider, property)); } - - if (!valueProvider.hasProperty(property)) { - return; - } - - propertyAccessor.setProperty(property, getReadValue(valueProvider, property)); - } - - private Object instantiatePrimaryKey(CassandraPersistentEntity entity, CassandraPersistentProperty keyProperty, - CassandraValueProvider propertyProvider) { - - return this.instantiators.getInstantiatorFor(entity).createInstance(entity, - newParameterValueProvider(entity, propertyProvider)); } /* (non-Javadoc) @@ -687,7 +669,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } // if the class doesn't have an id property, then it's using MapId - final MapId id = BasicMapId.id(); + MapId id = BasicMapId.id(); for (CassandraPersistentProperty property : entity) { @@ -954,8 +936,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (property.isCompositePrimaryKey()) { CassandraPersistentEntity keyEntity = getMappingContext().getRequiredPersistentEntity(property); - - return instantiatePrimaryKey(keyEntity, property, valueProvider); + return doReadEntity(keyEntity, valueProvider); } Object value = valueProvider.getPropertyValue(property); 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 9b1336898..3f9694c42 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 @@ -822,6 +822,20 @@ public class MappingCassandraConverterUnitTests { assertThat(result.id.localDate).isEqualTo(java.time.LocalDate.of(2017, 1, 2)); } + @Test // DATACASS-672 + public void shouldReadTypeCompositePrimaryKeyUsingEntityInstantiatorAndPropertyPopulationInKeyCorrectly() { + + // condition, localDate + Row row = RowMockUtil.newRowMock(column("firstname", "Walter", DataType.varchar()), + column("lastname", "White", DataType.varchar())); + + TableWithCompositeKeyViaConstructor result = mappingCassandraConverter + .read(TableWithCompositeKeyViaConstructor.class, row); + + assertThat(result.key.firstname).isEqualTo("Walter"); + assertThat(result.key.lastname).isEqualTo("White"); + } + @Test // DATACASS-308 public void shouldWriteWhereConditionForTypeWithPkClassKeyUsingMapId() { @@ -1136,6 +1150,20 @@ public class MappingCassandraConverterUnitTests { MINT, USED } + @PrimaryKeyClass + public static class CompositeKeyWithPropertyAccessors { + + @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) String firstname; + @PrimaryKeyColumn String lastname; + } + + @Table + @RequiredArgsConstructor + public static class TableWithCompositeKeyViaConstructor { + + @PrimaryKey private final CompositeKeyWithPropertyAccessors key; + } + @Table public static class TypeWithLocalDate {