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.
This commit is contained in:
Mark Paluch
2019-07-01 12:25:54 +02:00
parent 1022985b85
commit e5ce4a226f
2 changed files with 53 additions and 44 deletions

View File

@@ -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 <S> ConvertingPropertyAccessor<S> 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 <S> PersistentEntityParameterValueProvider<CassandraPersistentProperty> newParameterValueProvider(
@@ -247,28 +249,33 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
private <S> S readEntityFromRow(CassandraPersistentEntity<S> entity, Row row) {
return doRead(entity, row, expressionEvaluator -> new BasicCassandraRowValueProvider(row, expressionEvaluator));
return doReadEntity(entity, row,
expressionEvaluator -> new BasicCassandraRowValueProvider(row, expressionEvaluator));
}
private <S> S readEntityFromTuple(CassandraPersistentEntity<S> entity, TupleValue tupleValue) {
return doRead(entity, tupleValue,
return doReadEntity(entity, tupleValue,
expressionEvaluator -> new CassandraTupleValueProvider(tupleValue, getCodecRegistry(), expressionEvaluator));
}
private <S> S readEntityFromUdt(CassandraPersistentEntity<S> entity, UDTValue udtValue) {
return doRead(entity, udtValue,
return doReadEntity(entity, udtValue,
expressionEvaluator -> new CassandraUDTValueProvider(udtValue, getCodecRegistry(), expressionEvaluator));
}
private <S, V> S doRead(CassandraPersistentEntity<S> entity, V value,
private <S> S doReadEntity(CassandraPersistentEntity<S> entity, Object value,
Function<SpELExpressionEvaluator, CassandraValueProvider> valueProviderSupplier) {
SpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(value, this.spELContext);
CassandraValueProvider valueProvider = valueProviderSupplier.apply(expressionEvaluator);
return doReadEntity(entity, valueProvider);
}
private <S> S doReadEntity(CassandraPersistentEntity<S> entity, CassandraValueProvider valueProvider) {
PersistentEntityParameterValueProvider<CassandraPersistentProperty> 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<S> 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);

View File

@@ -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 {