DATACASS-832 - Use index-based parameter binding.

We now use positional parameters when building SimpleStatement through StatementBuilder by default to access the individual parameters later on for prepared statement usage.
This commit is contained in:
Mark Paluch
2020-12-03 14:12:33 +01:00
parent 8093392d60
commit 529e0692e6
11 changed files with 496 additions and 265 deletions

View File

@@ -25,6 +25,10 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
@@ -33,14 +37,10 @@ import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.term.Term;
import com.datastax.oss.driver.internal.querybuilder.CqlHelper;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Functional builder for Cassandra {@link BuildableQuery statements}. Statements are built by applying
* {@link UnaryOperator builder functions} that get applied when {@link #build() building} the actual
* {@link SimpleStatement statement}. The {@code StatmentBuilder} provides a mutable container for statement creation
* {@link SimpleStatement statement}. The {@code StatementBuilder} provides a mutable container for statement creation
* allowing a functional declaration of actions that are necessary to build a statement. This class helps building CQL
* statements as a {@link BuildableQuery} classes are typically immutable and require return value tracking across
* methods that want to apply modifications to a statement.
@@ -69,11 +69,11 @@ import org.springframework.util.Assert;
*/
public class StatementBuilder<S extends BuildableQuery> {
private S statement;
private final S statement;
private List<BuilderRunnable<S>> queryActions = new ArrayList<>();
private List<Consumer<SimpleStatementBuilder>> onBuild = new ArrayList<>();
private List<UnaryOperator<SimpleStatement>> onBuilt = new ArrayList<>();
private final List<BuilderRunnable<S>> queryActions = new ArrayList<>();
private final List<Consumer<SimpleStatementBuilder>> onBuild = new ArrayList<>();
private final List<UnaryOperator<SimpleStatement>> onBuilt = new ArrayList<>();
/**
* Factory method used to create a new {@link StatementBuilder} with the given {@link BuildableQuery query stub}.
@@ -170,12 +170,12 @@ public class StatementBuilder<S extends BuildableQuery> {
/**
* Build a {@link SimpleStatement statement} by applying builder and bind functions using the default
* {@link CodecRegistry} and {@link ParameterHandling#INLINE} parameter rendering.
* {@link CodecRegistry} and {@link ParameterHandling#BY_INDEX} parameter rendering.
*
* @return the built {@link SimpleStatement}.
*/
public SimpleStatement build() {
return build(ParameterHandling.INLINE, CodecRegistry.DEFAULT);
return build(ParameterHandling.BY_INDEX, CodecRegistry.DEFAULT);
}
/**
@@ -228,7 +228,11 @@ public class StatementBuilder<S extends BuildableQuery> {
statement = runnable.run(statement, termFactory);
}
return build(statement.builder().addPositionalValues(values));
SimpleStatementBuilder builder = statement.builder();
values.forEach(builder::addPositionalValue);
return build(builder);
}
if (parameterHandling == ParameterHandling.BY_NAME) {

View File

@@ -58,6 +58,7 @@ import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link AsyncCassandraTemplate}.
@@ -130,7 +131,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(list)).hasSize(1).contains(new User("myid", "Walter", "White"));
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users");
}
@Test // DATACASS-292
@@ -156,7 +157,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(result)).isNull();
assertThat(list).hasSize(1).contains(new User("myid", "Walter", "White"));
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users");
}
@Test // DATACASS-292
@@ -197,7 +198,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(new User("myid", "Walter", "White"));
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid'");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid'");
}
@Test // DATACASS-292
@@ -220,7 +221,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(new User("myid", "Walter", "White"));
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-696
@@ -242,7 +243,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isTrue();
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-292
@@ -252,7 +253,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isFalse();
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-512
@@ -264,7 +265,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isTrue();
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users LIMIT 1");
}
@Test // DATACASS-292
@@ -278,7 +279,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(42L);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT count(1) FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
}
@Test // DATACASS-292
@@ -292,7 +293,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(42L);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT count(1) FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
}
@Test // DATACASS-292, DATACASS-618
@@ -306,7 +307,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(user);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White')");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -323,7 +324,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(user);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"INSERT INTO vusers (firstname,id,lastname,version) VALUES ('Walter','heisenberg','White',0) IF NOT EXISTS");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -359,7 +360,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(user);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg'");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -377,7 +378,8 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(user);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
SimpleStatement value = statementCaptor.getValue();
assertThat(render(value)).isEqualTo(
"UPDATE vusers SET firstname='Walter', lastname='White', version=1 WHERE id='heisenberg' IF version=0");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -392,7 +394,7 @@ public class AsyncCassandraTemplateUnitTests {
template.update(user, updateOptions);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF EXISTS");
}
@@ -405,7 +407,7 @@ public class AsyncCassandraTemplateUnitTests {
template.update(user, options);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -418,7 +420,7 @@ public class AsyncCassandraTemplateUnitTests {
template.update(query, update, User.class);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter' WHERE id='heisenberg'");
}
@@ -435,7 +437,7 @@ public class AsyncCassandraTemplateUnitTests {
template.update(query, update, User.class);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"UPDATE users SET firstname='Walter' WHERE id='heisenberg' IF firstname='Walter' AND lastname='White'");
}
@@ -469,7 +471,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isTrue();
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
}
@Test // DATACASS-292
@@ -483,7 +485,7 @@ public class AsyncCassandraTemplateUnitTests {
assertThat(getUninterruptibly(future)).isEqualTo(user);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
}
@Test // DATACASS-575
@@ -495,7 +497,7 @@ public class AsyncCassandraTemplateUnitTests {
template.delete(user, options);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -508,7 +510,7 @@ public class AsyncCassandraTemplateUnitTests {
template.delete(query, User.class);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -537,7 +539,22 @@ public class AsyncCassandraTemplateUnitTests {
template.truncate(User.class);
verify(session).executeAsync(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("TRUNCATE users");
assertThat(render(statementCaptor.getValue())).isEqualTo("TRUNCATE users");
}
private static String render(SimpleStatement statement) {
String query = statement.getQuery();
List<Object> positionalValues = statement.getPositionalValues();
for (Object positionalValue : positionalValues) {
query = query.replaceFirst("\\?",
positionalValue != null
? CodecRegistry.DEFAULT.codecFor((Class) positionalValue.getClass()).format(positionalValue)
: "NULL");
}
return query;
}
private static <T> T getUninterruptibly(Future<T> future) {

View File

@@ -53,6 +53,7 @@ import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link CassandraTemplate}.
@@ -125,7 +126,7 @@ class CassandraTemplateUnitTests {
assertThat(list).hasSize(1).contains(new User("myid", "Walter", "White"));
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users");
}
@Test // DATACASS-292
@@ -162,7 +163,7 @@ class CassandraTemplateUnitTests {
assertThat(user).isEqualTo(new User("myid", "Walter", "White"));
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid'");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid'");
}
@Test // DATACASS-696
@@ -195,7 +196,7 @@ class CassandraTemplateUnitTests {
assertThat(user).isEqualTo(new User("myid", "Walter", "White"));
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-313
@@ -214,7 +215,7 @@ class CassandraTemplateUnitTests {
assertThat(user.getFirstname()).isEqualTo("Walter");
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT firstname FROM users LIMIT 2");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT firstname FROM users LIMIT 2");
}
@Test // DATACASS-292
@@ -226,7 +227,7 @@ class CassandraTemplateUnitTests {
assertThat(exists).isTrue();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-292
@@ -236,7 +237,7 @@ class CassandraTemplateUnitTests {
assertThat(exists).isFalse();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-512
@@ -248,7 +249,7 @@ class CassandraTemplateUnitTests {
assertThat(exists).isTrue();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users LIMIT 1");
}
@Test // DATACASS-292
@@ -262,7 +263,7 @@ class CassandraTemplateUnitTests {
assertThat(count).isEqualTo(42L);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT count(1) FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
}
@Test // DATACASS-512
@@ -276,7 +277,7 @@ class CassandraTemplateUnitTests {
assertThat(count).isEqualTo(42L);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT count(1) FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
}
@Test // DATACASS-292, DATACASS-618
@@ -289,7 +290,7 @@ class CassandraTemplateUnitTests {
template.insert(user);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White')");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -305,7 +306,7 @@ class CassandraTemplateUnitTests {
template.insert(user);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"INSERT INTO vusers (firstname,id,lastname,version) VALUES ('Walter','heisenberg','White',0) IF NOT EXISTS");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -323,7 +324,7 @@ class CassandraTemplateUnitTests {
template.insert(user, insertOptions);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White') IF NOT EXISTS");
}
@@ -339,7 +340,7 @@ class CassandraTemplateUnitTests {
template.insert(user, insertOptions);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES (NULL,'heisenberg',NULL)");
}
@@ -380,7 +381,7 @@ class CassandraTemplateUnitTests {
template.update(user);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg'");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -397,7 +398,7 @@ class CassandraTemplateUnitTests {
template.update(user);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"UPDATE vusers SET firstname='Walter', lastname='White', version=1 WHERE id='heisenberg' IF version=0");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -416,7 +417,7 @@ class CassandraTemplateUnitTests {
assertThat(writeResult.wasApplied()).isTrue();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF EXISTS");
}
@@ -429,7 +430,7 @@ class CassandraTemplateUnitTests {
template.update(user, options);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -442,7 +443,7 @@ class CassandraTemplateUnitTests {
template.update(query, update, User.class);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter' WHERE id='heisenberg'");
}
@@ -459,7 +460,7 @@ class CassandraTemplateUnitTests {
template.update(query, update, User.class);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"UPDATE users SET firstname='Walter' WHERE id='heisenberg' IF firstname='Walter' AND lastname='White'");
}
@@ -489,7 +490,7 @@ class CassandraTemplateUnitTests {
assertThat(deleted).isTrue();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
}
@Test // DATACASS-292
@@ -500,7 +501,7 @@ class CassandraTemplateUnitTests {
template.delete(user);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
}
@Test // DATACASS-575
@@ -512,7 +513,7 @@ class CassandraTemplateUnitTests {
template.delete(user, options);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -525,7 +526,7 @@ class CassandraTemplateUnitTests {
template.delete(query, User.class);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -550,7 +551,22 @@ class CassandraTemplateUnitTests {
template.truncate(User.class);
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("TRUNCATE users");
assertThat(render(statementCaptor.getValue())).isEqualTo("TRUNCATE users");
}
private static String render(SimpleStatement statement) {
String query = statement.getQuery();
List<Object> positionalValues = statement.getPositionalValues();
for (Object positionalValue : positionalValues) {
query = query.replaceFirst("\\?",
positionalValue != null
? CodecRegistry.DEFAULT.codecFor((Class) positionalValue.getClass()).format(positionalValue)
: "NULL");
}
return query;
}
private interface UserProjection {

View File

@@ -24,6 +24,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -53,6 +55,7 @@ import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link ReactiveCassandraTemplate}.
@@ -126,7 +129,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users");
}
@Test // DATACASS-335
@@ -161,7 +164,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-313
@@ -184,7 +187,7 @@ class ReactiveCassandraTemplateUnitTests {
}).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT firstname FROM users LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT firstname FROM users LIMIT 1");
}
@Test // DATACASS-696
@@ -204,7 +207,7 @@ class ReactiveCassandraTemplateUnitTests {
template.exists("myid", User.class).as(StepVerifier::create).expectNext(true).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-335
@@ -215,7 +218,7 @@ class ReactiveCassandraTemplateUnitTests {
template.exists("myid", User.class).as(StepVerifier::create).expectNext(false).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
}
@Test // DATACASS-512
@@ -226,7 +229,7 @@ class ReactiveCassandraTemplateUnitTests {
template.exists(Query.empty(), User.class).as(StepVerifier::create).expectNext(true).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users LIMIT 1");
}
@Test // DATACASS-512
@@ -237,7 +240,7 @@ class ReactiveCassandraTemplateUnitTests {
template.exists(Query.empty(), User.class).as(StepVerifier::create).expectNext(false).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT * FROM users LIMIT 1");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users LIMIT 1");
}
@Test // DATACASS-335
@@ -250,7 +253,7 @@ class ReactiveCassandraTemplateUnitTests {
template.count(User.class).as(StepVerifier::create).expectNext(42L).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT count(1) FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
}
@Test // DATACASS-512
@@ -263,7 +266,7 @@ class ReactiveCassandraTemplateUnitTests {
template.count(Query.empty(), User.class).as(StepVerifier::create).expectNext(42L).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("SELECT count(1) FROM users");
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
}
@Test // DATACASS-335, DATACASS-618
@@ -276,7 +279,7 @@ class ReactiveCassandraTemplateUnitTests {
template.insert(user).as(StepVerifier::create).expectNext(user).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White')");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -292,7 +295,7 @@ class ReactiveCassandraTemplateUnitTests {
StepVerifier.create(template.insert(user)).expectNext(user).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"INSERT INTO vusers (firstname,id,lastname,version) VALUES ('Walter','heisenberg','White',0) IF NOT EXISTS");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -322,7 +325,7 @@ class ReactiveCassandraTemplateUnitTests {
template.update(user).as(StepVerifier::create).expectNext(user).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg'");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -340,7 +343,7 @@ class ReactiveCassandraTemplateUnitTests {
StepVerifier.create(template.update(user)).expectNext(user).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"UPDATE vusers SET firstname='Walter', lastname='White', version=1 WHERE id='heisenberg' IF version=0");
assertThat(beforeConvert).isSameAs(user);
assertThat(beforeSave).isSameAs(user);
@@ -360,7 +363,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF EXISTS");
}
@@ -378,7 +381,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -396,7 +399,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("UPDATE users SET firstname='Walter' WHERE id='heisenberg'");
}
@@ -418,7 +421,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo(
assertThat(render(statementCaptor.getValue())).isEqualTo(
"UPDATE users SET firstname='Walter' WHERE id='heisenberg' IF firstname='Walter' AND lastname='White'");
}
@@ -433,7 +436,7 @@ class ReactiveCassandraTemplateUnitTests {
template.delete(user).as(StepVerifier::create).expectNext(user).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
}
@Test // DATACASS-575
@@ -450,7 +453,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -468,7 +471,7 @@ class ReactiveCassandraTemplateUnitTests {
.verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery())
assertThat(render(statementCaptor.getValue()))
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
}
@@ -478,7 +481,22 @@ class ReactiveCassandraTemplateUnitTests {
template.truncate(User.class).as(StepVerifier::create).verifyComplete();
verify(session).execute(statementCaptor.capture());
assertThat(statementCaptor.getValue().getQuery()).isEqualTo("TRUNCATE users");
assertThat(render(statementCaptor.getValue())).isEqualTo("TRUNCATE users");
}
private static String render(SimpleStatement statement) {
String query = statement.getQuery();
List<Object> positionalValues = statement.getPositionalValues();
for (Object positionalValue : positionalValues) {
query = query.replaceFirst("\\?",
positionalValue != null
? CodecRegistry.DEFAULT.codecFor((Class) positionalValue.getClass()).format(positionalValue)
: "NULL");
}
return query;
}
private interface UserProjection {

View File

@@ -22,7 +22,6 @@ import lombok.Data;
import lombok.Getter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Currency;
import java.util.List;
import java.util.Map;
@@ -36,9 +35,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.core.StatementFactory;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
import org.springframework.data.cassandra.core.mapping.Table;
import org.springframework.data.cassandra.core.mapping.UserDefinedType;
@@ -51,7 +47,6 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.data.UdtValue;
/**
@@ -142,24 +137,6 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
assertThat(address.getStreetLines()).contains("Heckenpfad", "14");
}
@Test // DATACASS-172
void shouldWriteMappedUdt() {
AddressUserType addressUserType = new AddressUserType();
addressUserType.setZip("69469");
addressUserType.setCity("Weinheim");
addressUserType.setStreetLines(Arrays.asList("Heckenpfad", "14"));
AddressBook addressBook = new AddressBook();
addressBook.setId("1");
addressBook.setCurrentaddress(addressUserType);
SimpleStatement statement = new StatementFactory(converter).insert(addressBook, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo("INSERT INTO addressbook (currentaddress,id) "
+ "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']},'1')");
}
@Test // DATACASS-172
void shouldReadMappedUdtCollection() {
@@ -178,24 +155,6 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
assertThat(address.getStreetLines()).isEmpty();
}
@Test // DATACASS-172
void shouldWriteMappedUdtCollection() {
AddressUserType addressUserType = new AddressUserType();
addressUserType.setZip("69469");
addressUserType.setCity("Weinheim");
addressUserType.setStreetLines(Arrays.asList("Heckenpfad", "14"));
AddressBook addressBook = new AddressBook();
addressBook.setId("1");
addressBook.setPreviousaddresses(Collections.singletonList(addressUserType));
SimpleStatement statement = new StatementFactory(converter).insert(addressBook, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo("INSERT INTO addressbook (id,previousaddresses) "
+ "VALUES ('1',[{zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']}])");
}
@Test // DATACASS-172
void shouldReadUdt() {
@@ -210,68 +169,6 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
assertThat(addressBook.getAlternate().getString("zip")).isEqualTo("69469");
}
@Test // DATACASS-172
void shouldWriteUdt() {
CassandraPersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(AddressUserType.class);
com.datastax.oss.driver.api.core.type.UserDefinedType udtType = (com.datastax.oss.driver.api.core.type.UserDefinedType) converter
.getColumnTypeResolver().resolve(persistentEntity.getTypeInformation()).getDataType();
UdtValue udtValue = udtType.newValue();
udtValue.setString("zip", "69469");
udtValue.setString("city", "Weinheim");
udtValue.setList("streetlines", Arrays.asList("Heckenpfad", "14"), String.class);
AddressBook addressBook = new AddressBook();
addressBook.setId("1");
addressBook.setAlternate(udtValue);
SimpleStatement statement = new StatementFactory(converter).insert(addressBook, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo("INSERT INTO addressbook (alternate,id) "
+ "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']},'1')");
}
@Test // DATACASS-172
void shouldWriteUdtPk() {
AddressUserType addressUserType = new AddressUserType();
addressUserType.setZip("69469");
addressUserType.setCity("Weinheim");
addressUserType.setStreetLines(Arrays.asList("Heckenpfad", "14"));
WithMappedUdtId withUdtId = new WithMappedUdtId();
withUdtId.setId(addressUserType);
SimpleStatement statement = new StatementFactory(converter).insert(withUdtId, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo(
"INSERT INTO withmappedudtid (id) " + "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']})");
}
@Test // DATACASS-172
void shouldWriteMappedUdtPk() {
CassandraPersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(AddressUserType.class);
com.datastax.oss.driver.api.core.type.UserDefinedType udtType = (com.datastax.oss.driver.api.core.type.UserDefinedType) converter
.getColumnTypeResolver().resolve(persistentEntity.getTypeInformation()).getDataType();
UdtValue udtValue = udtType.newValue();
udtValue.setString("zip", "69469");
udtValue.setString("city", "Weinheim");
udtValue.setList("streetlines", Arrays.asList("Heckenpfad", "14"), String.class);
WithUdtId withUdtId = new WithUdtId();
withUdtId.setId(udtValue);
SimpleStatement statement = new StatementFactory(converter).insert(withUdtId, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo(
"INSERT INTO withudtid (id) " + "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']})");
}
@Test // DATACASS-172
void shouldReadUdtWithCustomConversion() {
@@ -295,50 +192,6 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
assertThat(addressBook.getOtherCurrencies()).hasSize(1).contains(Currency.getInstance("EUR"));
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteUdtWithCustomConversion() {
Bank bank = new Bank(null, Currency.getInstance("EUR"), null);
SimpleStatement statement = new StatementFactory(converter).insert(bank, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo("INSERT INTO bank (currency) VALUES ({currency:'EUR'})");
}
@Test // DATACASS-172
void shouldWriteUdtWhereWherePrimaryKeyWithCustomConversion() {
Money money = new Money();
money.setCurrency(Currency.getInstance("EUR"));
Where where = new Where();
converter.write(money, where);
assertThat((UdtValue) where.get(CqlIdentifier.fromCql("currency"))) //
.extracting(UdtValue::getFormattedContents) //
.isEqualTo("{currency:'EUR'}");
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteUdtUpdateAssignmentsWithCustomConversion() {
MoneyTransfer money = new MoneyTransfer("1", Currency.getInstance("EUR"));
SimpleStatement statement = new StatementFactory(converter).update(money, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo("UPDATE moneytransfer SET currency={currency:'EUR'} WHERE id='1'");
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteUdtListWithCustomConversion() {
Bank bank = new Bank(null, null, Collections.singletonList(Currency.getInstance("EUR")));
SimpleStatement statement = new StatementFactory(converter).insert(bank, WriteOptions.empty()).build();
assertThat(statement.getQuery()).isEqualTo("INSERT INTO bank (othercurrencies) VALUES ([{currency:'EUR'}])");
}
@Test // DATACASS-172
void shouldReadNestedUdt() {
@@ -352,19 +205,6 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
assertThat(car.getEngine().getManufacturer().getName()).isEqualTo("a good one");
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteNestedUdt() {
Engine engine = new Engine(new Manufacturer("a good one"));
Car car = new Car("1", engine);
SimpleStatement statement = new StatementFactory(converter).insert(car, WriteOptions.empty()).build();
assertThat(statement.getQuery())
.isEqualTo("INSERT INTO car (engine,id) VALUES ({manufacturer:{name:'a good one'}},'1')");
}
@Test // DATACASS-487
void shouldReadUdtInMap() {

View File

@@ -21,6 +21,7 @@ import static org.springframework.data.cassandra.test.util.RowMockUtil.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import java.util.Arrays;
import java.util.Collections;
@@ -37,10 +38,16 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.cassandra.core.StatementFactory;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.Embedded;
import org.springframework.data.cassandra.core.mapping.Table;
import org.springframework.data.cassandra.core.mapping.UserDefinedType;
import org.springframework.data.cassandra.core.mapping.UserTypeResolver;
import org.springframework.data.cassandra.support.UserDefinedTypeBuilder;
@@ -48,6 +55,7 @@ import org.springframework.data.cassandra.test.util.RowMockUtil;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.type.DataTypes;
@@ -66,19 +74,29 @@ class MappingCassandraConverterUDTUnitTests {
private com.datastax.oss.driver.api.core.type.UserDefinedType manufacturer = UserDefinedTypeBuilder
.forName("manufacturer")
.withField("name", DataTypes.TEXT).withField("displayname", DataTypes.TEXT).build();
private com.datastax.oss.driver.api.core.type.UserDefinedType engine = UserDefinedTypeBuilder.forName("engine")
.withField("manufacturer", manufacturer).build();
private com.datastax.oss.driver.api.core.type.UserDefinedType currency = UserDefinedTypeBuilder.forName("mycurrency")
.withField("currency", DataTypes.TEXT).build();
private com.datastax.oss.driver.api.core.type.UserDefinedType withnullableembeddedtype = UserDefinedTypeBuilder
.forName("withnullableembeddedtype").withField("value", DataTypes.TEXT).withField("firstname", DataTypes.TEXT)
.withField("age", DataTypes.INT).build();
private com.datastax.oss.driver.api.core.type.UserDefinedType withprefixednullableembeddedtype = UserDefinedTypeBuilder
.forName("withnullableembeddedtype").withField("value", DataTypes.TEXT)
.withField("prefixfirstname", DataTypes.TEXT).withField("prefixage", DataTypes.INT).build();
private com.datastax.oss.driver.api.core.type.UserDefinedType address = UserDefinedTypeBuilder.forName("address")
.withField("zip", DataTypes.TEXT).withField("city", DataTypes.TEXT)
.withField("streetLines", DataTypes.listOf(DataTypes.TEXT)).build();
private Row rowMock;
private CassandraMappingContext mappingContext;
private MappingCassandraConverter mappingCassandraConverter;
private MappingCassandraConverter converter;
@BeforeEach
void setUp() {
@@ -86,9 +104,16 @@ class MappingCassandraConverterUDTUnitTests {
mappingContext = new CassandraMappingContext();
mappingContext.setUserTypeResolver(userTypeResolver);
mappingCassandraConverter = new MappingCassandraConverter(mappingContext);
mappingCassandraConverter.afterPropertiesSet();
CassandraCustomConversions cassandraCustomConversions = new CassandraCustomConversions(
Arrays.asList(new UDTToCurrencyConverter(), new CurrencyToUDTConverter(userTypeResolver)));
mappingContext.setSimpleTypeHolder(cassandraCustomConversions.getSimpleTypeHolder());
converter = new MappingCassandraConverter(mappingContext);
converter.setCustomConversions(cassandraCustomConversions);
converter.afterPropertiesSet();
when(userTypeResolver.resolveType(CqlIdentifier.fromCql("address"))).thenReturn(address);
when(userTypeResolver.resolveType(CqlIdentifier.fromCql("engine"))).thenReturn(engine);
when(userTypeResolver.resolveType(CqlIdentifier.fromCql("manufacturer"))).thenReturn(manufacturer);
when(userTypeResolver.resolveType(CqlIdentifier.fromCql("currency"))).thenReturn(currency);
when(userTypeResolver.resolveType(CqlIdentifier.fromCql("withnullableembeddedtype")))
@@ -97,6 +122,155 @@ class MappingCassandraConverterUDTUnitTests {
.thenReturn(withprefixednullableembeddedtype);
}
@Test // DATACASS-172
void shouldWriteMappedUdt() {
AddressUserType addressUserType = new AddressUserType();
addressUserType.setZip("69469");
addressUserType.setCity("Weinheim");
addressUserType.setStreetLines(Arrays.asList("Heckenpfad", "14"));
AddressBook addressBook = new AddressBook();
addressBook.setId("1");
addressBook.setCurrentaddress(addressUserType);
SimpleStatement statement = new StatementFactory(converter).insert(addressBook, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("INSERT INTO addressbook (currentaddress,id) "
+ "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']},'1')");
}
@Test // DATACASS-172
void shouldWriteMappedUdtCollection() {
AddressUserType addressUserType = new AddressUserType();
addressUserType.setZip("69469");
addressUserType.setCity("Weinheim");
addressUserType.setStreetLines(Arrays.asList("Heckenpfad", "14"));
AddressBook addressBook = new AddressBook();
addressBook.setId("1");
addressBook.setPreviousaddresses(Collections.singletonList(addressUserType));
SimpleStatement statement = new StatementFactory(converter).insert(addressBook, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("INSERT INTO addressbook (id,previousaddresses) "
+ "VALUES ('1',[{zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']}])");
}
@Test // DATACASS-172
void shouldWriteUdt() {
CassandraPersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(AddressUserType.class);
com.datastax.oss.driver.api.core.type.UserDefinedType udtType = (com.datastax.oss.driver.api.core.type.UserDefinedType) converter
.getColumnTypeResolver().resolve(persistentEntity.getTypeInformation()).getDataType();
UdtValue udtValue = udtType.newValue();
udtValue.setString("zip", "69469");
udtValue.setString("city", "Weinheim");
udtValue.setList("streetlines", Arrays.asList("Heckenpfad", "14"), String.class);
AddressBook addressBook = new AddressBook();
addressBook.setId("1");
addressBook.setAlternate(udtValue);
SimpleStatement statement = new StatementFactory(converter).insert(addressBook, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("INSERT INTO addressbook (alternate,id) "
+ "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']},'1')");
}
@Test // DATACASS-172
void shouldWriteUdtPk() {
AddressUserType addressUserType = new AddressUserType();
addressUserType.setZip("69469");
addressUserType.setCity("Weinheim");
addressUserType.setStreetLines(Arrays.asList("Heckenpfad", "14"));
WithMappedUdtId withUdtId = new WithMappedUdtId();
withUdtId.setId(addressUserType);
SimpleStatement statement = new StatementFactory(converter).insert(withUdtId, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo(
"INSERT INTO withmappedudtid (id) " + "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']})");
}
@Test // DATACASS-172
void shouldWriteMappedUdtPk() {
CassandraPersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(AddressUserType.class);
com.datastax.oss.driver.api.core.type.UserDefinedType udtType = (com.datastax.oss.driver.api.core.type.UserDefinedType) converter
.getColumnTypeResolver().resolve(persistentEntity.getTypeInformation()).getDataType();
UdtValue udtValue = udtType.newValue();
udtValue.setString("zip", "69469");
udtValue.setString("city", "Weinheim");
udtValue.setList("streetlines", Arrays.asList("Heckenpfad", "14"), String.class);
WithUdtId withUdtId = new WithUdtId();
withUdtId.setId(udtValue);
SimpleStatement statement = new StatementFactory(converter).insert(withUdtId, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo(
"INSERT INTO withudtid (id) " + "VALUES ({zip:'69469',city:'Weinheim',streetlines:['Heckenpfad','14']})");
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteUdtWithCustomConversion() {
Bank bank = new Bank(null, new Currency("EUR"), null);
SimpleStatement statement = new StatementFactory(converter).insert(bank, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("INSERT INTO bank (currency) VALUES ({currency:'EUR'})");
}
@Test // DATACASS-172
void shouldWriteUdtWhereWherePrimaryKeyWithCustomConversion() {
Money money = new Money();
money.setCurrency(new Currency("EUR"));
Where where = new Where();
converter.write(money, where);
assertThat((UdtValue) where.get(CqlIdentifier.fromCql("currency"))) //
.extracting(UdtValue::getFormattedContents) //
.isEqualTo("{currency:'EUR'}");
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteUdtUpdateAssignmentsWithCustomConversion() {
MoneyTransfer money = new MoneyTransfer("1", new Currency("EUR"));
SimpleStatement statement = new StatementFactory(converter).update(money, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("UPDATE moneytransfer SET currency={currency:'EUR'} WHERE id='1'");
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteUdtListWithCustomConversion() {
Bank bank = new Bank(null, null, Collections.singletonList(new Currency("EUR")));
SimpleStatement statement = new StatementFactory(converter).insert(bank, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("INSERT INTO bank (othercurrencies) VALUES ([{currency:'EUR'}])");
}
@Test // DATACASS-487, DATACASS-623
void shouldReadMappedUdtInMap() {
@@ -111,7 +285,7 @@ class MappingCassandraConverterUDTUnitTests {
rowMock = RowMockUtil
.newRowMock(column("acceptedCurrencies", map, DataTypes.mapOf(manufacturer, DataTypes.listOf(currency))));
Supplier supplier = mappingCassandraConverter.read(Supplier.class, rowMock);
Supplier supplier = converter.read(Supplier.class, rowMock);
assertThat(supplier.getAcceptedCurrencies()).isNotEmpty();
@@ -130,7 +304,7 @@ class MappingCassandraConverterUDTUnitTests {
Map<CqlIdentifier, Object> insert = new LinkedHashMap<>();
mappingCassandraConverter.write(supplier, insert);
converter.write(supplier, insert);
Map<UdtValue, List<UdtValue>> acceptedcurrencies = (Map) insert.get(CqlIdentifier.fromCql("acceptedcurrencies"));
@@ -157,7 +331,7 @@ class MappingCassandraConverterUDTUnitTests {
Map<CqlIdentifier, Object> sink = new LinkedHashMap<>();
mappingCassandraConverter.write(entity, sink);
converter.write(entity, sink);
assertThat(sink).containsEntry(CqlIdentifier.fromInternal("id"), "id-1");
assertThat((UdtValue) sink.get(CqlIdentifier.fromInternal("udtvalue"))).extracting(UdtValue::getFormattedContents)
@@ -175,7 +349,7 @@ class MappingCassandraConverterUDTUnitTests {
Map<CqlIdentifier, Object> sink = new LinkedHashMap<>();
mappingCassandraConverter.write(entity, sink);
converter.write(entity, sink);
assertThat(sink).containsEntry(CqlIdentifier.fromInternal("id"), "id-1");
assertThat((UdtValue) sink.get(CqlIdentifier.fromInternal("udtvalue"))).extracting(UdtValue::getFormattedContents)
@@ -195,7 +369,7 @@ class MappingCassandraConverterUDTUnitTests {
Map<CqlIdentifier, Object> sink = new LinkedHashMap<>();
mappingCassandraConverter.write(entity, sink);
converter.write(entity, sink);
assertThat(sink).containsEntry(CqlIdentifier.fromInternal("id"), "id-1");
assertThat((UdtValue) sink.get(CqlIdentifier.fromInternal("udtvalue"))).extracting(UdtValue::getFormattedContents)
@@ -211,7 +385,7 @@ class MappingCassandraConverterUDTUnitTests {
rowMock = RowMockUtil.newRowMock(column("id", "id-1", DataTypes.TEXT),
column("udtvalue", udtValue, withnullableembeddedtype));
OuterWithNullableEmbeddedType target = mappingCassandraConverter.read(OuterWithNullableEmbeddedType.class, rowMock);
OuterWithNullableEmbeddedType target = converter.read(OuterWithNullableEmbeddedType.class, rowMock);
assertThat(target.getId()).isEqualTo("id-1");
assertThat(target.udtValue).isNotNull();
assertThat(target.udtValue.value).isEqualTo("value-string");
@@ -228,7 +402,7 @@ class MappingCassandraConverterUDTUnitTests {
rowMock = RowMockUtil.newRowMock(column("id", "id-1", DataTypes.TEXT),
column("udtvalue", udtValue, withprefixednullableembeddedtype));
OuterWithPrefixedNullableEmbeddedType target = mappingCassandraConverter
OuterWithPrefixedNullableEmbeddedType target = converter
.read(OuterWithPrefixedNullableEmbeddedType.class, rowMock);
assertThat(target.getId()).isEqualTo("id-1");
assertThat(target.udtValue).isNotNull();
@@ -237,16 +411,44 @@ class MappingCassandraConverterUDTUnitTests {
assertThat(target.udtValue.nested.age).isEqualTo(30);
}
@Test // DATACASS-172, DATACASS-400
void shouldWriteNestedUdt() {
Engine engine = new Engine(new Manufacturer("a good one", "display name"));
Car car = new Car("1", engine);
SimpleStatement statement = new StatementFactory(converter).insert(car, WriteOptions.empty())
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery())
.isEqualTo("INSERT INTO car (engine,id) VALUES ({manufacturer:{name:'a good one',displayname:NULL}},'1')");
}
@Table
@Getter
@AllArgsConstructor
private static class Car {
@Id String id;
Engine engine;
}
@UserDefinedType
@Getter
@AllArgsConstructor
private static class Engine {
Manufacturer manufacturer;
}
@UserDefinedType
@Data
@AllArgsConstructor
private static class Manufacturer {
String name;
@ReadOnlyProperty String displayName;
}
@UserDefinedType
@Data
@AllArgsConstructor
private static class Currency {
@@ -317,4 +519,88 @@ class MappingCassandraConverterUDTUnitTests {
}
}
@Data
@Table
public static class AddressBook {
@Id private String id;
private AddressUserType currentaddress;
private List<AddressUserType> previousaddresses;
private UdtValue alternate;
}
@Data
@Table
public static class WithUdtId {
@Id private UdtValue id;
}
@Data
@Table
public static class WithMappedUdtId {
@Id private AddressUserType id;
}
@UserDefinedType("address")
@Data
public static class AddressUserType {
String zip;
String city;
List<String> streetLines;
}
@Table
@Getter
@AllArgsConstructor
private static class Bank {
@Id String id;
Currency currency;
List<Currency> otherCurrencies;
}
@Data
@Table
public static class Money {
@Id private Currency currency;
}
@Table
@AllArgsConstructor
@Getter
public static class MoneyTransfer {
@Id String id;
private Currency currency;
}
private static class UDTToCurrencyConverter implements Converter<UdtValue, Currency> {
@Override
public Currency convert(UdtValue source) {
return new Currency(source.getString("currency"));
}
}
private static class CurrencyToUDTConverter implements Converter<Currency, UdtValue> {
private final UserTypeResolver userTypeResolver;
CurrencyToUDTConverter(UserTypeResolver userTypeResolver) {
this.userTypeResolver = userTypeResolver;
}
@Override
public UdtValue convert(Currency source) {
com.datastax.oss.driver.api.core.type.UserDefinedType userType = userTypeResolver
.resolveType(CqlIdentifier.fromCql("currency"));
UdtValue udtValue = userType.newValue();
udtValue.setString("currency", source.getCurrency());
return udtValue;
}
}
}

View File

@@ -40,6 +40,7 @@ import java.util.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;

View File

@@ -55,7 +55,8 @@ class StatementBuilderUnitTests {
void shouldApplyBindFunction() {
SimpleStatement statement = StatementBuilder.of(QueryBuilder.selectFrom("person").all())
.bind((select, factory) -> select.where(Relation.column("foo").isEqualTo(factory.create("bar")))).build();
.bind((select, factory) -> select.where(Relation.column("foo").isEqualTo(factory.create("bar"))))
.build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person WHERE foo='bar'");
}
@@ -122,7 +123,7 @@ class StatementBuilderUnitTests {
.bind((select, factory) -> select.where(Relation.column("foo").isEqualTo(factory.create("bar"))))
.apply(select -> select.orderBy("one", ClusteringOrder.ASC))
.bind((select, factory) -> select.where(Relation.column("bar").isEqualTo(factory.create("baz"))))
.apply(select -> select.orderBy("two", ClusteringOrder.ASC)).build();
.apply(select -> select.orderBy("two", ClusteringOrder.ASC)).build(StatementBuilder.ParameterHandling.INLINE);
assertThat(statement.getQuery())
.isEqualTo("SELECT * FROM person WHERE foo='bar' AND bar='baz' ORDER BY one ASC,two ASC");

View File

@@ -22,6 +22,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
@@ -164,9 +165,10 @@ class PartTreeCassandraQueryUnitTests {
@Test // DATACASS-172
void shouldDeriveSimpleQueryWithMappedUDT() {
String query = deriveQueryFromMethod("findByMainAddress", new AddressType());
SimpleStatement query = deriveQueryFromMethod(Repo.class, "findByMainAddress", new Class[] { AddressType.class },
new AddressType());
assertThat(query).isEqualTo("SELECT * FROM person WHERE mainaddress={city:NULL,country:NULL}");
assertThat(query.getQuery()).isEqualTo("SELECT * FROM person WHERE mainaddress=?");
}
@Test // DATACASS-172
@@ -175,7 +177,7 @@ class PartTreeCassandraQueryUnitTests {
String query = deriveQueryFromMethod(Repo.class, "findByMainAddress", new Class[] { UdtValue.class }, udtValue)
.getQuery();
assertThat(query).isEqualTo("SELECT * FROM person WHERE mainaddress={city:NULL,country:NULL}");
assertThat(query).isEqualTo("SELECT * FROM person WHERE mainaddress=?");
}
@Test // DATACASS-357
@@ -193,7 +195,7 @@ class PartTreeCassandraQueryUnitTests {
SimpleStatement query = deriveQueryFromMethod(GroupRepository.class, "findByIdHashPrefix",
new Class[] { String.class }, "foo");
assertThat(query.getQuery()).isEqualTo("SELECT * FROM group WHERE hash_prefix='foo'");
assertThat(query.getQuery()).isEqualTo("SELECT * FROM group WHERE hash_prefix=?");
}
@Test // DATACASS-376
@@ -201,7 +203,7 @@ class PartTreeCassandraQueryUnitTests {
SimpleStatement query = deriveQueryFromMethod(Repo.class, "findByFirstname", new Class[] { String.class }, "foo");
assertThat(query.getQuery()).isEqualTo("SELECT * FROM person WHERE firstname='foo' ALLOW FILTERING");
assertThat(query.getQuery()).isEqualTo("SELECT * FROM person WHERE firstname=? ALLOW FILTERING");
}
@Test // DATACASS-146
@@ -211,7 +213,7 @@ class PartTreeCassandraQueryUnitTests {
SimpleStatement statement = deriveQueryFromMethod(Repo.class, "findByFirstname",
new Class[] { QueryOptions.class, String.class }, queryOptions, "Walter");
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person WHERE firstname='Walter'");
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person WHERE firstname=?");
assertThat(statement.getPageSize()).isEqualTo(777);
}
@@ -238,7 +240,7 @@ class PartTreeCassandraQueryUnitTests {
SimpleStatement statement = deriveQueryFromMethod(Repo.class, "deleteAllByLastname", new Class[] { String.class },
"Walter");
assertThat(statement.getQuery()).isEqualTo("DELETE FROM person WHERE lastname='Walter'");
assertThat(statement.getQuery()).isEqualTo("DELETE FROM person WHERE lastname=?");
}
@Test // DATACASS-512
@@ -257,7 +259,19 @@ class PartTreeCassandraQueryUnitTests {
types[i] = ClassUtils.getUserClass(args[i].getClass());
}
return deriveQueryFromMethod(Repo.class, method, types, args).getQuery();
SimpleStatement statement = deriveQueryFromMethod(Repo.class, method, types, args);
String query = statement.getQuery();
List<Object> positionalValues = statement.getPositionalValues();
for (Object positionalValue : positionalValues) {
query = query.replaceFirst("\\?",
positionalValue != null
? CodecRegistry.DEFAULT.codecFor((Class) positionalValue.getClass()).format(positionalValue)
: "null");
}
return query;
}
private SimpleStatement deriveQueryFromMethod(Class<?> repositoryInterface, String method, Class<?>[] types,

View File

@@ -24,6 +24,7 @@ import rx.Single;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -47,6 +48,7 @@ import org.springframework.util.ClassUtils;
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link ReactivePartTreeCassandraQuery}.
@@ -117,7 +119,7 @@ class ReactivePartTreeCassandraQueryUnitTests {
SimpleStatement statement = deriveQueryFromMethod(Repo.class, "findByFirstname",
new Class[] { QueryOptions.class, String.class }, queryOptions, "Walter");
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person WHERE firstname='Walter'");
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person WHERE firstname=?");
assertThat(statement.getPageSize()).isEqualTo(777);
}
@@ -145,7 +147,7 @@ class ReactivePartTreeCassandraQueryUnitTests {
SimpleStatement statement = deriveQueryFromMethod(PartTreeCassandraQueryUnitTests.Repo.class, "deleteAllByLastname",
new Class[] { String.class }, "Walter");
assertThat(statement.getQuery()).isEqualTo("DELETE FROM person WHERE lastname='Walter'");
assertThat(statement.getQuery()).isEqualTo("DELETE FROM person WHERE lastname=?");
}
@Test // DATACASS-512
@@ -165,7 +167,20 @@ class ReactivePartTreeCassandraQueryUnitTests {
types[i] = ClassUtils.getUserClass(args[i].getClass());
}
return deriveQueryFromMethod(Repo.class, method, types, args).getQuery();
SimpleStatement statement = deriveQueryFromMethod(Repo.class, method, types, args);
String query = statement.getQuery();
List<Object> positionalValues = statement.getPositionalValues();
for (Object positionalValue : positionalValues) {
query = query.replaceFirst("\\?",
positionalValue != null
? CodecRegistry.DEFAULT.codecFor((Class) positionalValue.getClass()).format(positionalValue)
: "NULL");
}
return query;
}
private SimpleStatement deriveQueryFromMethod(Class<?> repositoryInterface, String method, Class<?>[] types,

View File

@@ -61,7 +61,7 @@ public class UserDefinedTypeBuilder {
DefaultUserDefinedType type = new DefaultUserDefinedType(CqlIdentifier.fromCql("system"), this.typeName, false,
new ArrayList<>(fields.keySet()), new ArrayList<>(fields.values()));
return new UserDefinedTypeWrapper(type);
return type;
}
private static class UserDefinedTypeWrapper implements UserDefinedType {
@@ -138,10 +138,29 @@ public class UserDefinedTypeBuilder {
return delegate.newValue(fields);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj) || delegate.equals(obj);
}
@Override
@NonNull
public AttachmentPoint getAttachmentPoint() {
return delegate.getAttachmentPoint();
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer();
sb.append(getClass().getSimpleName());
sb.append(" [delegate=").append(delegate);
sb.append(']');
return sb.toString();
}
}
}