Skip to content

Commit 66a0bf9

Browse files
committed
Apply configured CodecRegistry to StatementBuilder.
Closes #1114
1 parent bb2b7a4 commit 66a0bf9

File tree

4 files changed

+162
-18
lines changed

4 files changed

+162
-18
lines changed

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ public StatementBuilder<Select> selectOneById(Object id, CassandraPersistentEnti
238238

239239
cassandraConverter.write(id, where, entity);
240240

241-
return StatementBuilder.of(QueryBuilder.selectFrom(getKeyspace(entity, tableName), tableName).all().limit(1))
241+
return StatementBuilder
242+
.of(QueryBuilder.selectFrom(getKeyspace(entity, tableName), tableName).all().limit(1),
243+
cassandraConverter.getCodecRegistry())
242244
.bind((statement, factory) -> statement.where(toRelations(where, factory)));
243245
}
244246

@@ -325,7 +327,8 @@ public StatementBuilder<RegularInsert> insert(Object objectToInsert, WriteOption
325327
cassandraConverter.write(objectToInsert, object, entity);
326328

327329
StatementBuilder<RegularInsert> builder = StatementBuilder
328-
.of(QueryBuilder.insertInto(getKeyspace(entity, tableName), tableName).valuesByIds(Collections.emptyMap()))
330+
.of(QueryBuilder.insertInto(getKeyspace(entity, tableName), tableName).valuesByIds(Collections.emptyMap()),
331+
cassandraConverter.getCodecRegistry())
329332
.bind((statement, factory) -> {
330333

331334
Map<CqlIdentifier, Term> values = createTerms(insertNulls, object, factory);
@@ -457,7 +460,9 @@ public StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update>
457460
where.forEach((cqlIdentifier, o) -> object.remove(cqlIdentifier));
458461

459462
StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> builder = StatementBuilder
460-
.of(QueryBuilder.update(getKeyspace(entity, tableName), tableName).set().where()).bind((statement, factory) -> {
463+
.of(QueryBuilder.update(getKeyspace(entity, tableName), tableName).set().where(),
464+
cassandraConverter.getCodecRegistry())
465+
.bind((statement, factory) -> {
461466

462467
CqlStatementOptionsAccessor<UpdateStart> accessor = factory.ifBoundOrInline(
463468
bindings -> CqlStatementOptionsAccessor.ofUpdate(bindings, (UpdateStart) statement),
@@ -492,7 +497,9 @@ public StatementBuilder<Delete> deleteById(Object id, CassandraPersistentEntity<
492497

493498
cassandraConverter.write(id, where, entity);
494499

495-
return StatementBuilder.of(QueryBuilder.deleteFrom(getKeyspace(entity, tableName), tableName).where())
500+
return StatementBuilder
501+
.of(QueryBuilder.deleteFrom(getKeyspace(entity, tableName), tableName).where(),
502+
cassandraConverter.getCodecRegistry())
496503
.bind((statement, factory) -> statement.where(toRelations(where, factory)));
497504
}
498505

@@ -565,7 +572,8 @@ public StatementBuilder<Delete> delete(Object entity, QueryOptions options, Enti
565572
.getRequiredPersistentEntity(ProxyUtils.getUserClass(entity.getClass()));
566573

567574
StatementBuilder<Delete> builder = StatementBuilder
568-
.of(QueryBuilder.deleteFrom(getKeyspace(persistentEntity, tableName), tableName).where())
575+
.of(QueryBuilder.deleteFrom(getKeyspace(persistentEntity, tableName), tableName).where(),
576+
cassandraConverter.getCodecRegistry())
569577
.bind((statement, factory) -> {
570578

571579
Delete statementToUse;
@@ -697,7 +705,7 @@ private StatementBuilder<Select> createSelectAndOrder(List<Selector> selectors,
697705
select = QueryBuilder.selectFrom(getKeyspace(entity, from), from).selectors(mappedSelectors);
698706
}
699707

700-
StatementBuilder<Select> builder = StatementBuilder.of(select);
708+
StatementBuilder<Select> builder = StatementBuilder.of(select, cassandraConverter.getCodecRegistry());
701709

702710
builder.bind((statement, factory) -> {
703711
return statement.where(getRelations(filter, factory));
@@ -758,7 +766,8 @@ private StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update>
758766

759767
UpdateStart updateStart = QueryBuilder.update(getKeyspace(entity, table), table);
760768

761-
return StatementBuilder.of((com.datastax.oss.driver.api.querybuilder.update.Update) updateStart)
769+
return StatementBuilder
770+
.of((com.datastax.oss.driver.api.querybuilder.update.Update) updateStart, cassandraConverter.getCodecRegistry())
762771
.bind((statement, factory) -> {
763772

764773
com.datastax.oss.driver.api.querybuilder.update.Update statementToUse;
@@ -918,7 +927,7 @@ private StatementBuilder<Delete> delete(List<CqlIdentifier> columnNames, Cassand
918927
select = select.column(columnName);
919928
}
920929

921-
return StatementBuilder.of(select.where()).bind((statement, factory) -> {
930+
return StatementBuilder.of(select.where(), cassandraConverter.getCodecRegistry()).bind((statement, factory) -> {
922931

923932
WriteOptions options = optionsOptional.orElse(null);
924933
Delete statementToUse;

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/cql/util/StatementBuilder.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
* Functional builder for Cassandra {@link BuildableQuery statements}. Statements are built by applying
4444
* {@link UnaryOperator builder functions} that get applied when {@link #build() building} the actual
4545
* {@link SimpleStatement statement}. The {@code StatementBuilder} provides a mutable container for statement creation
46-
* allowing a functional declaration of actions that are necessary to build a statement. This class helps building CQL
47-
* statements as a {@link BuildableQuery} classes are typically immutable and require return value tracking across
46+
* allowing a functional declaration of actions that are necessary to build a statement. This class helps with building
47+
* CQL statements as a {@link BuildableQuery} classes are typically immutable and require return value tracking across
4848
* methods that want to apply modifications to a statement.
4949
* <p>
5050
* Building a statement consists of three phases:
@@ -61,7 +61,11 @@
6161
* The builder can be used for structural evolution and value evolution of statements. Values are bound through
6262
* {@link BindFunction binding functions} that accept the statement and a {@link TermFactory}. Values can be bound
6363
* inline or through bind markers when {@link #build(ParameterHandling, CodecRegistry) building} the statement. All
64-
* functions are applied in the order of their declaration.
64+
* functions remain in the order of their declaration.
65+
* <p>
66+
* {@link ParameterHandling#INLINE Inline} rendering of parameters requires a {@link CodecRegistry}. A StatementBuilder
67+
* can be {@link StatementBuilder#of(BuildableQuery, CodecRegistry) created} by providing a custom CodecRegistry.
68+
* Otherwise, the registry falls back to {@link CodecRegistry#DEFAULT}.
6569
* <p>
6670
* All methods returning {@link StatementBuilder} point to the same instance. This class is intended for internal use.
6771
*
@@ -72,14 +76,16 @@
7276
public class StatementBuilder<S extends BuildableQuery> {
7377

7478
private final S statement;
79+
private final CodecRegistry registry;
7580

7681
private final List<BuilderRunnable<S>> queryActions = new ArrayList<>();
7782
private final List<Consumer<SimpleStatementBuilder>> onBuild = new ArrayList<>();
7883
private final List<UnaryOperator<SimpleStatement>> onBuilt = new ArrayList<>();
7984

8085
/**
8186
* Factory method used to create a new {@link StatementBuilder} with the given {@link BuildableQuery query stub}. The
82-
* stub is used as base for the built query so each query inherits properties of this stub.
87+
* stub is used as base for the built query so each query inherits properties of this stub. This factory method
88+
* initializes StatementBuilder with the default {@link CodecRegistry#DEFAULT CodecRegistry}.
8389
*
8490
* @param <S> query type.
8591
* @param stub the {@link BuildableQuery query stub} to use.
@@ -88,10 +94,27 @@ public class StatementBuilder<S extends BuildableQuery> {
8894
* @see com.datastax.oss.driver.api.querybuilder.BuildableQuery
8995
*/
9096
public static <S extends BuildableQuery> StatementBuilder<S> of(S stub) {
97+
return of(stub, CodecRegistry.DEFAULT);
98+
}
99+
100+
/**
101+
* Factory method used to create a new {@link StatementBuilder} with the given {@link BuildableQuery query stub}. The
102+
* stub is used as base for the built query so each query inherits properties of this stub.
103+
*
104+
* @param <S> query type.
105+
* @param stub the {@link BuildableQuery query stub} to use.
106+
* @param registry the default {@link CodecRegistry} to use for inline parameter rendering.
107+
* @return a {@link StatementBuilder} for the given {@link BuildableQuery query stub}.
108+
* @throws IllegalArgumentException if the {@link BuildableQuery query stub} is {@literal null}.
109+
* @see com.datastax.oss.driver.api.querybuilder.BuildableQuery
110+
* @since 4.4
111+
*/
112+
public static <S extends BuildableQuery> StatementBuilder<S> of(S stub, CodecRegistry registry) {
91113

92114
Assert.notNull(stub, "Query stub must not be null");
115+
Assert.notNull(registry, "CodecRegistry stub must not be null");
93116

94-
return new StatementBuilder<>(stub);
117+
return new StatementBuilder<>(stub, registry);
95118
}
96119

97120
/**
@@ -101,8 +124,9 @@ public static <S extends BuildableQuery> StatementBuilder<S> of(S stub) {
101124
* {@link com.datastax.oss.driver.api.core.cql.Statement}.
102125
* @see com.datastax.oss.driver.api.querybuilder.BuildableQuery
103126
*/
104-
private StatementBuilder(S statement) {
127+
private StatementBuilder(S statement, CodecRegistry registry) {
105128
this.statement = statement;
129+
this.registry = registry;
106130
}
107131

108132
/**
@@ -177,7 +201,7 @@ public StatementBuilder<S> transform(UnaryOperator<SimpleStatement> mappingFunct
177201
* @return the built {@link SimpleStatement}.
178202
*/
179203
public SimpleStatement build() {
180-
return build(ParameterHandling.BY_INDEX, CodecRegistry.DEFAULT);
204+
return build(ParameterHandling.BY_INDEX, this.registry);
181205
}
182206

183207
/**
@@ -188,7 +212,7 @@ public SimpleStatement build() {
188212
* @return the built {@link SimpleStatement}.
189213
*/
190214
public SimpleStatement build(ParameterHandling parameterHandling) {
191-
return build(parameterHandling, CodecRegistry.DEFAULT);
215+
return build(parameterHandling, this.registry);
192216
}
193217

194218
/**

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/StatementFactoryUnitTests.java

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.springframework.data.cassandra.core.query.Criteria.*;
2020
import static org.springframework.data.domain.Sort.Direction.*;
2121

22+
import java.nio.ByteBuffer;
2223
import java.time.Duration;
2324
import java.util.Arrays;
2425
import java.util.Collections;
@@ -29,7 +30,6 @@
2930
import org.junit.jupiter.api.Test;
3031

3132
import org.springframework.data.annotation.Id;
32-
import org.springframework.data.cassandra.core.convert.CassandraConverter;
3333
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
3434
import org.springframework.data.cassandra.core.convert.UpdateMapper;
3535
import org.springframework.data.cassandra.core.cql.QueryOptions;
@@ -47,10 +47,16 @@
4747

4848
import com.datastax.oss.driver.api.core.CqlIdentifier;
4949
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
50+
import com.datastax.oss.driver.api.core.ProtocolVersion;
5051
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
52+
import com.datastax.oss.driver.api.core.type.DataType;
53+
import com.datastax.oss.driver.api.core.type.DataTypes;
54+
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
55+
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
5156
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
5257
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;
5358
import com.datastax.oss.driver.api.querybuilder.select.Select;
59+
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
5460

5561
/**
5662
* Unit tests for {@link StatementFactory}.
@@ -60,7 +66,7 @@
6066
*/
6167
class StatementFactoryUnitTests {
6268

63-
private CassandraConverter converter = new MappingCassandraConverter();
69+
private MappingCassandraConverter converter = new MappingCassandraConverter();
6470

6571
private UpdateMapper updateMapper = new UpdateMapper(converter);
6672

@@ -850,6 +856,53 @@ void shouldCreateCountQuery() {
850856
.isEqualTo("SELECT count(1) FROM group WHERE foo='bar'");
851857
}
852858

859+
@Test // GH-1114
860+
void shouldConsiderCodecRegistry() {
861+
862+
DefaultCodecRegistry cr = new DefaultCodecRegistry("foo");
863+
cr.register(new TypeCodec<MyString>() {
864+
@Override
865+
public GenericType<MyString> getJavaType() {
866+
return GenericType.of(MyString.class);
867+
}
868+
869+
@Override
870+
public DataType getCqlType() {
871+
return DataTypes.TEXT;
872+
}
873+
874+
@Override
875+
public ByteBuffer encode(MyString value, ProtocolVersion protocolVersion) {
876+
return null;
877+
}
878+
879+
@Override
880+
public MyString decode(ByteBuffer bytes, ProtocolVersion protocolVersion) {
881+
return null;
882+
}
883+
884+
@Override
885+
public String format(MyString value) {
886+
return "'" + value.value() + "'";
887+
}
888+
889+
@Override
890+
public MyString parse(String value) {
891+
return new MyString(value);
892+
}
893+
});
894+
895+
converter.setCodecRegistry(cr);
896+
897+
Query query = Query.query(where("foo").is(new MyString("bar")));
898+
899+
StatementBuilder<Select> count = statementFactory.count(query,
900+
converter.getMappingContext().getRequiredPersistentEntity(Group.class));
901+
902+
assertThat(count.build(ParameterHandling.INLINE).getQuery())
903+
.isEqualTo("SELECT count(1) FROM group WHERE foo='bar'");
904+
}
905+
853906
@SuppressWarnings("unused")
854907
static class Person {
855908

@@ -865,4 +918,8 @@ static class Person {
865918

866919
@Column("first_name") private String firstName;
867920
}
921+
922+
record MyString(String value) {
923+
924+
}
868925
}

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/cql/util/StatementBuilderUnitTests.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,22 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import java.nio.ByteBuffer;
2021
import java.util.Collections;
2122

2223
import org.junit.jupiter.api.Test;
2324

2425
import com.datastax.oss.driver.api.core.CqlIdentifier;
26+
import com.datastax.oss.driver.api.core.ProtocolVersion;
2527
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
2628
import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder;
29+
import com.datastax.oss.driver.api.core.type.DataType;
30+
import com.datastax.oss.driver.api.core.type.DataTypes;
31+
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
32+
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
2733
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
2834
import com.datastax.oss.driver.api.querybuilder.relation.Relation;
35+
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
2936

3037
/**
3138
* Unit tests for {@link StatementBuilder}.
@@ -151,4 +158,51 @@ void shouldTransformBuiltStatement() {
151158
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person");
152159
assertThat(statement.getExecutionProfileName()).isEqualTo("foo");
153160
}
161+
162+
@Test // GH-1114
163+
void shouldConsiderCodecRegistry() {
164+
165+
DefaultCodecRegistry cr = new DefaultCodecRegistry("foo");
166+
cr.register(new TypeCodec<MyString>() {
167+
@Override
168+
public GenericType<MyString> getJavaType() {
169+
return GenericType.of(MyString.class);
170+
}
171+
172+
@Override
173+
public DataType getCqlType() {
174+
return DataTypes.TEXT;
175+
}
176+
177+
@Override
178+
public ByteBuffer encode(MyString value, ProtocolVersion protocolVersion) {
179+
return null;
180+
}
181+
182+
@Override
183+
public MyString decode(ByteBuffer bytes, ProtocolVersion protocolVersion) {
184+
return null;
185+
}
186+
187+
@Override
188+
public String format(MyString value) {
189+
return "'" + value.value() + "'";
190+
}
191+
192+
@Override
193+
public MyString parse(String value) {
194+
return new MyString(value);
195+
}
196+
});
197+
198+
SimpleStatement statement = StatementBuilder.of(QueryBuilder.selectFrom("person").all(), cr)
199+
.bind((select, factory) -> select.where(Relation.column("foo").isEqualTo(factory.create(new MyString("bar")))))
200+
.build(StatementBuilder.ParameterHandling.INLINE);
201+
202+
assertThat(statement.getQuery()).isEqualTo("SELECT * FROM person WHERE foo='bar'");
203+
}
204+
205+
record MyString(String value) {
206+
207+
}
154208
}

0 commit comments

Comments
 (0)