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 772026a3b..8c762f099 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 @@ -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 { - private S statement; + private final S statement; - private List> queryActions = new ArrayList<>(); - private List> onBuild = new ArrayList<>(); - private List> onBuilt = new ArrayList<>(); + private final List> queryActions = new ArrayList<>(); + private final List> onBuild = new ArrayList<>(); + private final List> 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 { /** * 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 { 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) { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/AsyncCassandraTemplateUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/AsyncCassandraTemplateUnitTests.java index af35db38a..3138e3897 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/AsyncCassandraTemplateUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/AsyncCassandraTemplateUnitTests.java @@ -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 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 getUninterruptibly(Future future) { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java index 964930417..eb28c34c6 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java @@ -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 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 { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplateUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplateUnitTests.java index ae54df33c..6e259c457 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplateUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplateUnitTests.java @@ -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 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 { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java index 5e1a6a9f2..8a5a95ff0 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java @@ -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() { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java index c4cc4b1a4..9f36935a2 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java @@ -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 insert = new LinkedHashMap<>(); - mappingCassandraConverter.write(supplier, insert); + converter.write(supplier, insert); Map> acceptedcurrencies = (Map) insert.get(CqlIdentifier.fromCql("acceptedcurrencies")); @@ -157,7 +331,7 @@ class MappingCassandraConverterUDTUnitTests { Map 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 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 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 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 streetLines; + } + + @Table + @Getter + @AllArgsConstructor + private static class Bank { + + @Id String id; + Currency currency; + List 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 { + + @Override + public Currency convert(UdtValue source) { + return new Currency(source.getString("currency")); + } + } + + private static class CurrencyToUDTConverter implements Converter { + + 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; + } + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java index d12813786..e99a2123f 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java @@ -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; diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/util/StatementBuilderUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/util/StatementBuilderUnitTests.java index 35e2c7bb3..d2c2f759b 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/util/StatementBuilderUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/util/StatementBuilderUnitTests.java @@ -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"); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQueryUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQueryUnitTests.java index 2aecec65f..2a416ed4d 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQueryUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQueryUnitTests.java @@ -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 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, diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQueryUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQueryUnitTests.java index 757a552ee..5ece53aa7 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQueryUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQueryUnitTests.java @@ -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 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, diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/UserDefinedTypeBuilder.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/UserDefinedTypeBuilder.java index 973006e97..bf01c576b 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/UserDefinedTypeBuilder.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/support/UserDefinedTypeBuilder.java @@ -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(); + } } }