diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/DeleteOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/DeleteOptions.java index 6f96dea03..123cce888 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/DeleteOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/DeleteOptions.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.time.Instant; import java.util.concurrent.TimeUnit; +import org.springframework.data.cassandra.core.cql.ExecutionProfileResolver; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.data.cassandra.core.query.CriteriaDefinition; import org.springframework.data.cassandra.core.query.Filter; @@ -28,7 +29,6 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.oss.driver.api.core.ConsistencyLevel; -import com.datastax.oss.driver.api.core.retry.RetryPolicy; /** * Extension to {@link WriteOptions} for use with {@code DELETE} operations. @@ -45,11 +45,12 @@ public class DeleteOptions extends WriteOptions { private final @Nullable Filter ifCondition; - private DeleteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, - @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, - @Nullable Long timestamp, boolean ifExists, @Nullable Filter ifCondition) { + private DeleteOptions(@Nullable ConsistencyLevel consistencyLevel, ExecutionProfileResolver executionProfileResolver, + @Nullable Integer pageSize, @Nullable ConsistencyLevel serialConsistencyLevel, Duration timeout, Duration ttl, + @Nullable Long timestamp, @Nullable Boolean tracing, boolean ifExists, @Nullable Filter ifCondition) { - super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl, timestamp); + super(consistencyLevel, executionProfileResolver, pageSize, serialConsistencyLevel, timeout, ttl, timestamp, + tracing); this.ifExists = ifExists; this.ifCondition = ifCondition; @@ -129,13 +130,20 @@ public class DeleteOptions extends WriteOptions { } /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#retryPolicy(com.datastax.driver.core.policies.RetryPolicy) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(String) */ @Override - @Deprecated - public DeleteOptionsBuilder retryPolicy(RetryPolicy driverRetryPolicy) { + public DeleteOptionsBuilder executionProfile(String profileName) { + super.executionProfile(profileName); + return this; + } - super.retryPolicy(driverRetryPolicy); + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(org.springframework.data.cassandra.core.cql.ExecutionProfileResolver) + */ + @Override + public DeleteOptionsBuilder executionProfile(ExecutionProfileResolver executionProfileResolver) { + super.executionProfile(executionProfileResolver); return this; } @@ -182,6 +190,15 @@ public class DeleteOptions extends WriteOptions { return this; } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#serialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) + */ + @Override + public DeleteOptionsBuilder serialConsistencyLevel(ConsistencyLevel consistencyLevel) { + super.serialConsistencyLevel(consistencyLevel); + return this; + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#readTimeout(java.time.Duration) */ @@ -312,8 +329,9 @@ public class DeleteOptions extends WriteOptions { */ public DeleteOptions build() { - return new DeleteOptions(this.consistencyLevel, this.retryPolicy, this.tracing, this.pageSize, this.timeout, - this.ttl, this.timestamp, this.ifExists, this.ifCondition); + return new DeleteOptions(this.consistencyLevel, this.executionProfileResolver, this.pageSize, + this.serialConsistencyLevel, this.timeout, this.ttl, this.timestamp, this.tracing, this.ifExists, + this.ifCondition); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java index 27aba15ec..fb936e47d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java @@ -21,11 +21,11 @@ import java.time.Duration; import java.time.Instant; import java.util.concurrent.TimeUnit; +import org.springframework.data.cassandra.core.cql.ExecutionProfileResolver; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.lang.Nullable; import com.datastax.oss.driver.api.core.ConsistencyLevel; -import com.datastax.oss.driver.api.core.retry.RetryPolicy; /** * Extension to {@link WriteOptions} for use with {@code INSERT} operations. @@ -43,11 +43,12 @@ public class InsertOptions extends WriteOptions { private boolean insertNulls; - private InsertOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, - @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, - @Nullable Long timestamp, boolean ifNotExists, boolean insertNulls) { + private InsertOptions(@Nullable ConsistencyLevel consistencyLevel, ExecutionProfileResolver executionProfileResolver, + @Nullable Integer pageSize, @Nullable ConsistencyLevel serialConsistencyLevel, Duration timeout, Duration ttl, + @Nullable Long timestamp, @Nullable Boolean tracing, boolean ifNotExists, boolean insertNulls) { - super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl, timestamp); + super(consistencyLevel, executionProfileResolver, pageSize, serialConsistencyLevel, timeout, ttl, timestamp, + tracing); this.ifNotExists = ifNotExists; this.insertNulls = insertNulls; @@ -131,13 +132,20 @@ public class InsertOptions extends WriteOptions { } /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#retryPolicy(com.datastax.driver.core.policies.RetryPolicy) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(String) */ @Override - @Deprecated - public InsertOptionsBuilder retryPolicy(RetryPolicy driverRetryPolicy) { + public InsertOptionsBuilder executionProfile(String profileName) { + super.executionProfile(profileName); + return this; + } - super.retryPolicy(driverRetryPolicy); + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(org.springframework.data.cassandra.core.cql.ExecutionProfileResolver) + */ + @Override + public InsertOptionsBuilder executionProfile(ExecutionProfileResolver executionProfileResolver) { + super.executionProfile(executionProfileResolver); return this; } @@ -180,6 +188,15 @@ public class InsertOptions extends WriteOptions { return this; } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#serialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) + */ + @Override + public InsertOptionsBuilder serialConsistencyLevel(ConsistencyLevel consistencyLevel) { + super.serialConsistencyLevel(consistencyLevel); + return this; + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#readTimeout(java.time.Duration) */ @@ -274,8 +291,8 @@ public class InsertOptions extends WriteOptions { /** * Insert {@literal null} values from an entity. This allows the usage of {@code INSERT} statements as upsert by - * ensuring * that the whole entity state is persisted. Inserting {@literal null}s in Cassandra creates tombstones - * so this * option should be used with caution. + * ensuring that the whole entity state is persisted. Inserting {@literal null}s in Cassandra creates tombstones so + * this option should be used with caution. * * @return {@code this} {@link InsertOptionsBuilder} * @since 2.1 @@ -306,8 +323,9 @@ public class InsertOptions extends WriteOptions { * @return a new {@link InsertOptions} with the configured values */ public InsertOptions build() { - return new InsertOptions(this.consistencyLevel, this.retryPolicy, this.tracing, this.pageSize, this.timeout, - this.ttl, this.timestamp, this.ifNotExists, this.insertNulls); + return new InsertOptions(this.consistencyLevel, this.executionProfileResolver, this.pageSize, + this.serialConsistencyLevel, this.timeout, this.ttl, this.timestamp, this.tracing, this.ifNotExists, + this.insertNulls); } } } 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 65ea5115f..072301cfe 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 @@ -845,8 +845,8 @@ public class ReactiveCassandraTemplate if (getReactiveCqlOperations() instanceof CassandraAccessor) { CassandraAccessor accessor = (CassandraAccessor) getReactiveCqlOperations(); - if (accessor.getFetchSize() != -1) { - return Mono.just(accessor.getFetchSize()); + if (accessor.getPageSize() != -1) { + return Mono.just(accessor.getPageSize()); } } 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 771b73ae3..c58ed5bd8 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 @@ -315,9 +315,7 @@ public class StatementFactory { return statement.valuesByIds(values); }).apply(statement -> (RegularInsert) addWriteOptions(statement, options)); - builder.onBuild(statementBuilder -> { - QueryOptionsUtil.addQueryOptions(statementBuilder, options); - }); + builder.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, options)); return builder; } @@ -395,9 +393,7 @@ public class StatementFactory { }); query.getQueryOptions().ifPresent(options -> { - builder.onBuild(statementBuilder -> { - query.getQueryOptions().ifPresent(it -> QueryOptionsUtil.addQueryOptions(statementBuilder, it)); - }); + builder.transform(statementBuilder -> QueryOptionsUtil.addQueryOptions(statementBuilder, options)); }); return builder; @@ -462,9 +458,7 @@ public class StatementFactory { applyUpdateIfCondition(builder, criteriaDefinitions); }); - builder.onBuild(statementBuilder -> { - QueryOptionsUtil.addQueryOptions(statementBuilder, options); - }); + builder.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, options)); return builder; } @@ -542,9 +536,7 @@ public class StatementFactory { }); query.getQueryOptions().ifPresent(options -> { - builder.onBuild(statementBuilder -> { - query.getQueryOptions().ifPresent(it -> QueryOptionsUtil.addQueryOptions(statementBuilder, it)); - }); + builder.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, options)); }); return builder; @@ -588,9 +580,7 @@ public class StatementFactory { applyDeleteIfCondition(builder, criteriaDefinitions); }); - builder.onBuild(statementBuilder -> { - QueryOptionsUtil.addQueryOptions(statementBuilder, options); - }); + builder.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, options)); return builder; } @@ -651,7 +641,10 @@ public class StatementFactory { select.onBuild(statementBuilder -> { query.getPagingState().ifPresent(statementBuilder::setPagingState); - query.getQueryOptions().ifPresent(it -> QueryOptionsUtil.addQueryOptions(statementBuilder, it)); + }); + + query.getQueryOptions().ifPresent(it -> { + select.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, it)); }); return select; @@ -839,8 +832,7 @@ public class StatementFactory { Collection collection = (Collection) updateOp.getValue(); Assert.isTrue(collection.size() == 1, "RemoveOp must contain a single set element"); - return Assignment.removeSetElement(updateOp.toCqlIdentifier(), - termFactory.create(collection.iterator().next())); + return Assignment.removeSetElement(updateOp.toCqlIdentifier(), termFactory.create(collection.iterator().next())); } if (updateOp.getValue() instanceof List) { @@ -848,8 +840,7 @@ public class StatementFactory { Collection collection = (Collection) updateOp.getValue(); Assert.isTrue(collection.size() == 1, "RemoveOp must contain a single list element"); - return Assignment.removeListElement(updateOp.toCqlIdentifier(), - termFactory.create(collection.iterator().next())); + return Assignment.removeListElement(updateOp.toCqlIdentifier(), termFactory.create(collection.iterator().next())); } return Assignment.remove(updateOp.toCqlIdentifier(), termFactory.create(updateOp.getValue())); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java index ace6a8fa7..59c5cfa10 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/UpdateOptions.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.time.Instant; import java.util.concurrent.TimeUnit; +import org.springframework.data.cassandra.core.cql.ExecutionProfileResolver; import org.springframework.data.cassandra.core.cql.WriteOptions; import org.springframework.data.cassandra.core.query.CriteriaDefinition; import org.springframework.data.cassandra.core.query.Filter; @@ -28,7 +29,6 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.oss.driver.api.core.ConsistencyLevel; -import com.datastax.oss.driver.api.core.retry.RetryPolicy; /** * Extension to {@link WriteOptions} for use with {@code UPDATE} operations. @@ -46,11 +46,12 @@ public class UpdateOptions extends WriteOptions { private final @Nullable Filter ifCondition; - private UpdateOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, - @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, - @Nullable Long timestamp, boolean ifExists, @Nullable Filter ifCondition) { + private UpdateOptions(@Nullable ConsistencyLevel consistencyLevel, ExecutionProfileResolver executionProfileResolver, + @Nullable Integer pageSize, @Nullable ConsistencyLevel serialConsistencyLevel, Duration timeout, Duration ttl, + @Nullable Long timestamp, @Nullable Boolean tracing, boolean ifExists, @Nullable Filter ifCondition) { - super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl, timestamp); + super(consistencyLevel, executionProfileResolver, pageSize, serialConsistencyLevel, timeout, ttl, timestamp, + tracing); this.ifExists = ifExists; this.ifCondition = ifCondition; @@ -135,13 +136,20 @@ public class UpdateOptions extends WriteOptions { } /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#retryPolicy(com.datastax.driver.core.policies.RetryPolicy) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(String) */ @Override - @Deprecated - public UpdateOptionsBuilder retryPolicy(RetryPolicy driverRetryPolicy) { + public UpdateOptionsBuilder executionProfile(String profileName) { + super.executionProfile(profileName); + return this; + } - super.retryPolicy(driverRetryPolicy); + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(org.springframework.data.cassandra.core.cql.ExecutionProfileResolver) + */ + @Override + public UpdateOptionsBuilder executionProfile(ExecutionProfileResolver executionProfileResolver) { + super.executionProfile(executionProfileResolver); return this; } @@ -188,6 +196,15 @@ public class UpdateOptions extends WriteOptions { return this; } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#serialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) + */ + @Override + public UpdateOptionsBuilder serialConsistencyLevel(ConsistencyLevel consistencyLevel) { + super.serialConsistencyLevel(consistencyLevel); + return this; + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.core.cql.WriteOptions.WriteOptionsBuilder#readTimeout(java.time.Duration) */ @@ -320,8 +337,9 @@ public class UpdateOptions extends WriteOptions { */ public UpdateOptions build() { - return new UpdateOptions(this.consistencyLevel, this.retryPolicy, this.tracing, this.pageSize, this.timeout, - this.ttl, this.timestamp, this.ifExists, this.ifCondition); + return new UpdateOptions(this.consistencyLevel, this.executionProfileResolver, this.pageSize, + this.serialConsistencyLevel, this.timeout, this.ttl, this.timestamp, this.tracing, this.ifExists, + this.ifCondition); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java index 657ca3d4f..843ed641f 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/CassandraAccessor.java @@ -54,6 +54,12 @@ public class CassandraAccessor implements InitializingBean { private CqlExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator(); + /** + * If this variable is set to a value, it will be used for setting the {@code executionProfile} property on statements + * used for query processing. + */ + private ExecutionProfileResolver executionProfileResolver = ExecutionProfileResolver.none(); + /** * If this variable is set to a non-negative value, it will be used for setting the {@code pageSize} property on * statements used for query processing. @@ -67,10 +73,10 @@ public class CassandraAccessor implements InitializingBean { private @Nullable ConsistencyLevel consistencyLevel; /** - * If this variable is set to a value, it will be used for setting the {@code retryPolicy} property on statements used - * for query processing. + * If this variable is set to a value, it will be used for setting the {@code serial consistencyLevel} property on + * statements used for query processing. */ - private @Deprecated @Nullable RetryPolicy retryPolicy; + private @Nullable ConsistencyLevel serialConsistencyLevel; private @Nullable SessionFactory sessionFactory; @@ -127,6 +133,40 @@ public class CassandraAccessor implements InitializingBean { return this.exceptionTranslator; } + /** + * Set the driver execution profile for this template. + * + * @see Statement#setExecutionProfileName(String) + * @see ExecutionProfileResolver + * @since 3.0 + */ + public void setExecutionProfile(String profileName) { + setExecutionProfileResolver(ExecutionProfileResolver.from(profileName)); + + } + + /** + * Set the {@link ExecutionProfileResolver} for this template. + * + * @see com.datastax.oss.driver.api.core.config.DriverExecutionProfile + * @see ExecutionProfileResolver + * @since 3.0 + */ + public void setExecutionProfileResolver(ExecutionProfileResolver executionProfileResolver) { + + Assert.notNull(executionProfileResolver, "ExecutionProfileResolver must not be null"); + + this.executionProfileResolver = executionProfileResolver; + } + + /** + * @return the {@link ExecutionProfileResolver} specified for this template. + * @since 3.0 + */ + public ExecutionProfileResolver getExecutionProfileResolver() { + return executionProfileResolver; + } + /** * Set the fetch size for this template. This is important for processing large result sets: Setting this higher than * the default value will increase processing speed at the cost of memory consumption; setting this lower can avoid @@ -170,24 +210,23 @@ public class CassandraAccessor implements InitializingBean { } /** - * Set the retry policy for this template. This is important for defining behavior when a request fails. + * Set the serial consistency level for this template. * - * @see RetryPolicy - * @deprecated since 3.0. Use driver execution profiles instead. + * @since 3.0 + * @see Statement#setSerialConsistencyLevel(ConsistencyLevel) + * @see ConsistencyLevel */ - @Deprecated - public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) { - this.retryPolicy = retryPolicy; + public void setSerialConsistencyLevel(@Nullable ConsistencyLevel consistencyLevel) { + this.serialConsistencyLevel = consistencyLevel; } /** - * @return the {@link RetryPolicy} specified for this template. - * @deprecated since 3.0. Use driver execution profiles instead. + * @return the serial {@link ConsistencyLevel} specified for this template. + * @since 3.0 */ @Nullable - @Deprecated - public RetryPolicy getRetryPolicy() { - return this.retryPolicy; + public ConsistencyLevel getSerialConsistencyLevel() { + return this.serialConsistencyLevel; } /** @@ -263,22 +302,33 @@ public class CassandraAccessor implements InitializingBean { * Prepare the given CQL Statement applying statement settings such as page size and consistency level. * * @param statement the CQL Statement to prepare - * @see #setPageSize(int) * @see #setConsistencyLevel(ConsistencyLevel) + * @see #setSerialConsistencyLevel(ConsistencyLevel) + * @see #setPageSize(int) + * @see #setExecutionProfile(String) + * @see #setExecutionProfileResolver(ExecutionProfileResolver) */ protected Statement applyStatementSettings(Statement statement) { Statement statementToUse = statement; ConsistencyLevel consistencyLevel = getConsistencyLevel(); + ConsistencyLevel serialConsistencyLevel = getSerialConsistencyLevel(); + int pageSize = getPageSize(); - if (getFetchSize() > -1 && statement.getPageSize() < 0) { - statementToUse = statementToUse.setPageSize(getFetchSize()); + if (consistencyLevel != null) { + statementToUse = statementToUse.setConsistencyLevel(consistencyLevel); } - if (consistencyLevel != null && statementToUse.getConsistencyLevel() == null) { - statementToUse = statementToUse.setConsistencyLevel(getConsistencyLevel()); + if (serialConsistencyLevel != null) { + statementToUse = statementToUse.setSerialConsistencyLevel(serialConsistencyLevel); } + if (pageSize > -1) { + statementToUse = statementToUse.setPageSize(pageSize); + } + + statementToUse = getExecutionProfileResolver().apply(statementToUse); + return statementToUse; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExecutionProfileResolver.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExecutionProfileResolver.java new file mode 100644 index 000000000..0a57e2545 --- /dev/null +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ExecutionProfileResolver.java @@ -0,0 +1,78 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.cassandra.core.cql; + +import org.springframework.util.Assert; + +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.Statement; + +/** + * Resolver for a {@link com.datastax.oss.driver.api.core.config.DriverExecutionProfile} used with + * {@link com.datastax.oss.driver.api.core.cql.Statement#setExecutionProfileName(String)} or + * {@link com.datastax.oss.driver.api.core.cql.Statement#setExecutionProfile(DriverExecutionProfile)}. + * + * @author Mark Paluch + * @since 3.0 + */ +@FunctionalInterface +public interface ExecutionProfileResolver { + + /** + * Apply an execution profile based on the {@link Statement}. + * + * @param statement the statement to inspect and to apply the {@code driver execution profile} to. + * @return the statement with the profile applied. + */ + Statement apply(Statement statement); + + /** + * Create a no-op {@link ExecutionProfileResolver} that preserves the {@link Statement} settings. + * + * @return no-op {@link ExecutionProfileResolver} that preserves the {@link Statement} settings. + */ + static ExecutionProfileResolver none() { + return statement -> statement; + } + + /** + * Create a {@link ExecutionProfileResolver} from a {@link DriverExecutionProfile} to apply the profile object. + * + * @param driverExecutionProfile must not be {@literal null}. + * @return a {@link ExecutionProfileResolver} that applies the given {@link DriverExecutionProfile}. + * @see Statement#setExecutionProfile(DriverExecutionProfile) + */ + static ExecutionProfileResolver from(DriverExecutionProfile driverExecutionProfile) { + + Assert.notNull(driverExecutionProfile, "DriverExecutionProfile must not be null"); + + return statement -> statement.setExecutionProfile(driverExecutionProfile); + } + + /** + * Create a {@link ExecutionProfileResolver} from a {@code profileName}. + * + * @param profileName must not be {@literal null} or empty. + * @return a {@link ExecutionProfileResolver} that applies the given {@code profileName}. + * @see Statement#setExecutionProfileName(String) + */ + static ExecutionProfileResolver from(String profileName) { + + Assert.hasText(profileName, "DriverExecutionProfile name must not be empty"); + + return statement -> statement.setExecutionProfileName(profileName); + } +} diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java index b21b3ac5e..f189911c2 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptions.java @@ -24,7 +24,7 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.oss.driver.api.core.ConsistencyLevel; -import com.datastax.oss.driver.api.core.retry.RetryPolicy; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; /** * Cassandra Query Options for queries. {@link QueryOptions} allow tuning of various query options on a per-request @@ -38,41 +38,28 @@ public class QueryOptions { private static final QueryOptions EMPTY = QueryOptions.builder().build(); - private final @Nullable Boolean tracing; - private final @Nullable ConsistencyLevel consistencyLevel; - private final Duration readTimeout; + private final ExecutionProfileResolver executionProfileResolver; private final @Nullable Integer pageSize; - private final @Nullable RetryPolicy retryPolicy; + private final @Nullable ConsistencyLevel serialConsistencyLevel; - protected QueryOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, - @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout) { + private final Duration timeout; + + private final @Nullable Boolean tracing; + + protected QueryOptions(@Nullable ConsistencyLevel consistencyLevel, ExecutionProfileResolver executionProfileResolver, + @Nullable Integer pageSize, @Nullable ConsistencyLevel serialConsistencyLevel, Duration timeout, + @Nullable Boolean tracing) { this.consistencyLevel = consistencyLevel; - this.retryPolicy = retryPolicy; + this.executionProfileResolver = executionProfileResolver; + this.pageSize = pageSize; + this.serialConsistencyLevel = serialConsistencyLevel; + this.timeout = timeout; this.tracing = tracing; - this.pageSize = fetchSize; - this.readTimeout = readTimeout; - } - - /** - * Creates new {@link QueryOptions} for the given {@link ConsistencyLevel} and {@link RetryPolicy}. - * - * @param consistencyLevel the consistency level, may be {@literal null}. - * @param retryPolicy the retry policy, may be {@literal null}. - * @deprecated since 2.0, use {@link #builder()}. - */ - @Deprecated - public QueryOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy) { - - this.consistencyLevel = consistencyLevel; - this.retryPolicy = retryPolicy; - this.tracing = false; - this.pageSize = null; - this.readTimeout = Duration.ofMillis(-1); } /** @@ -106,7 +93,7 @@ public class QueryOptions { } /** - * @return the the driver {@link ConsistencyLevel} + * @return the the driver {@link ConsistencyLevel}. * @since 1.5 */ @Nullable @@ -114,6 +101,14 @@ public class QueryOptions { return this.consistencyLevel; } + /** + * @return the the {@link ExecutionProfileResolver}. + * @since 3.0 + */ + protected ExecutionProfileResolver getExecutionProfileResolver() { + return this.executionProfileResolver; + } + /** * @return the number of rows to fetch per chunking request. May be {@literal null} if not set. * @since 1.5 @@ -124,20 +119,33 @@ public class QueryOptions { } /** - * @return the read timeout in milliseconds. May be {@literal null} if not set. + * @return the command timeout. May be {@link Duration#isNegative() negative} if not set. * @since 1.5 + * @see com.datastax.oss.driver.api.core.cql.Statement#setTimeout(Duration) + * @deprecated since 3.0, use {@link #getTimeout()} instead. */ + @Deprecated protected Duration getReadTimeout() { - return this.readTimeout; + return getTimeout(); } /** - * @return the driver {@link RetryPolicy} - * @since 1.5 + * @return the command timeout. May be {@link Duration#isNegative() negative} if not set. + * @since 3.0 + * @see com.datastax.oss.driver.api.core.cql.Statement#setTimeout(Duration) + */ + protected Duration getTimeout() { + return this.timeout; + } + + /** + * @return the the serial {@link ConsistencyLevel}. + * @since 3.0 + * @see com.datastax.oss.driver.api.core.cql.Statement#setSerialConsistencyLevel(ConsistencyLevel) */ @Nullable - protected RetryPolicy getRetryPolicy() { - return this.retryPolicy; + protected ConsistencyLevel getSerialConsistencyLevel() { + return this.serialConsistencyLevel; } /** @@ -156,24 +164,27 @@ public class QueryOptions { */ public static class QueryOptionsBuilder { - protected @Nullable Boolean tracing; - protected @Nullable ConsistencyLevel consistencyLevel; - protected Duration timeout = Duration.ofMillis(-1); + protected ExecutionProfileResolver executionProfileResolver = ExecutionProfileResolver.none(); protected @Nullable Integer pageSize; - protected @Nullable RetryPolicy retryPolicy; + protected @Nullable ConsistencyLevel serialConsistencyLevel; + + protected Duration timeout = Duration.ofMillis(-1); + + protected @Nullable Boolean tracing; QueryOptionsBuilder() {} QueryOptionsBuilder(QueryOptions queryOptions) { this.consistencyLevel = queryOptions.consistencyLevel; + this.executionProfileResolver = queryOptions.executionProfileResolver; this.pageSize = queryOptions.pageSize; - this.timeout = queryOptions.readTimeout; - this.retryPolicy = queryOptions.retryPolicy; + this.serialConsistencyLevel = queryOptions.serialConsistencyLevel; + this.timeout = queryOptions.timeout; this.tracing = queryOptions.tracing; } @@ -193,19 +204,29 @@ public class QueryOptions { } /** - * Sets the {@link RetryPolicy driver RetryPolicy} to use. Setting both ( {@link RetryPolicy} and {@link RetryPolicy - * driver RetryPolicy}) retry policies is not supported. + * Sets the {@code execution profile} to use. * - * @param retryPolicy must not be {@literal null}. + * @param profileName must not be {@literal null} or empty. * @return {@code this} {@link QueryOptionsBuilder} - * @deprecated since 3.0, use execution profiles instead. + * @since 3.0 + * @see com.datastax.oss.driver.api.core.cql.Statement#setExecutionProfileName(String) */ - @Deprecated - public QueryOptionsBuilder retryPolicy(RetryPolicy retryPolicy) { + public QueryOptionsBuilder executionProfile(String profileName) { + return executionProfile(ExecutionProfileResolver.from(profileName)); + } - Assert.notNull(retryPolicy, "RetryPolicy must not be null"); + /** + * Sets the {@link ExecutionProfileResolver} to use. + * + * @param executionProfileResolver must not be {@literal null}. + * @return {@code this} {@link QueryOptionsBuilder} + * @see com.datastax.oss.driver.api.core.cql.Statement#setExecutionProfile(DriverExecutionProfile) + */ + public QueryOptionsBuilder executionProfile(ExecutionProfileResolver executionProfileResolver) { - this.retryPolicy = retryPolicy; + Assert.notNull(executionProfileResolver, "ExecutionProfileResolver must not be null"); + + this.executionProfileResolver = executionProfileResolver; return this; } @@ -301,6 +322,21 @@ public class QueryOptions { return this; } + /** + * Sets the serial {@link ConsistencyLevel} to use. + * + * @param consistencyLevel must not be {@literal null}. + * @return {@code this} {@link QueryOptionsBuilder} + */ + public QueryOptionsBuilder serialConsistencyLevel(ConsistencyLevel consistencyLevel) { + + Assert.notNull(consistencyLevel, "Serial ConsistencyLevel must not be null"); + + this.serialConsistencyLevel = consistencyLevel; + + return this; + } + /** * Sets the request timeout. Overrides the default timeout. * @@ -347,7 +383,8 @@ public class QueryOptions { * @return a new {@link QueryOptions} with the configured values */ public QueryOptions build() { - return new QueryOptions(this.consistencyLevel, this.retryPolicy, this.tracing, this.pageSize, this.timeout); + return new QueryOptions(this.consistencyLevel, this.executionProfileResolver, this.pageSize, + this.serialConsistencyLevel, this.timeout, this.tracing); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java index 7402cf9f5..bdbfdb997 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/QueryOptionsUtil.java @@ -19,7 +19,6 @@ import java.time.Duration; import org.springframework.util.Assert; -import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder; import com.datastax.oss.driver.api.core.cql.Statement; import com.datastax.oss.driver.api.querybuilder.delete.Delete; import com.datastax.oss.driver.api.querybuilder.delete.DeleteSelection; @@ -52,12 +51,18 @@ public abstract class QueryOptionsUtil { statementToUse = statementToUse.setConsistencyLevel(queryOptions.getConsistencyLevel()); } + statementToUse = queryOptions.getExecutionProfileResolver().apply(statementToUse); + if (queryOptions.getPageSize() != null) { statementToUse = statementToUse.setPageSize(queryOptions.getPageSize()); } - if (!queryOptions.getReadTimeout().isNegative()) { - statementToUse = statementToUse.setTimeout(queryOptions.getReadTimeout()); + if (queryOptions.getSerialConsistencyLevel() != null) { + statementToUse = statementToUse.setSerialConsistencyLevel(queryOptions.getSerialConsistencyLevel()); + } + + if (!queryOptions.getTimeout().isNegative()) { + statementToUse = statementToUse.setTimeout(queryOptions.getTimeout()); } if (queryOptions.getTracing() != null) { @@ -71,37 +76,6 @@ public abstract class QueryOptionsUtil { return (T) statementToUse; } - /** - * Add common {@link QueryOptions} to all types of queries. - * - * @param statement a {@link SimpleStatementBuilder}, must not be {@literal null}. - * @param queryOptions query options (e.g. consistency level) to add to the CQL statement. - */ - public static void addQueryOptions(SimpleStatementBuilder statementBuilder, QueryOptions queryOptions) { - - Assert.notNull(statementBuilder, "SimpleStatementBuilder must not be null"); - - if (queryOptions.getConsistencyLevel() != null) { - statementBuilder.setConsistencyLevel(queryOptions.getConsistencyLevel()); - } - - if (queryOptions.getPageSize() != null) { - statementBuilder.setPageSize(queryOptions.getPageSize()); - } - - if (!queryOptions.getReadTimeout().isNegative()) { - statementBuilder.setTimeout(queryOptions.getReadTimeout()); - } - - if (queryOptions.getTracing() != null) { - if (queryOptions.getTracing()) { - statementBuilder.setTracing(true); - } else { - statementBuilder.setTracing(false); - } - } - } - /** * Add common {@link WriteOptions} options to {@link Insert} CQL statements. * diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java index 16d7b44af..6f3bc1837 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.java @@ -87,18 +87,24 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re */ private int pageSize = -1; - /** - * If this variable is set to a value, it will be used for setting the {@code retryPolicy} property on statements used - * for query processing. - */ - private @Deprecated @Nullable RetryPolicy retryPolicy; - /** * If this variable is set to a value, it will be used for setting the {@code consistencyLevel} property on statements * used for query processing. */ private @Nullable ConsistencyLevel consistencyLevel; + /** + * If this variable is set to a value, it will be used for setting the {@code executionProfile} property on statements + * used for query processing. + */ + private ExecutionProfileResolver executionProfileResolver = ExecutionProfileResolver.none(); + + /** + * If this variable is set to a value, it will be used for setting the serial {@code consistencyLevel} property on + * statements used for query processing. + */ + private @Nullable ConsistencyLevel serialConsistencyLevel; + /** * Construct a new {@link ReactiveCqlTemplate Note: The {@link ReactiveSessionFactory} has to be set before using the * instance. @@ -154,6 +160,40 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re return consistencyLevel; } + /** + * Set the driver execution profile for this template. + * + * @see Statement#setExecutionProfileName(String) + * @see ExecutionProfileResolver + * @since 3.0 + */ + public void setExecutionProfile(String profileName) { + setExecutionProfileResolver(ExecutionProfileResolver.from(profileName)); + + } + + /** + * Set the {@link ExecutionProfileResolver} for this template. + * + * @see com.datastax.oss.driver.api.core.config.DriverExecutionProfile + * @see ExecutionProfileResolver + * @since 3.0 + */ + public void setExecutionProfileResolver(ExecutionProfileResolver executionProfileResolver) { + + Assert.notNull(executionProfileResolver, "ExecutionProfileResolver must not be null"); + + this.executionProfileResolver = executionProfileResolver; + } + + /** + * @return the {@link ExecutionProfileResolver} specified for this {@link ReactiveCqlTemplate}. + * @since 3.0 + */ + public ExecutionProfileResolver getExecutionProfileResolver() { + return executionProfileResolver; + } + /** * Set the fetch size for this template. This is important for processing large result sets: Setting this higher than * the default value will increase processing speed at the cost of memory consumption; setting this lower can avoid @@ -197,24 +237,23 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re } /** - * Set the retry policy for this template. This is important for defining behavior when a request fails. + * Set the serial consistency level for this template. * - * @see RetryPolicy - * @deprecated since 3.0. Use driver execution profiles instead. + * @since 3.0 + * @see Statement#setSerialConsistencyLevel(ConsistencyLevel) + * @see ConsistencyLevel */ - @Deprecated - public void setRetryPolicy(@Nullable RetryPolicy retryPolicy) { - this.retryPolicy = retryPolicy; + public void setSerialConsistencyLevel(@Nullable ConsistencyLevel consistencyLevel) { + this.serialConsistencyLevel = consistencyLevel; } /** - * @return the {@link RetryPolicy} specified for this template. - * @deprecated since 3.0. Use driver execution profiles instead. + * @return the serial {@link ConsistencyLevel} specified for this template. + * @since 3.0 */ @Nullable - @Deprecated - public RetryPolicy getRetryPolicy() { - return this.retryPolicy; + public ConsistencyLevel getSerialConsistencyLevel() { + return this.serialConsistencyLevel; } // ------------------------------------------------------------------------- @@ -778,22 +817,33 @@ public class ReactiveCqlTemplate extends ReactiveCassandraAccessor implements Re * Prepare the given CQL Statement applying statement settings such as page size and consistency level. * * @param stmt the CQL Statement to prepare - * @see #setPageSize(int) * @see #setConsistencyLevel(ConsistencyLevel) + * @see #setSerialConsistencyLevel(ConsistencyLevel) + * @see #setPageSize(int) + * @see #setExecutionProfile(String) + * @see #setExecutionProfileResolver(ExecutionProfileResolver) */ protected Statement applyStatementSettings(Statement statement) { Statement statementToUse = statement; ConsistencyLevel consistencyLevel = getConsistencyLevel(); + ConsistencyLevel serialConsistencyLevel = getSerialConsistencyLevel(); + int pageSize = getPageSize(); - if (getFetchSize() > -1 && statement.getPageSize() < 0) { - statementToUse = statementToUse.setPageSize(getFetchSize()); + if (consistencyLevel != null) { + statementToUse = statementToUse.setConsistencyLevel(consistencyLevel); } - if (consistencyLevel != null && statementToUse.getConsistencyLevel() == null) { - statementToUse = statementToUse.setConsistencyLevel(getConsistencyLevel()); + if (serialConsistencyLevel != null) { + statementToUse = statementToUse.setSerialConsistencyLevel(serialConsistencyLevel); } + if (pageSize > -1) { + statementToUse = statementToUse.setPageSize(pageSize); + } + + statementToUse = getExecutionProfileResolver().apply(statementToUse); + return statementToUse; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java index 7c3143fc4..d4bfbca24 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/WriteOptions.java @@ -25,10 +25,9 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.datastax.oss.driver.api.core.ConsistencyLevel; -import com.datastax.oss.driver.api.core.retry.RetryPolicy; /** - * Cassandra Write Options are an extension to {@link QueryOptions} for write operations. {@link WriteOptions}allow + * Cassandra Write Options are an extension to {@link QueryOptions} for write operations. {@link WriteOptions} allow * tuning of various query options on a per-request level. Only options that are set are applied to queries. * * @author David Webb @@ -44,41 +43,11 @@ public class WriteOptions extends QueryOptions { private final Duration ttl; private final @Nullable Long timestamp; - /** - * Creates new {@link WriteOptions} for the given {@link ConsistencyLevel} and {@link RetryPolicy}. - * - * @param consistencyLevel the consistency level, may be {@literal null}. - * @param retryPolicy the retry policy, may be {@literal null}. - * @deprecated since 2.0, use {@link #builder()} or {@link #empty()}. - */ - @Deprecated - public WriteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy) { - this(consistencyLevel, retryPolicy, null); - } + protected WriteOptions(@Nullable ConsistencyLevel consistencyLevel, ExecutionProfileResolver executionProfileResolver, + @Nullable Integer pageSize, @Nullable ConsistencyLevel serialConsistencyLevel, Duration timeout, Duration ttl, + @Nullable Long timestamp, @Nullable Boolean tracing) { - /** - * Creates new {@link WriteOptions} for the given {@link ConsistencyLevel}, {@link RetryPolicy} and {@code ttl}. - * - * @param consistencyLevel the consistency level, may be {@literal null}. - * @param retryPolicy the retry policy, may be {@literal null}. - * @param ttl the ttl, may be {@literal null}. - * @deprecated since 2.0, use {@link #builder()}. - */ - @Deprecated - public WriteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, - @Nullable Integer ttl) { - - super(consistencyLevel, retryPolicy); - - this.ttl = ttl == null ? Duration.ofMillis(-1) : Duration.ofSeconds(ttl); - this.timestamp = null; - } - - protected WriteOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, - @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, - @Nullable Long timestamp) { - - super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout); + super(consistencyLevel, executionProfileResolver, pageSize, serialConsistencyLevel, timeout, tracing); this.ttl = ttl; this.timestamp = timestamp; @@ -154,7 +123,7 @@ public class WriteOptions extends QueryOptions { } /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#consistencyLevel(com.datastax.driver.core.ConsistencyLevel) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#consistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) */ @Override public WriteOptionsBuilder consistencyLevel(ConsistencyLevel consistencyLevel) { @@ -164,13 +133,20 @@ public class WriteOptions extends QueryOptions { } /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#retryPolicy(com.datastax.oss.driver.api.core.retry.RetryPolicy) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(String) */ @Override - @Deprecated - public WriteOptionsBuilder retryPolicy(RetryPolicy driverRetryPolicy) { + public WriteOptionsBuilder executionProfile(String profileName) { + super.executionProfile(profileName); + return this; + } - super.retryPolicy(driverRetryPolicy); + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#executionProfile(org.springframework.data.cassandra.core.cql.ExecutionProfileResolver) + */ + @Override + public WriteOptionsBuilder executionProfile(ExecutionProfileResolver executionProfileResolver) { + super.executionProfile(executionProfileResolver); return this; } @@ -217,6 +193,15 @@ public class WriteOptions extends QueryOptions { return this; } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#serialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) + */ + @Override + public WriteOptionsBuilder serialConsistencyLevel(ConsistencyLevel consistencyLevel) { + super.serialConsistencyLevel(consistencyLevel); + return this; + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.core.cql.QueryOptions.QueryOptionsBuilder#readTimeout(java.time.Duration) */ @@ -318,8 +303,8 @@ public class WriteOptions extends QueryOptions { * @return a new {@link WriteOptions} with the configured values */ public WriteOptions build() { - return new WriteOptions(this.consistencyLevel, this.retryPolicy, this.tracing, this.pageSize, this.timeout, - this.ttl, this.timestamp); + return new WriteOptions(this.consistencyLevel, this.executionProfileResolver, this.pageSize, + this.serialConsistencyLevel, this.timeout, this.ttl, this.timestamp, this.tracing); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/util/StatementBuilder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/util/StatementBuilder.java index 034498a5a..9a88d616b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/util/StatementBuilder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/util/StatementBuilder.java @@ -74,6 +74,7 @@ public class StatementBuilder { private List> queryActions = new ArrayList<>(); private List> onBuild = new ArrayList<>(); + private List> onBuilt = new ArrayList<>(); private StatementBuilder(S statement) { this.statement = statement; @@ -138,6 +139,22 @@ public class StatementBuilder { return this; } + /** + * Add behavior after the {@link SimpleStatement} has been built. The {@link UnaryOperator} gets invoked with a + * {@link SimpleStatement} allowing association of the final statement with additional settings. The + * {@link UnaryOperator} is applied on {@link #build()}. + * + * @param mappingFunction the {@link UnaryOperator} function that gets notified on {@link #build()}. + * @return {@code this} {@link StatementBuilder}. + */ + public StatementBuilder transform(UnaryOperator mappingFunction) { + + Assert.notNull(mappingFunction, "Mapping function must not be null"); + + onBuilt.add(mappingFunction); + return this; + } + /** * Build a {@link SimpleStatement statement} by applying builder and bind functions using the default * {@link CodecRegistry} and {@link ParameterHandling#INLINE} parameter rendering. @@ -182,7 +199,7 @@ public class StatementBuilder { statement = runnable.run(statement, termFactory); } - return onBuild(statement.builder()).build(); + return StatementBuilder.this.build(statement.builder()); } if (parameterHandling == ParameterHandling.BY_INDEX) { @@ -197,7 +214,7 @@ public class StatementBuilder { statement = runnable.run(statement, termFactory); } - return onBuild(statement.builder().addPositionalValues(values)).build(); + return build(statement.builder().addPositionalValues(values)); } if (parameterHandling == ParameterHandling.BY_NAME) { @@ -216,12 +233,23 @@ public class StatementBuilder { SimpleStatementBuilder builder = statement.builder(); values.forEach(builder::addNamedValue); - return onBuild(builder).build(); + return build(builder); } throw new UnsupportedOperationException(String.format("ParameterHandling %s not supported", parameterHandling)); } + private SimpleStatement build(SimpleStatementBuilder builder) { + + SimpleStatement statmentToUse = onBuild(builder).build(); + + for (UnaryOperator operator : onBuilt) { + statmentToUse = operator.apply(statmentToUse); + } + + return statmentToUse; + } + private static Term toLiteralTerms(@Nullable Object value, CodecRegistry codecRegistry) { if (value instanceof List) { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/DeleteOptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/DeleteOptionsUnitTests.java index 20c173028..f1fd7c945 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/DeleteOptionsUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/DeleteOptionsUnitTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.core; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import java.time.Duration; import java.time.Instant; @@ -24,6 +24,8 @@ import org.junit.Test; import org.springframework.data.cassandra.core.query.Query; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; + /** * Unit tests for {@link DeleteOptions}. * @@ -31,7 +33,7 @@ import org.springframework.data.cassandra.core.query.Query; */ public class DeleteOptionsUnitTests { - @Test // DATACASS-575 + @Test // DATACASS-575, DATACASS-708 public void shouldConfigureDeleteOptions() { Instant now = Instant.ofEpochSecond(1234); @@ -40,8 +42,11 @@ public class DeleteOptionsUnitTests { .ttl(10) // .timestamp(now) // .withIfExists() // + .executionProfile("foo") // + .serialConsistencyLevel(DefaultConsistencyLevel.LOCAL_ONE) // .build(); + assertThat(deleteOptions.getTtl()).isEqualTo(Duration.ofSeconds(10)); assertThat(deleteOptions.getTimestamp()).isEqualTo(now.toEpochMilli() * 1000); assertThat(deleteOptions.isIfExists()).isTrue(); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java index 8b9e563ad..a4732fb54 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/InsertOptionsUnitTests.java @@ -24,6 +24,8 @@ import java.time.ZoneOffset; import org.junit.Test; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; + /** * Unit tests for {@link InsertOptions}. * @@ -32,12 +34,18 @@ import org.junit.Test; */ public class InsertOptionsUnitTests { - @Test // DATACASS-250, DATACASS-155 + @Test // DATACASS-250, DATACASS-155, DATACASS-708 public void shouldConfigureInsertOptions() { Instant now = LocalDateTime.now().toInstant(ZoneOffset.UTC); - InsertOptions insertOptions = InsertOptions.builder().ttl(10).timestamp(now).withIfNotExists().build(); + InsertOptions insertOptions = InsertOptions.builder() // + .ttl(10) // + .timestamp(now) // + .withIfNotExists() // + .executionProfile("foo") // + .serialConsistencyLevel(DefaultConsistencyLevel.LOCAL_ONE) // + .build(); assertThat(insertOptions.getTtl()).isEqualTo(Duration.ofSeconds(10)); assertThat(insertOptions.getTimestamp()).isEqualTo(now.toEpochMilli() * 1000); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/StatementFactoryUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/StatementFactoryUnitTests.java index 356661e4a..f6c532e25 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/StatementFactoryUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/StatementFactoryUnitTests.java @@ -30,6 +30,7 @@ import org.springframework.data.annotation.Id; import org.springframework.data.cassandra.core.convert.CassandraConverter; import org.springframework.data.cassandra.core.convert.MappingCassandraConverter; 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.cql.util.StatementBuilder; import org.springframework.data.cassandra.core.cql.util.StatementBuilder.ParameterHandling; @@ -42,6 +43,8 @@ import org.springframework.data.cassandra.core.query.Update; import org.springframework.data.cassandra.domain.Group; import org.springframework.data.domain.Sort; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; import com.datastax.oss.driver.api.querybuilder.delete.Delete; import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert; import com.datastax.oss.driver.api.querybuilder.select.Select; @@ -71,6 +74,22 @@ public class StatementFactoryUnitTests { assertThat(select.build(ParameterHandling.INLINE).getQuery()).isEqualTo("SELECT * FROM group"); } + @Test // DATACASS-708 + public void selectShouldApplyQueryOptions() { + + QueryOptions queryOptions = QueryOptions.builder() // + .executionProfile("foo") // + .serialConsistencyLevel(DefaultConsistencyLevel.QUORUM) // + .build(); + + StatementBuilder