From e295e615bd5a2bba4fa32fe4bbc09b9a44cac9f2 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 12 Mar 2019 14:30:15 +0100 Subject: [PATCH] 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. --- .../convert/CassandraUDTValueProvider.java | 1 - .../convert/MappingCassandraConverter.java | 9 ++- .../CassandraTemplateIntegrationTests.java | 66 ++++++++++++++++++- .../data/cassandra/domain/User.java | 2 + 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java index cb2407b0c..b8a8e51f7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/CassandraUDTValueProvider.java @@ -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 getPropertyValue(CassandraPersistentProperty property) { String spelExpression = property.getSpelExpression(); 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 85929a7a1..5bb19ac43 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 @@ -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"); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateIntegrationTests.java index 193974286..fca4b3227 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateIntegrationTests.java @@ -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 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 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; + } + } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/domain/User.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/domain/User.java index 8dd4d36fe..215ad8132 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/domain/User.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/domain/User.java @@ -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;