DATACASS-638 - Skip properties that are not part of the result.

We now skip properties during mapping (assign a null value) if the backing result (Row, UDT) does not contain a particular column.

Resultsets with limited columns are common for projections, in interface projections we use the actual entity to back calls to the projection.
This commit is contained in:
Mark Paluch
2019-03-12 14:30:15 +01:00
parent a704df7ec8
commit e295e615bd
4 changed files with 73 additions and 5 deletions

View File

@@ -79,7 +79,6 @@ public class CassandraUDTValueProvider implements CassandraValueProvider {
* @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@Nullable
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String spelExpression = property.getSpelExpression();

View File

@@ -27,6 +27,7 @@ import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.ApplicationContext;
@@ -952,7 +953,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
* @return the return value, may be {@literal null}.
*/
@Nullable
@SuppressWarnings("unchecked")
private Object getReadValue(CassandraValueProvider valueProvider, CassandraPersistentProperty property) {
if (property.isCompositePrimaryKey()) {
@@ -962,8 +962,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
return instantiatePrimaryKey(keyEntity, property, valueProvider);
}
Object value = valueProvider.getPropertyValue(property);
if (!valueProvider.hasProperty(property)) {
return null;
}
Object value = valueProvider.getPropertyValue(property);
return value == null ? null : convertReadValue(value, property.getTypeInformation());
}
@@ -1010,7 +1013,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
* @return the converted {@link Collection} or array, will never be {@literal null}.
*/
@Nullable
@SuppressWarnings({ "rawtypes", "unchecked" })
@SuppressWarnings({ "rawtypes" })
private Object readCollectionOrArrayInternal(Collection<?> source, TypeInformation<?> targetType) {
Assert.notNull(targetType, "Target type must not be null");

View File

@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import static org.springframework.data.cassandra.core.query.Criteria.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.time.Instant;
@@ -36,10 +37,15 @@ import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.mapping.BasicMapId;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.core.query.CassandraPageRequest;
import org.springframework.data.cassandra.core.query.Columns;
import org.springframework.data.cassandra.core.query.Query;
@@ -83,9 +89,11 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
SchemaTestUtils.potentiallyCreateTableFor(UserToken.class, template);
SchemaTestUtils.potentiallyCreateTableFor(BookReference.class, template);
SchemaTestUtils.potentiallyCreateTableFor(TimeClass.class, template);
SchemaTestUtils.potentiallyCreateTableFor(TypeWithCompositeKey.class, template);
SchemaTestUtils.truncate(User.class, template);
SchemaTestUtils.truncate(UserToken.class, template);
SchemaTestUtils.truncate(BookReference.class, template);
SchemaTestUtils.truncate(TypeWithCompositeKey.class, template);
}
@Test // DATACASS-343
@@ -130,6 +138,45 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
assertThat(loaded).containsSequence(token1, token2);
}
@Test // DATACASS-638
public void shouldSelectProjection() {
User user = new User("heisenberg", "Walter", "White");
template.insert(user);
Query query = Query.query(where("id").is(user.getId())).columns(Columns.empty().include("id").include("firstname"));
List<User> loaded = template.select(query, User.class);
assertThat(loaded).hasSize(1);
User loadedUser = loaded.get(0);
assertThat(loadedUser.getFirstname()).isNotNull();
assertThat(loadedUser.getLastname()).isNull();
}
@Test // DATACASS-638
public void shouldSelectProjectionWithCompositeKey() {
CompositeKey key = new CompositeKey("Walter", "White");
TypeWithCompositeKey user = new TypeWithCompositeKey(key, "comment");
template.insert(user);
Query query = Query.empty().columns(Columns.empty().include("firstname").include("comment"));
List<TypeWithCompositeKey> loaded = template.select(query, TypeWithCompositeKey.class);
assertThat(loaded).hasSize(1);
TypeWithCompositeKey loadedEntity = loaded.get(0);
assertThat(loadedEntity.getKey()).isNotNull();
assertThat(loadedEntity.getKey().getFirstname()).isNotNull();
assertThat(loadedEntity.getKey().getLastname()).isNull();
assertThat(loadedEntity.getComment()).isNotNull();
}
@Test // DATACASS-343
public void shouldSelectOneByQuery() {
@@ -203,7 +250,7 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
@Test // DATACASS-155
public void shouldNotOverrideLaterMutation() {
Instant now = LocalDateTime.now().atZone( ZoneId.systemDefault() ).toInstant();
Instant now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
User user = new User("heisenberg", "Walter", "White");
template.insert(user);
@@ -518,4 +565,21 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
@Id LocalTime id;
LocalTime bar;
}
@Data
@AllArgsConstructor
static class TypeWithCompositeKey {
@PrimaryKey CompositeKey key;
String comment;
}
@Data
@PrimaryKeyClass
@AllArgsConstructor
static class CompositeKey {
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) String firstname;
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) String lastname;
}
}

View File

@@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.cassandra.core.mapping.Table;
/**
@@ -43,6 +44,7 @@ public class User {
private String firstname;
private String lastname;
@PersistenceConstructor
public User(String id, String firstname, String lastname) {
this.id = id;