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:
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user