diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java index 67780bbca..9006b2e98 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java @@ -27,6 +27,7 @@ import java.util.stream.StreamSupport; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -39,20 +40,7 @@ import org.springframework.data.cassandra.SessionFactory; import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity; import org.springframework.data.cassandra.core.convert.CassandraConverter; import org.springframework.data.cassandra.core.convert.MappingCassandraConverter; -import org.springframework.data.cassandra.core.cql.AsyncCqlOperations; -import org.springframework.data.cassandra.core.cql.AsyncCqlTemplate; -import org.springframework.data.cassandra.core.cql.AsyncPreparedStatementCreator; -import org.springframework.data.cassandra.core.cql.AsyncResultSetExtractor; -import org.springframework.data.cassandra.core.cql.AsyncSessionCallback; -import org.springframework.data.cassandra.core.cql.CassandraAccessor; -import org.springframework.data.cassandra.core.cql.CqlProvider; -import org.springframework.data.cassandra.core.cql.PreparedStatementBinder; -import org.springframework.data.cassandra.core.cql.QueryExtractorDelegate; -import org.springframework.data.cassandra.core.cql.QueryOptions; -import org.springframework.data.cassandra.core.cql.RowCallbackHandler; -import org.springframework.data.cassandra.core.cql.RowMapper; -import org.springframework.data.cassandra.core.cql.SingleColumnRowMapper; -import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.data.cassandra.core.cql.*; import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory; import org.springframework.data.cassandra.core.cql.util.StatementBuilder; import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; @@ -244,6 +232,17 @@ public class AsyncCassandraTemplate return this.converter; } + /** + * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * + * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * @see StatementFactory + * @since 2.1 + */ + public StatementFactory getStatementFactory() { + return this.statementFactory; + } + /** * Returns whether this instance is configured to use {@link PreparedStatement prepared statements}. If enabled * (default), then all persistence methods (such as {@link #select}, {@link #update}, and others) will make use of @@ -300,17 +299,6 @@ public class AsyncCassandraTemplate return getEntityOperations().getRequiredPersistentEntity(entityType); } - /** - * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. - * - * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. - * @see StatementFactory - * @since 2.1 - */ - protected StatementFactory getStatementFactory() { - return this.statementFactory; - } - private CqlIdentifier getTableName(Class entityClass) { return getEntityOperations().getTableName(entityClass); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java index 9edec3837..e569f4b57 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java @@ -19,7 +19,6 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import org.springframework.data.cassandra.core.convert.CassandraConverter; -import org.springframework.data.cassandra.core.convert.UpdateMapper; import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity; @@ -66,7 +65,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations { * @param batchType must not be {@literal null}. * @since 3.2.6 */ - CassandraBatchTemplate(CassandraOperations operations, BatchType batchType) { + CassandraBatchTemplate(CassandraTemplate operations, BatchType batchType) { Assert.notNull(operations, "CassandraOperations must not be null"); Assert.notNull(batchType, "BatchType must not be null"); @@ -75,7 +74,7 @@ class CassandraBatchTemplate implements CassandraBatchOperations { this.batch = BatchStatement.builder(batchType); this.converter = operations.getConverter(); this.mappingContext = this.converter.getMappingContext(); - this.statementFactory = new StatementFactory(new UpdateMapper(converter)); + this.statementFactory = operations.getStatementFactory(); } /** diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index bf054131d..767dd4a07 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -23,6 +23,7 @@ import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -35,20 +36,7 @@ import org.springframework.data.cassandra.SessionFactory; import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity; import org.springframework.data.cassandra.core.convert.CassandraConverter; import org.springframework.data.cassandra.core.convert.MappingCassandraConverter; -import org.springframework.data.cassandra.core.convert.QueryMapper; -import org.springframework.data.cassandra.core.convert.UpdateMapper; -import org.springframework.data.cassandra.core.cql.CassandraAccessor; -import org.springframework.data.cassandra.core.cql.CqlOperations; -import org.springframework.data.cassandra.core.cql.CqlProvider; -import org.springframework.data.cassandra.core.cql.CqlTemplate; -import org.springframework.data.cassandra.core.cql.PreparedStatementBinder; -import org.springframework.data.cassandra.core.cql.PreparedStatementCreator; -import org.springframework.data.cassandra.core.cql.QueryExtractorDelegate; -import org.springframework.data.cassandra.core.cql.QueryOptions; -import org.springframework.data.cassandra.core.cql.RowMapper; -import org.springframework.data.cassandra.core.cql.SessionCallback; -import org.springframework.data.cassandra.core.cql.SingleColumnRowMapper; -import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.data.cassandra.core.cql.*; import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory; import org.springframework.data.cassandra.core.cql.util.StatementBuilder; import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; @@ -193,7 +181,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP this.converter = converter; this.cqlOperations = cqlOperations; this.entityOperations = new EntityOperations(converter); - this.statementFactory = new StatementFactory(new QueryMapper(converter), new UpdateMapper(converter)); + this.statementFactory = new StatementFactory(converter); this.eventDelegate = new EntityLifecycleEventDelegate(); } @@ -246,6 +234,17 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP return this.converter; } + /** + * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * + * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * @see org.springframework.data.cassandra.core.StatementFactory + * @since 2.1 + */ + public StatementFactory getStatementFactory() { + return this.statementFactory; + } + /** * Returns whether this instance is configured to use {@link PreparedStatement prepared statements}. If enabled * (default), then all persistence methods (such as {@link #select}, {@link #update}, and others) will make use of @@ -301,17 +300,6 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP return getEntityOperations().getRequiredPersistentEntity(entityType); } - /** - * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. - * - * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. - * @see org.springframework.data.cassandra.core.StatementFactory - * @since 2.1 - */ - protected StatementFactory getStatementFactory() { - return this.statementFactory; - } - @Override public CqlIdentifier getTableName(Class entityClass) { return getEntityOperations().getTableName(entityClass); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraBatchTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraBatchTemplate.java index a48715aa4..c4038606a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraBatchTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraBatchTemplate.java @@ -26,7 +26,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import org.springframework.data.cassandra.core.convert.CassandraConverter; -import org.springframework.data.cassandra.core.convert.UpdateMapper; import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity; @@ -74,7 +73,7 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations * @param batchType must not be {@literal null}. * @since 3.2.6 */ - ReactiveCassandraBatchTemplate(ReactiveCassandraOperations operations, BatchType batchType) { + ReactiveCassandraBatchTemplate(ReactiveCassandraTemplate operations, BatchType batchType) { Assert.notNull(operations, "CassandraOperations must not be null"); Assert.notNull(batchType, "BatchType must not be null"); @@ -83,7 +82,7 @@ class ReactiveCassandraBatchTemplate implements ReactiveCassandraBatchOperations this.batch = BatchStatement.builder(batchType); this.converter = operations.getConverter(); this.mappingContext = this.converter.getMappingContext(); - this.statementFactory = new StatementFactory(new UpdateMapper(converter)); + this.statementFactory = operations.getStatementFactory(); } private void assertNotExecuted() { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java index 2061f06d7..9bc523b99 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java @@ -27,6 +27,7 @@ import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -41,18 +42,7 @@ import org.springframework.data.cassandra.ReactiveSessionFactory; import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity; import org.springframework.data.cassandra.core.convert.CassandraConverter; import org.springframework.data.cassandra.core.convert.MappingCassandraConverter; -import org.springframework.data.cassandra.core.cql.CassandraAccessor; -import org.springframework.data.cassandra.core.cql.CqlProvider; -import org.springframework.data.cassandra.core.cql.PreparedStatementBinder; -import org.springframework.data.cassandra.core.cql.QueryExtractorDelegate; -import org.springframework.data.cassandra.core.cql.QueryOptions; -import org.springframework.data.cassandra.core.cql.ReactiveCqlOperations; -import org.springframework.data.cassandra.core.cql.ReactiveCqlTemplate; -import org.springframework.data.cassandra.core.cql.ReactivePreparedStatementCreator; -import org.springframework.data.cassandra.core.cql.ReactiveSessionCallback; -import org.springframework.data.cassandra.core.cql.RowMapper; -import org.springframework.data.cassandra.core.cql.SingleColumnRowMapper; -import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.data.cassandra.core.cql.*; import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessionFactory; import org.springframework.data.cassandra.core.cql.util.StatementBuilder; import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; @@ -253,6 +243,17 @@ public class ReactiveCassandraTemplate return this.converter; } + /** + * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * + * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * @see org.springframework.data.cassandra.core.StatementFactory + * @since 2.1 + */ + public StatementFactory getStatementFactory() { + return this.statementFactory; + } + /** * Returns whether this instance is configured to use {@link PreparedStatement prepared statements}. If enabled * (default), then all persistence methods (such as {@link #select}, {@link #update}, and others) will make use of @@ -309,17 +310,6 @@ public class ReactiveCassandraTemplate return getEntityOperations().getRequiredPersistentEntity(entityType); } - /** - * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. - * - * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. - * @see org.springframework.data.cassandra.core.StatementFactory - * @since 2.1 - */ - protected StatementFactory getStatementFactory() { - return this.statementFactory; - } - CqlIdentifier getTableName(Class entityClass) { return getRequiredPersistentEntity(entityClass).getTableName(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java index 8f0b31fce..1cdb02842 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java @@ -37,6 +37,7 @@ import org.springframework.data.cassandra.core.cql.QueryOptionsUtil.CqlStatement import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.data.cassandra.core.cql.util.StatementBuilder; import org.springframework.data.cassandra.core.cql.util.TermFactory; +import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity; import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty; import org.springframework.data.cassandra.core.mapping.PersistentPropertyTranslator; @@ -64,6 +65,7 @@ import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.projection.EntityProjection; import org.springframework.data.projection.ProjectionInformation; import org.springframework.data.util.Predicates; +import org.springframework.data.util.ProxyUtils; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -108,6 +110,8 @@ public class StatementFactory { private final UpdateMapper updateMapper; + private KeyspaceProvider keyspaceProvider = KeyspaceProviders.EMPTY_KEYSPACE; + /** * Create {@link StatementFactory} given {@link CassandraConverter}. * @@ -117,6 +121,7 @@ public class StatementFactory { public StatementFactory(CassandraConverter converter) { Assert.notNull(converter, "CassandraConverter must not be null"); + this.cassandraConverter = converter; UpdateMapper updateMapper = new UpdateMapper(converter); @@ -169,20 +174,34 @@ public class StatementFactory { return this.updateMapper; } + /** + * Sets the {@link KeyspaceProvider} to determine the {@link CqlIdentifier keyspace} for a + * {@link CassandraPersistentEntity entity}-related statement. + * + * @param keyspaceProvider the keyspace provider to use, must not be {@literal null}. + * @since 4.4 + */ + public void setKeyspaceProvider(KeyspaceProvider keyspaceProvider) { + + Assert.notNull(keyspaceProvider, "KeyspaceProvider must not be null"); + + this.keyspaceProvider = keyspaceProvider; + } + /** * Create a {@literal COUNT} statement by mapping {@link Query} to {@link Select}. * * @param query user-defined count {@link Query} to execute; must not be {@literal null}. - * @param persistentEntity {@link CassandraPersistentEntity entity} to count; must not be {@literal null}. + * @param entity {@link CassandraPersistentEntity entity} to count; must not be {@literal null}. * @return the select builder. * @since 2.1 */ - public StatementBuilder count(Query query, CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); - return count(query, persistentEntity, persistentEntity.getTableName()); + return count(query, entity, entity.getTableName()); } /** @@ -208,18 +227,18 @@ public class StatementFactory { * {@link UpdateOptions}. * * @param id must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @param tableName must not be {@literal null}. * @return the select builder. */ - public StatementBuilder selectOneById(Object id, CassandraPersistentEntity entity, CqlIdentifier tableName) { Where where = new Where(); - cassandraConverter.write(id, where, persistentEntity); + cassandraConverter.write(id, where, entity); - return StatementBuilder.of(QueryBuilder.selectFrom(tableName).all().limit(1)) + return StatementBuilder.of(QueryBuilder.selectFrom(getKeyspace(entity, tableName), tableName).all().limit(1)) .bind((statement, factory) -> statement.where(toRelations(where, factory))); } @@ -227,38 +246,37 @@ public class StatementFactory { * Create a {@literal SELECT} statement by mapping {@link Query} to {@link Select}. * * @param query must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @return the select builder. */ - public StatementBuilder select(Query query, CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); - return select(query, persistentEntity, persistentEntity.getTableName()); + return select(query, entity, entity.getTableName()); } /** * Create a {@literal SELECT} statement by mapping {@link Query} to {@link Select}. * * @param query must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @param tableName must not be {@literal null}. * @return the select builder. * @since 2.1 */ - public StatementBuilder select(Query query, CassandraPersistentEntity entity, CqlIdentifier tableName) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); - Assert.notNull(persistentEntity, "Table name must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "Table name must not be null"); - Filter filter = getQueryMapper().getMappedObject(query, persistentEntity); + Filter filter = getQueryMapper().getMappedObject(query, entity); - List selectors = getQueryMapper().getMappedSelectors(query.getColumns(), persistentEntity); + List selectors = getQueryMapper().getMappedSelectors(query.getColumns(), entity); - return createSelect(query, persistentEntity, filter, selectors, tableName); + return createSelect(query, entity, filter, selectors, tableName); } /** @@ -274,9 +292,9 @@ public class StatementFactory { Assert.notNull(objectToInsert, "Object to builder must not be null"); Assert.notNull(options, "WriteOptions must not be null"); - CassandraPersistentEntity persistentEntity = cassandraConverter.getMappingContext() + CassandraPersistentEntity entity = cassandraConverter.getMappingContext() .getRequiredPersistentEntity(objectToInsert.getClass()); - return insert(objectToInsert, options, persistentEntity, persistentEntity.getTableName()); + return insert(objectToInsert, options, entity, entity.getTableName()); } /** @@ -285,15 +303,15 @@ public class StatementFactory { * @param tableName the table name, must not be empty and not {@literal null}. * @param objectToInsert the object to save, must not be {@literal null}. * @param options optional {@link WriteOptions} to apply to the {@link Insert} statement, may be {@literal null}. - * @param persistentEntity the {@link CassandraPersistentEntity} to write insert values. + * @param entity the {@link CassandraPersistentEntity} to write insert values. * @return the select builder. */ public StatementBuilder insert(Object objectToInsert, WriteOptions options, - CassandraPersistentEntity persistentEntity, CqlIdentifier tableName) { + CassandraPersistentEntity entity, CqlIdentifier tableName) { Assert.notNull(tableName, "TableName must not be null"); Assert.notNull(objectToInsert, "Object to insert must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); boolean insertNulls; @@ -304,10 +322,11 @@ public class StatementFactory { } Map object = new LinkedHashMap<>(); - cassandraConverter.write(objectToInsert, object, persistentEntity); + cassandraConverter.write(objectToInsert, object, entity); StatementBuilder builder = StatementBuilder - .of(QueryBuilder.insertInto(tableName).valuesByIds(Collections.emptyMap())).bind((statement, factory) -> { + .of(QueryBuilder.insertInto(getKeyspace(entity, tableName), tableName).valuesByIds(Collections.emptyMap())) + .bind((statement, factory) -> { Map values = createTerms(insertNulls, object, factory); CqlStatementOptionsAccessor accessor = factory.ifBoundOrInline( @@ -342,17 +361,17 @@ public class StatementFactory { * Create an {@literal UPDATE} statement by mapping {@link Query} to {@link Update}. * * @param query must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @return the update builder. */ public StatementBuilder update(Query query, Update update, - CassandraPersistentEntity persistentEntity) { + CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); Assert.notNull(update, "Update must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); - return update(query, update, persistentEntity, persistentEntity.getTableName()); + return update(query, update, entity, entity.getTableName()); } /** @@ -360,25 +379,26 @@ public class StatementFactory { * * @param query must not be {@literal null}. * @param update must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @param tableName must not be {@literal null}. * @return the update builder. * @since 2.1 */ StatementBuilder update(Query query, Update update, - CassandraPersistentEntity persistentEntity, CqlIdentifier tableName) { + CassandraPersistentEntity entity, CqlIdentifier tableName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(update, "Update must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); Assert.notNull(tableName, "Table name must not be null"); - Filter filter = getQueryMapper().getMappedObject(query, persistentEntity); + Filter filter = getQueryMapper().getMappedObject(query, entity); - Update mappedUpdate = getUpdateMapper().getMappedObject(update, persistentEntity); + Update mappedUpdate = getUpdateMapper().getMappedObject(update, entity); - StatementBuilder builder = update(tableName, mappedUpdate, - filter, query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast)); + StatementBuilder builder = update(entity, tableName, + mappedUpdate, filter, + query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast)); query.getQueryOptions().filter(UpdateOptions.class::isInstance).map(UpdateOptions.class::cast) .map(UpdateOptions::getIfCondition) @@ -405,10 +425,10 @@ public class StatementFactory { Assert.notNull(objectToUpdate, "Object to builder must not be null"); Assert.notNull(options, "WriteOptions must not be null"); - CassandraPersistentEntity persistentEntity = cassandraConverter.getMappingContext() + CassandraPersistentEntity entity = cassandraConverter.getMappingContext() .getRequiredPersistentEntity(objectToUpdate.getClass()); - return update(objectToUpdate, options, persistentEntity, persistentEntity.getTableName()); + return update(objectToUpdate, options, entity, entity.getTableName()); } /** @@ -437,7 +457,7 @@ public class StatementFactory { where.forEach((cqlIdentifier, o) -> object.remove(cqlIdentifier)); StatementBuilder builder = StatementBuilder - .of(QueryBuilder.update(tableName).set().where()).bind((statement, factory) -> { + .of(QueryBuilder.update(getKeyspace(entity, tableName), tableName).set().where()).bind((statement, factory) -> { CqlStatementOptionsAccessor accessor = factory.ifBoundOrInline( bindings -> CqlStatementOptionsAccessor.ofUpdate(bindings, (UpdateStart) statement), @@ -462,18 +482,17 @@ public class StatementFactory { * {@link UpdateOptions}. * * @param id must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @param tableName must not be {@literal null}. * @return the delete builder. */ - public StatementBuilder deleteById(Object id, CassandraPersistentEntity persistentEntity, - CqlIdentifier tableName) { + public StatementBuilder deleteById(Object id, CassandraPersistentEntity entity, CqlIdentifier tableName) { Where where = new Where(); - cassandraConverter.write(id, where, persistentEntity); + cassandraConverter.write(id, where, entity); - return StatementBuilder.of(QueryBuilder.deleteFrom(tableName).where()) + return StatementBuilder.of(QueryBuilder.deleteFrom(getKeyspace(entity, tableName), tableName).where()) .bind((statement, factory) -> statement.where(toRelations(where, factory))); } @@ -481,37 +500,36 @@ public class StatementFactory { * Create a {@literal DELETE} statement by mapping {@link Query} to {@link Delete}. * * @param query must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @return the delete builder. */ - public StatementBuilder delete(Query query, CassandraPersistentEntity persistentEntity) { + public StatementBuilder delete(Query query, CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); - return delete(query, persistentEntity, persistentEntity.getTableName()); + return delete(query, entity, entity.getTableName()); } /** * Create a {@literal DELETE} statement by mapping {@link Query} to {@link Delete}. * * @param query must not be {@literal null}. - * @param persistentEntity must not be {@literal null}. + * @param entity must not be {@literal null}. * @param tableName must not be {@literal null}. * @return the delete builder. * @see 2.1 */ - public StatementBuilder delete(Query query, CassandraPersistentEntity persistentEntity, - CqlIdentifier tableName) { + public StatementBuilder delete(Query query, CassandraPersistentEntity entity, CqlIdentifier tableName) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); Assert.notNull(tableName, "Table name must not be null"); - Filter filter = getQueryMapper().getMappedObject(query, persistentEntity); - List columnNames = getQueryMapper().getMappedColumnNames(query.getColumns(), persistentEntity); + Filter filter = getQueryMapper().getMappedObject(query, entity); + List columnNames = getQueryMapper().getMappedColumnNames(query.getColumns(), entity); - StatementBuilder builder = delete(columnNames, tableName, filter, + StatementBuilder builder = delete(columnNames, entity, tableName, filter, query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast)); query.getQueryOptions().filter(DeleteOptions.class::isInstance).map(DeleteOptions.class::cast) @@ -543,8 +561,11 @@ public class StatementFactory { Where where = new Where(); entityWriter.write(entity, where); + BasicCassandraPersistentEntity persistentEntity = cassandraConverter.getMappingContext() + .getRequiredPersistentEntity(ProxyUtils.getUserClass(entity.getClass())); - StatementBuilder builder = StatementBuilder.of(QueryBuilder.deleteFrom(tableName).where()) + StatementBuilder builder = StatementBuilder + .of(QueryBuilder.deleteFrom(getKeyspace(persistentEntity, tableName), tableName).where()) .bind((statement, factory) -> { Delete statementToUse; @@ -595,9 +616,9 @@ public class StatementFactory { PersistentPropertyTranslator translator = PersistentPropertyTranslator.create(domainType, Predicates.negate(CassandraPersistentProperty::hasExplicitColumnName)); - CassandraPersistentEntity persistentEntity = getQueryMapper().getConverter().getMappingContext() + CassandraPersistentEntity entity = getQueryMapper().getConverter().getMappingContext() .getRequiredPersistentEntity(projection.getMappedType()); - for (CassandraPersistentProperty property : persistentEntity) { + for (CassandraPersistentProperty property : entity) { columns.include(translator.translate(property).getColumnName()); } } @@ -630,7 +651,7 @@ public class StatementFactory { Sort sort = Optional.of(query.getSort()).map(querySort -> getQueryMapper().getMappedSort(querySort, entity)) .orElse(Sort.unsorted()); - StatementBuilder select = createSelectAndOrder(selectors, entity, tableName, filter, sort); if (query.isAllowFiltering()) { select.apply(Select::allowFiltering); @@ -651,13 +672,18 @@ public class StatementFactory { return select; } - private static StatementBuilder createSelectAndOrder(List selectors, CassandraPersistentEntity entity, + CqlIdentifier from, Filter filter, Sort sort) { Select select; if (selectors.isEmpty()) { - select = QueryBuilder.selectFrom(from).all(); + select = QueryBuilder.selectFrom(getKeyspace(entity, from), from).all(); } else { List mappedSelectors = new ArrayList<>( @@ -668,7 +694,7 @@ public class StatementFactory { mappedSelectors.add(orElseGet); } - select = QueryBuilder.selectFrom(from).selectors(mappedSelectors); + select = QueryBuilder.selectFrom(getKeyspace(entity, from), from).selectors(mappedSelectors); } StatementBuilder select = statementFactory.select(Query.empty(), + converter.getMappingContext().getRequiredPersistentEntity(Group.class)); + + assertThat(select.build(ParameterHandling.INLINE).getQuery()).isEqualTo("SELECT * FROM ks_group.group"); + } + @Test // DATACASS-708 void selectShouldApplyQueryOptions() { @@ -98,7 +110,7 @@ class StatementFactoryUnitTests { @Test // DATACASS-343 void shouldMapSelectQueryWithColumnsAndCriteria() { - Query query = Query.query(Criteria.where("foo").is("bar")).columns(Columns.from("age")); + Query query = Query.query(where("foo").is("bar")).columns(Columns.from("age")); StatementBuilder select = statementFactory.select(query, groupEntity); @@ -118,7 +130,7 @@ class StatementFactoryUnitTests { @Test // DATACASS-549 void shouldMapSelectQueryIsNotNull() { - Query query = Query.query(Criteria.where("foo").isNotNull()).columns(Columns.from("age")); + Query query = Query.query(where("foo").isNotNull()).columns(Columns.from("age")); StatementBuilder select = statementFactory.select(query, converter.getMappingContext().getRequiredPersistentEntity(Group.class)); @@ -192,12 +204,12 @@ class StatementFactoryUnitTests { @Test // GH-1172 void shouldMapSelectInQueryAsInlineValue() { - StatementBuilder select = statementFactory.select(Query.query(where("foo").in("bar")), groupEntity); assertThat(select.build(ParameterHandling.INLINE).getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN ('bar')"); - select = statementFactory.select(Query.query(Criteria.where("foo").in("bar", "baz")), groupEntity); + select = statementFactory.select(Query.query(where("foo").in("bar", "baz")), groupEntity); assertThat(select.build(ParameterHandling.INLINE).getQuery()) .isEqualTo("SELECT * FROM group WHERE foo IN ('bar','baz')"); @@ -206,14 +218,14 @@ class StatementFactoryUnitTests { @Test // GH-1172 void shouldMapSelectInQueryAsByIndexValue() { - StatementBuilder select = statementFactory.select(Query.query(where("foo").in("bar")), groupEntity); SimpleStatement statement = select.build(ParameterHandling.BY_INDEX); assertThat(statement.getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN ?"); assertThat(statement.getPositionalValues()).containsOnly(Collections.singletonList("bar")); - select = statementFactory.select(Query.query(Criteria.where("foo").in("bar", "baz")), groupEntity); + select = statementFactory.select(Query.query(where("foo").in("bar", "baz")), groupEntity); statement = select.build(ParameterHandling.BY_INDEX); assertThat(statement.getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN ?"); @@ -223,7 +235,7 @@ class StatementFactoryUnitTests { @Test // GH-1172 void shouldMapSelectInQueryAsByNamedValue() { - StatementBuilder select = statementFactory.select(Query.query(where("foo").in("bar")), groupEntity); SimpleStatement statement = select.build(ParameterHandling.BY_NAME); @@ -231,7 +243,7 @@ class StatementFactoryUnitTests { assertThat(statement.getNamedValues()).hasSize(1).containsEntry(CqlIdentifier.fromCql("p0"), Collections.singletonList("bar")); - select = statementFactory.select(Query.query(Criteria.where("foo").in("bar", "baz")), groupEntity); + select = statementFactory.select(Query.query(where("foo").in("bar", "baz")), groupEntity); statement = select.build(ParameterHandling.BY_NAME); assertThat(statement.getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN :p0"); @@ -254,7 +266,7 @@ class StatementFactoryUnitTests { void shouldMapDeleteQueryWithTimestampColumns() { DeleteOptions options = DeleteOptions.builder().timestamp(1234).build(); - Query query = Query.query(Criteria.where("foo").is("bar")).queryOptions(options); + Query query = Query.query(where("foo").is("bar")).queryOptions(options); StatementBuilder delete = statementFactory.delete(query, converter.getMappingContext().getRequiredPersistentEntity(Group.class)); @@ -267,7 +279,7 @@ class StatementFactoryUnitTests { void deleteByQueryWithOptionsShouldRenderBindMarkers() { DeleteOptions options = DeleteOptions.builder().timestamp(1234).build(); - Query query = Query.query(Criteria.where("foo").is("bar")).queryOptions(options); + Query query = Query.query(where("foo").is("bar")).queryOptions(options); StatementBuilder delete = statementFactory.delete(query, converter.getMappingContext().getRequiredPersistentEntity(Group.class)); @@ -295,6 +307,33 @@ class StatementFactoryUnitTests { assertThat(statement.getPositionalValues()).containsExactly(1234L, "foo"); } + @Test // GH-1275 + void shouldConsiderKeyspaceForDeleteByEntity() { + + statementFactory.setKeyspaceProvider((entity, tableName) -> CqlIdentifier.fromCql("ks_" + tableName)); + + Person person = new Person(); + person.id = "foo"; + + StatementBuilder delete = statementFactory.delete(person, DeleteOptions.empty(), converter, + CqlIdentifier.fromCql("person")); + + SimpleStatement statement = delete.build(ParameterHandling.BY_INDEX); + + assertThat(statement.getQuery()).isEqualTo("DELETE FROM ks_person.person WHERE id=?"); + } + + @Test // GH-1275 + void shouldConsiderKeyspaceForDelete() { + + statementFactory.setKeyspaceProvider((entity, tableName) -> CqlIdentifier.fromCql("ks_" + tableName)); + StatementBuilder delete = statementFactory.delete(Query.query(where("foo").is("bar")), groupEntity); + + SimpleStatement statement = delete.build(ParameterHandling.BY_INDEX); + + assertThat(statement.getQuery()).isEqualTo("DELETE FROM ks_group.group WHERE foo=?"); + } + @Test // DATACASS-708 void deleteShouldApplyQueryOptions() { @@ -325,6 +364,20 @@ class StatementFactoryUnitTests { assertThat(insert.build(ParameterHandling.INLINE).getQuery()).isEqualTo("INSERT INTO person (id) VALUES ('foo')"); } + @Test // GH-1275 + void shouldConsiderKeyspaceForInsert() { + + statementFactory.setKeyspaceProvider((entity, tableName) -> CqlIdentifier.fromCql("ks_" + tableName)); + + Person person = new Person(); + person.id = "foo"; + + StatementBuilder insert = statementFactory.insert(person, WriteOptions.empty()); + + assertThat(insert.build(ParameterHandling.INLINE).getQuery()) + .isEqualTo("INSERT INTO ks_person.person (id) VALUES ('foo')"); + } + @Test // DATACASS-708 void insertShouldApplyQueryOptions() { @@ -415,7 +468,7 @@ class StatementFactoryUnitTests { @Test // DATACASS-343 void shouldCreateSetUpdate() { - Query query = Query.query(Criteria.where("foo").is("bar")); + Query query = Query.query(where("foo").is("bar")); StatementBuilder update = statementFactory.update(query, Update.empty().set("firstName", "baz").set("boo", "baa"), personEntity); @@ -424,11 +477,25 @@ class StatementFactoryUnitTests { .isEqualTo("UPDATE person SET first_name='baz', boo='baa' WHERE foo='bar'"); } + @Test // GH-1275 + void shouldConsiderKeyspaceForCreateSetUpdate() { + + statementFactory.setKeyspaceProvider((entity, tableName) -> CqlIdentifier.fromCql("ks_" + tableName)); + + Query query = Query.query(where("foo").is("bar")); + + StatementBuilder update = statementFactory.update(query, + Update.empty().set("firstName", "baz").set("boo", "baa"), personEntity); + + assertThat(update.build(ParameterHandling.INLINE).getQuery()) + .isEqualTo("UPDATE ks_person.person SET first_name='baz', boo='baa' WHERE foo='bar'"); + } + @Test // DATACASS-656 void shouldCreateSetUpdateWithTtl() { WriteOptions options = WriteOptions.builder().ttl(Duration.ofMinutes(1)).build(); - Query query = Query.query(Criteria.where("foo").is("bar")).queryOptions(options); + Query query = Query.query(where("foo").is("bar")).queryOptions(options); StatementBuilder update = statementFactory.update(query, Update.empty().set("firstName", "baz"), personEntity); @@ -441,7 +508,7 @@ class StatementFactoryUnitTests { void shouldCreateSetUpdateWithTimestamp() { WriteOptions options = WriteOptions.builder().timestamp(1234).build(); - Query query = Query.query(Criteria.where("foo").is("bar")).queryOptions(options); + Query query = Query.query(where("foo").is("bar")).queryOptions(options); StatementBuilder update = statementFactory.update(query, Update.empty().set("firstName", "baz"), personEntity); @@ -454,7 +521,7 @@ class StatementFactoryUnitTests { void updateWithOptionsShouldRenderBindMarker() { WriteOptions options = WriteOptions.builder().ttl(Duration.ofMinutes(1)).timestamp(1234).build(); - Query query = Query.query(Criteria.where("foo").is("bar")).queryOptions(options); + Query query = Query.query(where("foo").is("bar")).queryOptions(options); StatementBuilder update = statementFactory.update(query, Update.empty().set("firstName", "baz"), personEntity); @@ -626,7 +693,7 @@ class StatementFactoryUnitTests { @Test // DATACASS-569 void shouldCreateSetUpdateIfExists() { - Query query = Query.query(Criteria.where("foo").is("bar")) + Query query = Query.query(where("foo").is("bar")) .queryOptions(UpdateOptions.builder().withIfExists().build()); StatementBuilder update = statementFactory.update(query, @@ -639,8 +706,8 @@ class StatementFactoryUnitTests { @Test // DATACASS-656 void shouldCreateSetUpdateIfCondition() { - Query query = Query.query(Criteria.where("foo").is("bar")) - .queryOptions(UpdateOptions.builder().ifCondition(Criteria.where("foo").is("baz")).build()); + Query query = Query.query(where("foo").is("bar")) + .queryOptions(UpdateOptions.builder().ifCondition(where("foo").is("baz")).build()); StatementBuilder update = statementFactory.update(query, Update.empty().set("firstName", "baz"), personEntity); @@ -657,7 +724,7 @@ class StatementFactoryUnitTests { .serialConsistencyLevel(DefaultConsistencyLevel.QUORUM) // .build(); - Query query = Query.query(Criteria.where("foo").is("bar")).queryOptions(queryOptions); + Query query = Query.query(where("foo").is("bar")).queryOptions(queryOptions); StatementBuilder update = statementFactory.update(query, Update.empty().set("firstName", "baz"), personEntity); @@ -698,7 +765,7 @@ class StatementFactoryUnitTests { @Test // DATACASS-656 void shouldCreateSetUpdateFromObjectIfCondition() { - UpdateOptions options = UpdateOptions.builder().ifCondition(Criteria.where("foo").is("bar")).build(); + UpdateOptions options = UpdateOptions.builder().ifCondition(where("foo").is("bar")).build(); Person person = new Person(); person.id = "foo"; person.firstName = "bar"; @@ -774,7 +841,7 @@ class StatementFactoryUnitTests { @Test // DATACASS-512 void shouldCreateCountQuery() { - Query query = Query.query(Criteria.where("foo").is("bar")); + Query query = Query.query(where("foo").is("bar")); StatementBuilder