Skip to content

Commit 363c734

Browse files
committed
Refine value binding for statements accepting TTL and Timestamp.
We now use bind markers to represent the TTL and Timestamp for Insert, Update, and Delete if the StatementFactory uses bind markers. Otherwise, we fall back to inline values. This change prevents re-preparing statements for statements with different TTL and Timestamp values. Closes #1401
1 parent e950084 commit 363c734

File tree

6 files changed

+468
-64
lines changed

6 files changed

+468
-64
lines changed

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java

Lines changed: 77 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.data.cassandra.core.convert.Where;
3434
import org.springframework.data.cassandra.core.cql.QueryOptions;
3535
import org.springframework.data.cassandra.core.cql.QueryOptionsUtil;
36+
import org.springframework.data.cassandra.core.cql.QueryOptionsUtil.CqlStatementOptionsAccessor;
3637
import org.springframework.data.cassandra.core.cql.WriteOptions;
3738
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
3839
import org.springframework.data.cassandra.core.cql.util.TermFactory;
@@ -309,9 +310,13 @@ public StatementBuilder<RegularInsert> insert(Object objectToInsert, WriteOption
309310
.of(QueryBuilder.insertInto(tableName).valuesByIds(Collections.emptyMap())).bind((statement, factory) -> {
310311

311312
Map<CqlIdentifier, Term> values = createTerms(insertNulls, object, factory);
313+
CqlStatementOptionsAccessor<Insert> accessor = factory.ifBoundOrInline(
314+
bindings -> CqlStatementOptionsAccessor.ofInsert(bindings, statement),
315+
() -> CqlStatementOptionsAccessor.ofInsert(statement));
316+
RegularInsert afterOptions = (RegularInsert) addInsertOptions(accessor, options);
312317

313-
return statement.valuesByIds(values);
314-
}).apply(statement -> (RegularInsert) addWriteOptions(statement, options));
318+
return afterOptions.valuesByIds(values);
319+
});
315320

316321
builder.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, options));
317322

@@ -373,15 +378,12 @@ StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> update(
373378
Update mappedUpdate = getUpdateMapper().getMappedObject(update, persistentEntity);
374379

375380
StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> builder = update(tableName, mappedUpdate,
376-
filter);
381+
filter, query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast));
377382

378383
query.getQueryOptions().filter(UpdateOptions.class::isInstance).map(UpdateOptions.class::cast)
379384
.map(UpdateOptions::getIfCondition)
380385
.ifPresent(criteriaDefinitions -> applyUpdateIfCondition(builder, criteriaDefinitions));
381386

382-
query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast)
383-
.ifPresent(writeOptions -> builder.apply(statement -> addWriteOptions(statement, writeOptions)));
384-
385387
query.getQueryOptions().ifPresent(
386388
options -> builder.transform(statementBuilder -> QueryOptionsUtil.addQueryOptions(statementBuilder, options)));
387389

@@ -435,10 +437,16 @@ public StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update>
435437
where.forEach((cqlIdentifier, o) -> object.remove(cqlIdentifier));
436438

437439
StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> builder = StatementBuilder
438-
.of(QueryBuilder.update(tableName).set().where())
439-
.bind((statement, factory) -> ((UpdateWithAssignments) statement).set(toAssignments(object, factory))
440-
.where(toRelations(where, factory)))
441-
.apply(update -> addWriteOptions(update, options));
440+
.of(QueryBuilder.update(tableName).set().where()).bind((statement, factory) -> {
441+
442+
CqlStatementOptionsAccessor<UpdateStart> accessor = factory.ifBoundOrInline(
443+
bindings -> CqlStatementOptionsAccessor.ofUpdate(bindings, (UpdateStart) statement),
444+
() -> CqlStatementOptionsAccessor.ofUpdate((UpdateStart) statement));
445+
com.datastax.oss.driver.api.querybuilder.update.Update statementToUse = addUpdateOptions(accessor, options);
446+
447+
return ((UpdateWithAssignments) statementToUse).set(toAssignments(object, factory))
448+
.where(toRelations(where, factory));
449+
});
442450

443451
Optional.of(options).filter(UpdateOptions.class::isInstance).map(UpdateOptions.class::cast)
444452
.map(UpdateOptions::getIfCondition)
@@ -503,15 +511,13 @@ public StatementBuilder<Delete> delete(Query query, CassandraPersistentEntity<?>
503511
Filter filter = getQueryMapper().getMappedObject(query, persistentEntity);
504512
List<CqlIdentifier> columnNames = getQueryMapper().getMappedColumnNames(query.getColumns(), persistentEntity);
505513

506-
StatementBuilder<Delete> builder = delete(columnNames, tableName, filter);
514+
StatementBuilder<Delete> builder = delete(columnNames, tableName, filter,
515+
query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast));
507516

508517
query.getQueryOptions().filter(DeleteOptions.class::isInstance).map(DeleteOptions.class::cast)
509518
.map(DeleteOptions::getIfCondition)
510519
.ifPresent(criteriaDefinitions -> applyDeleteIfCondition(builder, criteriaDefinitions));
511520

512-
query.getQueryOptions().filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast)
513-
.ifPresent(writeOptions -> builder.apply(statement -> addWriteOptions(statement, writeOptions)));
514-
515521
query.getQueryOptions()
516522
.ifPresent(options -> builder.transform(statement -> QueryOptionsUtil.addQueryOptions(statement, options)));
517523

@@ -539,10 +545,21 @@ public StatementBuilder<Delete> delete(Object entity, QueryOptions options, Enti
539545
entityWriter.write(entity, where);
540546

541547
StatementBuilder<Delete> builder = StatementBuilder.of(QueryBuilder.deleteFrom(tableName).where())
542-
.bind((statement, factory) -> statement.where(toRelations(where, factory)));
548+
.bind((statement, factory) -> {
549+
550+
Delete statementToUse;
551+
if (options instanceof WriteOptions wo) {
552+
553+
CqlStatementOptionsAccessor<DeleteSelection> accessor = factory.ifBoundOrInline(
554+
bindings -> CqlStatementOptionsAccessor.ofDelete(bindings, (DeleteSelection) statement),
555+
() -> CqlStatementOptionsAccessor.ofDelete((DeleteSelection) statement));
556+
statementToUse = addDeleteOptions(accessor, wo);
557+
} else {
558+
statementToUse = statement;
559+
}
543560

544-
Optional.of(options).filter(WriteOptions.class::isInstance).map(WriteOptions.class::cast)
545-
.ifPresent(it -> builder.apply(statement -> addWriteOptions(statement, it)));
561+
return statementToUse.where(toRelations(where, factory));
562+
});
546563

547564
Optional.of(options).filter(DeleteOptions.class::isInstance).map(DeleteOptions.class::cast)
548565
.map(DeleteOptions::getIfCondition)
@@ -707,17 +724,28 @@ private static com.datastax.oss.driver.api.querybuilder.select.Selector getSelec
707724
}
708725

709726
private static StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> update(CqlIdentifier table,
710-
Update mappedUpdate, Filter filter) {
727+
Update mappedUpdate, Filter filter, Optional<WriteOptions> optionalOptions) {
711728

712729
UpdateStart updateStart = QueryBuilder.update(table);
713730

714731
return StatementBuilder.of((com.datastax.oss.driver.api.querybuilder.update.Update) updateStart)
715732
.bind((statement, factory) -> {
716733

734+
com.datastax.oss.driver.api.querybuilder.update.Update statementToUse;
735+
WriteOptions options = optionalOptions.orElse(null);
736+
if (options != null) {
737+
CqlStatementOptionsAccessor<UpdateStart> accessor = factory.ifBoundOrInline(
738+
bindings -> CqlStatementOptionsAccessor.ofUpdate(bindings, (UpdateStart) statement),
739+
() -> CqlStatementOptionsAccessor.ofUpdate((UpdateStart) statement));
740+
statementToUse = addUpdateOptions(accessor, options);
741+
} else {
742+
statementToUse = statement;
743+
}
744+
717745
List<Assignment> assignments = mappedUpdate.getUpdateOperations().stream()
718746
.map(assignmentOp -> getAssignment(assignmentOp, factory)).collect(Collectors.toList());
719747

720-
return (com.datastax.oss.driver.api.querybuilder.update.Update) ((OngoingAssignment) statement)
748+
return (com.datastax.oss.driver.api.querybuilder.update.Update) ((OngoingAssignment) statementToUse)
721749
.set(assignments);
722750

723751
}).bind((statement, factory) -> {
@@ -851,7 +879,8 @@ private static Assignment getAssignment(AddToMapOp updateOp, TermFactory termFac
851879
return Assignment.append(updateOp.toCqlIdentifier(), termFactory.create(updateOp.getValue()));
852880
}
853881

854-
private StatementBuilder<Delete> delete(List<CqlIdentifier> columnNames, CqlIdentifier from, Filter filter) {
882+
private StatementBuilder<Delete> delete(List<CqlIdentifier> columnNames, CqlIdentifier from, Filter filter,
883+
Optional<WriteOptions> optionsOptional) {
855884

856885
DeleteSelection select = QueryBuilder.deleteFrom(from);
857886

@@ -860,7 +889,19 @@ private StatementBuilder<Delete> delete(List<CqlIdentifier> columnNames, CqlIden
860889
}
861890

862891
return StatementBuilder.of(select.where()).bind((statement, factory) -> {
863-
return statement.where(getRelations(filter, factory));
892+
893+
WriteOptions options = optionsOptional.orElse(null);
894+
Delete statementToUse;
895+
if (options != null) {
896+
CqlStatementOptionsAccessor<DeleteSelection> accessor = factory.ifBoundOrInline(
897+
bindings -> CqlStatementOptionsAccessor.ofDelete(bindings, (DeleteSelection) statement),
898+
() -> CqlStatementOptionsAccessor.ofDelete((DeleteSelection) statement));
899+
statementToUse = addDeleteOptions(accessor, options);
900+
} else {
901+
statementToUse = statement;
902+
}
903+
904+
return statementToUse.where(getRelations(filter, factory));
864905
});
865906
}
866907

@@ -870,21 +911,22 @@ private StatementBuilder<Delete> delete(List<CqlIdentifier> columnNames, CqlIden
870911
* @param insert {@link Insert} CQL statement, must not be {@literal null}.
871912
* @param writeOptions write options (e.g. consistency level) to add to the CQL statement.
872913
* @return the given {@link Insert}.
873-
* @see #addWriteOptions(Insert, WriteOptions)
874914
* @since 2.1
875915
*/
876-
static Insert addWriteOptions(Insert insert, WriteOptions writeOptions) {
916+
static Insert addInsertOptions(CqlStatementOptionsAccessor<Insert> insert, WriteOptions writeOptions) {
877917

878918
Assert.notNull(insert, "Insert must not be null");
879919

920+
Insert insertToUse = QueryOptionsUtil.addWriteOptions(insert, writeOptions);
921+
880922
if (writeOptions instanceof InsertOptions insertOptions) {
881923

882924
if (insertOptions.isIfNotExists()) {
883-
insert = insert.ifNotExists();
925+
insertToUse = insertToUse.ifNotExists();
884926
}
885927
}
886928

887-
return QueryOptionsUtil.addWriteOptions(insert, writeOptions);
929+
return insertToUse;
888930
}
889931

890932
/**
@@ -898,13 +940,13 @@ static Insert addWriteOptions(Insert insert, WriteOptions writeOptions) {
898940
* @see QueryOptionsUtil#addWriteOptions(com.datastax.oss.driver.api.querybuilder.update.Update, WriteOptions)
899941
* @since 2.1
900942
*/
901-
static com.datastax.oss.driver.api.querybuilder.update.Update addWriteOptions(
902-
com.datastax.oss.driver.api.querybuilder.update.Update update, WriteOptions writeOptions) {
943+
static com.datastax.oss.driver.api.querybuilder.update.Update addUpdateOptions(
944+
CqlStatementOptionsAccessor<UpdateStart> update, WriteOptions writeOptions) {
903945

904946
Assert.notNull(update, "Update must not be null");
905947

906-
com.datastax.oss.driver.api.querybuilder.update.Update updateToUse = QueryOptionsUtil.addWriteOptions(update,
907-
writeOptions);
948+
com.datastax.oss.driver.api.querybuilder.update.Update updateToUse = (com.datastax.oss.driver.api.querybuilder.update.Update) QueryOptionsUtil
949+
.addWriteOptions(update, writeOptions);
908950

909951
if (writeOptions instanceof UpdateOptions updateOptions) {
910952

@@ -924,11 +966,11 @@ static com.datastax.oss.driver.api.querybuilder.update.Update addWriteOptions(
924966
* @return the given {@link Delete}.
925967
* @since 2.1
926968
*/
927-
static Delete addWriteOptions(Delete delete, WriteOptions writeOptions) {
969+
static Delete addDeleteOptions(CqlStatementOptionsAccessor<DeleteSelection> delete, WriteOptions writeOptions) {
928970

929971
Assert.notNull(delete, "Delete must not be null");
930972

931-
Delete deleteToUse = QueryOptionsUtil.addWriteOptions(delete, writeOptions);
973+
Delete deleteToUse = (Delete) QueryOptionsUtil.addWriteOptions(delete, writeOptions);
932974

933975
if (writeOptions instanceof DeleteOptions deleteOptions) {
934976

@@ -996,15 +1038,13 @@ private static Relation toClause(CriteriaDefinition criteriaDefinition, TermFact
9961038

9971039
case CONTAINS:
9981040

999-
Assert.state(value != null,
1000-
() -> String.format("CONTAINS value for column %s is null", columnName));
1041+
Assert.state(value != null, () -> String.format("CONTAINS value for column %s is null", columnName));
10011042

10021043
return column.contains(factory.create(value));
10031044

10041045
case CONTAINS_KEY:
10051046

1006-
Assert.state(value != null,
1007-
() -> String.format("CONTAINS KEY value for column %s is null", columnName));
1047+
Assert.state(value != null, () -> String.format("CONTAINS KEY value for column %s is null", columnName));
10081048

10091049
return column.containsKey(factory.create(value));
10101050
}
@@ -1061,8 +1101,8 @@ private static Condition toCondition(CriteriaDefinition criteriaDefinition, Term
10611101
return column.in(factory.create(value));
10621102
}
10631103

1064-
throw new IllegalArgumentException(String.format("Criteria %s %s %s not supported for IF Conditions", columnName,
1065-
predicate.getOperator(), value));
1104+
throw new IllegalArgumentException(
1105+
String.format("Criteria %s %s %s not supported for IF Conditions", columnName, predicate.getOperator(), value));
10661106
}
10671107

10681108
static List<Term> toLiterals(@Nullable Object arrayOrList) {

0 commit comments

Comments
 (0)