From ac0d34fe2c5377a705f246c16e6eb1641e709158 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 19 Jul 2017 10:50:44 +0200 Subject: [PATCH] =?UTF-8?q?DATACASS-362=20-=20Add=20tests=20to=20assert=20?= =?UTF-8?q?composite=20key=20usage=20in=20=E2=80=A6ById=20template=20metho?= =?UTF-8?q?ds.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend Javadoc to mention the accepted Id types. Add tests to verify query creation via MappingCassandraConverter. --- .../core/AsyncCassandraOperations.java | 12 ++- .../cassandra/core/CassandraOperations.java | 12 ++- .../core/ReactiveCassandraOperations.java | 12 ++- .../convert/MappingCassandraConverter.java | 27 ++++--- .../MappingCassandraConverterUnitTests.java | 79 ++++++++++++++++--- 5 files changed, 111 insertions(+), 31 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java index 6014ca0f8..0f7170b29 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraOperations.java @@ -206,7 +206,9 @@ public interface AsyncCassandraOperations { /** * Determine whether the row {@code entityClass} with the given {@code id} exists. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass must not be {@literal null}. * @return {@literal true}, if the object exists. * @throws DataAccessException if there is any problem executing the query. @@ -216,7 +218,9 @@ public interface AsyncCassandraOperations { /** * Execute the Select by {@code id} for the given {@code entityClass}. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @return the converted object or {@literal null}. * @throws DataAccessException if there is any problem executing the query. @@ -283,7 +287,9 @@ public interface AsyncCassandraOperations { /** * Remove the given object from the table by id. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @return {@literal true} if the deletion was applied. * @throws DataAccessException if there is any problem executing the query. diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index 98921ac1d..e8d58a43a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -227,7 +227,9 @@ public interface CassandraOperations { /** * Determine whether the row {@code entityClass} with the given {@code id} exists. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @return true, if the object exists. * @throws DataAccessException if there is any problem executing the query. @@ -237,7 +239,9 @@ public interface CassandraOperations { /** * Execute the Select by {@code id} for the given {@code entityClass}. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @return the converted object or {@literal null}. * @throws DataAccessException if there is any problem executing the query. @@ -301,7 +305,9 @@ public interface CassandraOperations { /** * Remove the given object from the table by id. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @throws DataAccessException if there is any problem executing the query. */ diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java index 65fc11921..33a6f783d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraOperations.java @@ -152,7 +152,9 @@ public interface ReactiveCassandraOperations { /** * Determine whether the row {@code entityClass} with the given {@code id} exists. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass must not be {@literal null}. * @return {@literal true} if the object exists. * @throws DataAccessException if there is any problem issuing the execution. @@ -162,7 +164,9 @@ public interface ReactiveCassandraOperations { /** * Execute the Select by {@code id} for the given {@code entityClass}. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @return the result object returned by the action or {@link Mono#empty()} * @throws DataAccessException if there is any problem issuing the execution. @@ -229,7 +233,9 @@ public interface ReactiveCassandraOperations { /** * Remove the given object from the table by id. * - * @param id must not be {@literal null}. + * @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the + * {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or + * {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}. * @param entityClass The entity type must not be {@literal null}. * @return {@literal true} if the deletion was applied. * @throws DataAccessException if there is any problem issuing the execution. 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 65e739aa5..b7a3ea68b 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 @@ -144,6 +144,13 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return mappingContext; } + /** + * Read a {@link Row} into the requested target {@code type}. + * + * @param type must not be {@literal null}. + * @param row must not be {@literal null}. + * @return the converted valued. + */ @SuppressWarnings("unchecked") public R readRow(Class type, Row row) { @@ -498,12 +505,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (id instanceof MapId) { - // FIXME: Generics CassandraPersistentEntity whereEntity = compositeIdProperty != null ? mappingContext.getRequiredPersistentEntity(compositeIdProperty) : entity; - return getWhereClauses((MapId) id, whereEntity); + return getWhereClauses(MapId.class.cast(id), whereEntity); } if (idProperty == null) { @@ -518,20 +524,16 @@ public class MappingCassandraConverter extends AbstractCassandraConverter String.format("Cannot use [%s] as composite Id for [%s]", id, entity.getName())); } - CassandraPersistentEntity compositePrimaryKey = - mappingContext.getRequiredPersistentEntity(compositeIdProperty); + CassandraPersistentEntity compositePrimaryKey = mappingContext + .getRequiredPersistentEntity(compositeIdProperty); return getWhereClauses(getConvertingAccessor(id, compositePrimaryKey), compositePrimaryKey); } Class targetType = getTargetType(idProperty); - if (getConversionService().canConvert(id.getClass(), targetType)) { - return Collections.singleton( - QueryBuilder.eq(idProperty.getColumnName().toCql(), getPotentiallyConvertedSimpleValue(id, targetType))); - } - - return Collections.singleton(QueryBuilder.eq(idProperty.getColumnName().toCql(), id)); + return Collections.singleton( + QueryBuilder.eq(idProperty.getColumnName().toCql(), getPotentiallyConvertedSimpleValue(id, targetType))); } private Object extractId(Object source, CassandraPersistentEntity entity) { @@ -608,8 +610,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (idProperty != null) { // TODO: NullId - return propertyAccessor.getProperty(idProperty, idProperty.isCompositePrimaryKey() - ? (Class) idProperty.getType() : (Class) getTargetType(idProperty)); + return propertyAccessor.getProperty(idProperty, + idProperty.isCompositePrimaryKey() ? (Class) idProperty.getType() + : (Class) getTargetType(idProperty)); } // if the class doesn't have an id property, then it's using MapId 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 0b85eaf5d..9cb495b23 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 @@ -50,8 +50,10 @@ import org.junit.rules.ExpectedException; import org.springframework.core.SpringVersion; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.data.cassandra.core.cql.PrimaryKeyType; +import org.springframework.data.cassandra.core.mapping.BasicMapId; import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; import org.springframework.data.cassandra.core.mapping.CassandraType; +import org.springframework.data.cassandra.core.mapping.MapId; import org.springframework.data.cassandra.core.mapping.PrimaryKey; import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass; import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; @@ -77,6 +79,7 @@ import com.datastax.driver.core.querybuilder.Delete; import com.datastax.driver.core.querybuilder.Delete.Where; import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Update; import com.datastax.driver.core.querybuilder.Update.Assignments; @@ -851,32 +854,88 @@ public class MappingCassandraConverterUnitTests { mappingContext.getRequiredPersistentEntity(TypeWithMapId.class)); } + @Test // DATACASS-362 + public void shouldSelectCompositeIdUsingMapId() { + + Select select = QueryBuilder.select().from("foo"); + + MapId mapId = BasicMapId.id("firstname", "first").with("lastname", "last"); + + mappingCassandraConverter.write(mapId, select.where(), + mappingContext.getRequiredPersistentEntity(TypeWithMapId.class)); + + assertThat(select.toString()).isEqualTo("SELECT * FROM foo WHERE firstname='first' AND lastname='last';"); + } + + @Test // DATACASS-362 + public void shouldSelectCompositeIdUsingCompositeKeyClass() { + + Select select = QueryBuilder.select().from("foo"); + + CompositeKey key = new CompositeKey(); + key.setFirstname("first"); + key.setLastname("last"); + + mappingCassandraConverter.write(key, select.where(), + mappingContext.getRequiredPersistentEntity(TypeWithKeyClass.class)); + + assertThat(select.toString()).isEqualTo("SELECT * FROM foo WHERE first_name='first' AND lastname='last';"); + } + + @Test // DATACASS-362 + public void shouldSelectCompositeIdUsingCompositeKeyClassViaMapId() { + + Select select = QueryBuilder.select().from("foo"); + + MapId mapId = BasicMapId.id("firstname", "first").with("lastname", "last"); + + mappingCassandraConverter.write(mapId, select.where(), + mappingContext.getRequiredPersistentEntity(TypeWithKeyClass.class)); + + assertThat(select.toString()).isEqualTo("SELECT * FROM foo WHERE first_name='first' AND lastname='last';"); + } + + @Test // DATACASS-362 + public void shouldDeleteCompositeIdUsingCompositeKeyClass() { + + Delete delete = QueryBuilder.delete().from("foo"); + + CompositeKey key = new CompositeKey(); + key.setFirstname("first"); + key.setLastname("last"); + + mappingCassandraConverter.write(key, delete.where(), + mappingContext.getRequiredPersistentEntity(TypeWithKeyClass.class)); + + assertThat(delete.toString()).isEqualTo("DELETE FROM foo WHERE first_name='first' AND lastname='last';"); + } + @SuppressWarnings("unchecked") - private List getListValue(Insert statement) { + private static List getListValue(Insert statement) { List values = getValues(statement); return (List) values.stream().filter(value -> value instanceof List).findFirst().orElse(null); } @SuppressWarnings("unchecked") - private Set getSetValue(Insert statement) { + private static Set getSetValue(Insert statement) { List values = getValues(statement); return (Set) values.stream().filter(value -> value instanceof Set).findFirst().orElse(null); } @SuppressWarnings("unchecked") - private List getValues(Insert statement) { + private static List getValues(Insert statement) { return (List) ReflectionTestUtils.getField(statement, "values"); } @SuppressWarnings("unchecked") - private Collection getAssignmentValues(Update statement) { + private static Collection getAssignmentValues(Update statement) { return getAssignments(statement).values(); } @SuppressWarnings("unchecked") - private Map getAssignments(Update statement) { + private static Map getAssignments(Update statement) { Map result = new LinkedHashMap<>(); @@ -891,24 +950,24 @@ public class MappingCassandraConverterUnitTests { return result; } - private Collection getWhereValues(Update update) { + private static Collection getWhereValues(Update update) { return getWherePredicates(update.where()).values(); } - private Collection getWhereValues(BuiltStatement where) { + private static Collection getWhereValues(BuiltStatement where) { return getWherePredicates(where).values(); } - private Map getWherePredicates(Update statement) { + private static Map getWherePredicates(Update statement) { return getWherePredicates(statement.where()); } - private Map getWherePredicates(Delete statement) { + private static Map getWherePredicates(Delete statement) { return getWherePredicates(statement.where()); } @SuppressWarnings("unchecked") - private Map getWherePredicates(BuiltStatement where) { + private static Map getWherePredicates(BuiltStatement where) { Map result = new LinkedHashMap<>();