Polishing.

Introduce TermFactory.canBindCollection() to avoid unecessary parameter encoding.

See #1172
Original pull request: #1178.
This commit is contained in:
Mark Paluch
2021-10-13 15:27:55 +02:00
parent 813de10bb4
commit be9cfc7d1e
4 changed files with 111 additions and 41 deletions

View File

@@ -944,63 +944,65 @@ public class StatementFactory {
() -> new IllegalArgumentException(String.format("Unknown operator [%s]", predicate.getOperator())));
ColumnRelationBuilder<Relation> column = Relation.column(columnName);
Object value = predicate.getValue();
switch (predicateOperator) {
case EQ:
return column.isEqualTo(factory.create(predicate.getValue()));
return column.isEqualTo(factory.create(value));
case NE:
return column.isNotEqualTo(factory.create(predicate.getValue()));
return column.isNotEqualTo(factory.create(value));
case GT:
return column.isGreaterThan(factory.create(predicate.getValue()));
return column.isGreaterThan(factory.create(value));
case GTE:
return column.isGreaterThanOrEqualTo(factory.create(predicate.getValue()));
return column.isGreaterThanOrEqualTo(factory.create(value));
case LT:
return column.isLessThan(factory.create(predicate.getValue()));
return column.isLessThan(factory.create(value));
case LTE:
return column.isLessThanOrEqualTo(factory.create(predicate.getValue()));
return column.isLessThanOrEqualTo(factory.create(value));
case IN:
if (predicate.getValue() instanceof List
|| (predicate.getValue() != null && predicate.getValue().getClass().isArray())) {
Term term = factory.create(predicate.getValue());
if (term instanceof BindMarker) {
return column.in((BindMarker) term);
if (isCollectionLike(value)) {
if (factory.canBindCollection()) {
Term term = factory.create(value);
return term instanceof BindMarker ? column.in((BindMarker) term) : column.in(term);
}
return column.in(toLiterals(predicate.getValue()));
return column.in(toLiterals(value));
}
return column.in(factory.create(predicate.getValue()));
return column.in(factory.create(value));
case LIKE:
return column.like(factory.create(predicate.getValue()));
return column.like(factory.create(value));
case IS_NOT_NULL:
return column.isNotNull();
case CONTAINS:
Assert.state(predicate.getValue() != null,
Assert.state(value != null,
() -> String.format("CONTAINS value for column %s is null", columnName));
return column.contains(factory.create(predicate.getValue()));
return column.contains(factory.create(value));
case CONTAINS_KEY:
Assert.state(predicate.getValue() != null,
Assert.state(value != null,
() -> String.format("CONTAINS KEY value for column %s is null", columnName));
return column.containsKey(factory.create(predicate.getValue()));
return column.containsKey(factory.create(value));
}
throw new IllegalArgumentException(
String.format("Criteria %s %s %s not supported", columnName, predicate.getOperator(), predicate.getValue()));
String.format("Criteria %s %s %s not supported", columnName, predicate.getOperator(), value));
}
private static Condition toCondition(CriteriaDefinition criteriaDefinition, TermFactory factory) {
@@ -1014,43 +1016,45 @@ public class StatementFactory {
() -> new IllegalArgumentException(String.format("Unknown operator [%s]", predicate.getOperator())));
ConditionBuilder<Condition> column = Condition.column(columnName);
Object value = predicate.getValue();
switch (predicateOperator) {
case EQ:
return column.isEqualTo(factory.create(predicate.getValue()));
return column.isEqualTo(factory.create(value));
case NE:
return column.isNotEqualTo(factory.create(predicate.getValue()));
return column.isNotEqualTo(factory.create(value));
case GT:
return column.isGreaterThan(factory.create(predicate.getValue()));
return column.isGreaterThan(factory.create(value));
case GTE:
return column.isGreaterThanOrEqualTo(factory.create(predicate.getValue()));
return column.isGreaterThanOrEqualTo(factory.create(value));
case LT:
return column.isLessThan(factory.create(predicate.getValue()));
return column.isLessThan(factory.create(value));
case LTE:
return column.isLessThanOrEqualTo(factory.create(predicate.getValue()));
return column.isLessThanOrEqualTo(factory.create(value));
case IN:
if (predicate.getValue() instanceof List
|| (predicate.getValue() != null && predicate.getValue().getClass().isArray())) {
Term term = factory.create(predicate.getValue());
if (term instanceof BindMarker) {
return column.in((BindMarker) term);
if (isCollectionLike(value)) {
if (factory.canBindCollection()) {
Term term = factory.create(value);
return term instanceof BindMarker ? column.in((BindMarker) term) : column.in(term);
}
return column.in(toLiterals(predicate.getValue()));
return column.in(toLiterals(value));
}
return column.in(factory.create(predicate.getValue()));
return column.in(factory.create(value));
}
throw new IllegalArgumentException(String.format("Criteria %s %s %s not supported for IF Conditions", columnName,
predicate.getOperator(), predicate.getValue()));
predicate.getOperator(), value));
}
static List<Term> toLiterals(@Nullable Object arrayOrList) {
@@ -1084,6 +1088,10 @@ public class StatementFactory {
return Collections.emptyList();
}
private static boolean isCollectionLike(@Nullable Object value) {
return value instanceof List || (value != null && value.getClass().isArray());
}
static class SimpleSelector implements com.datastax.oss.driver.api.querybuilder.select.Selector {
private final String selector;

View File

@@ -206,7 +206,17 @@ public class StatementBuilder<S extends BuildableQuery> {
if (parameterHandling == ParameterHandling.INLINE) {
TermFactory termFactory = value -> toLiteralTerms(value, codecRegistry);
TermFactory termFactory = new TermFactory() {
@Override
public Term create(@Nullable Object value) {
return toLiteralTerms(value, codecRegistry);
}
@Override
public boolean canBindCollection() {
return false;
}
};
for (BuilderRunnable<S> runnable : queryActions) {
statement = runnable.run(statement, termFactory);

View File

@@ -15,10 +15,10 @@
*/
package org.springframework.data.cassandra.core.cql.util;
import com.datastax.oss.driver.api.querybuilder.term.Term;
import org.springframework.lang.Nullable;
import com.datastax.oss.driver.api.querybuilder.term.Term;
/**
* Factory for {@link Term} objects encapsulating a binding {@code value}. Classes implementing this factory interface
* may return inline terms to render values as part of the query string, or bind markers to supply parameters on
@@ -39,4 +39,14 @@ public interface TermFactory {
* @return the {@link Term} for the given {@code value}.
*/
Term create(@Nullable Object value);
/**
* Check whether the term factory accepts {@link java.util.Collection} values to be created as {@link Term}.
*
* @return {@code true} whether the term factory can {@link java.util.Collection} values.
* @since 3.2.6
*/
default boolean canBindCollection() {
return true;
}
}

View File

@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.Sort.Direction.*;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -43,6 +44,7 @@ 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.CqlIdentifier;
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;
@@ -171,14 +173,54 @@ class StatementFactoryUnitTests {
.isEqualTo("SELECT * FROM group LIMIT 10 ALLOW FILTERING");
}
@Test
void shouldMapSelectInQuery() {
@Test // GH-1172
void shouldMapSelectInQueryAsInlineValue() {
Query query = Query.query(Criteria.where("foo").in("bar"));
StatementBuilder<Select> select = statementFactory.select(query, groupEntity);
StatementBuilder<Select> select = statementFactory.select(Query.query(Criteria.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);
assertThat(select.build(ParameterHandling.INLINE).getQuery())
.isEqualTo("SELECT * FROM group WHERE foo IN ('bar','baz')");
}
@Test // GH-1172
void shouldMapSelectInQueryAsByIndexValue() {
StatementBuilder<Select> select = statementFactory.select(Query.query(Criteria.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);
statement = select.build(ParameterHandling.BY_INDEX);
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN ?");
assertThat(statement.getPositionalValues()).containsOnly(Arrays.asList("bar", "baz"));
}
@Test // GH-1172
void shouldMapSelectInQueryAsByNamedValue() {
StatementBuilder<Select> select = statementFactory.select(Query.query(Criteria.where("foo").in("bar")),
groupEntity);
SimpleStatement statement = select.build(ParameterHandling.BY_NAME);
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN :p0");
assertThat(statement.getNamedValues()).hasSize(1).containsEntry(CqlIdentifier.fromCql("p0"),
Collections.singletonList("bar"));
select = statementFactory.select(Query.query(Criteria.where("foo").in("bar", "baz")), groupEntity);
statement = select.build(ParameterHandling.BY_NAME);
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM group WHERE foo IN :p0");
assertThat(statement.getNamedValues()).hasSize(1).containsEntry(CqlIdentifier.fromCql("p0"),
(Arrays.asList("bar", "baz")));
}
@Test // DATACASS-343