Skip to content

Commit 1d1ae65

Browse files
committed
Introduce customization hook for PreparedStatementHandler.
CassandraTemplate and its asynchronous and reactive variants now define a createPreparedStatementHandler() template method to customize CQL preparation and binding. Closes #1237
1 parent 8356323 commit 1d1ae65

File tree

3 files changed

+93
-16
lines changed

3 files changed

+93
-16
lines changed

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

+46-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.Collections;
1919
import java.util.List;
20+
import java.util.concurrent.CompletionStage;
2021
import java.util.function.Consumer;
2122
import java.util.function.Function;
2223
import java.util.stream.Collectors;
@@ -33,6 +34,7 @@
3334
import org.springframework.dao.DataAccessException;
3435
import org.springframework.dao.OptimisticLockingFailureException;
3536
import org.springframework.dao.support.DataAccessUtils;
37+
import org.springframework.dao.support.PersistenceExceptionTranslator;
3638
import org.springframework.data.cassandra.SessionFactory;
3739
import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity;
3840
import org.springframework.data.cassandra.core.convert.CassandraConverter;
@@ -822,6 +824,19 @@ public ListenableFuture<Void> truncate(Class<?> entityClass) {
822824
// Implementation hooks and utility methods
823825
// -------------------------------------------------------------------------
824826

827+
/**
828+
* Create a new statement-based {@link AsyncPreparedStatementHandler} using the statement passed in.
829+
* <p>
830+
* This method allows for the creation to be overridden by subclasses.
831+
*
832+
* @param statement the statement to be prepared.
833+
* @return the new {@link PreparedStatementHandler} to use.
834+
* @since 3.3.3
835+
*/
836+
protected AsyncPreparedStatementHandler createPreparedStatementHandler(Statement<?> statement) {
837+
return new PreparedStatementHandler(statement, exceptionTranslator);
838+
}
839+
825840
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
826841
SimpleStatement statement) {
827842

@@ -874,7 +889,7 @@ private <T> ListenableFuture<List<T>> doQuery(Statement<?> statement, RowMapper<
874889

875890
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
876891

877-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
892+
AsyncPreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
878893
return getAsyncCqlOperations().query(statementHandler, statementHandler, rowMapper);
879894
}
880895

@@ -885,7 +900,7 @@ private ListenableFuture<Void> doQuery(Statement<?> statement, RowCallbackHandle
885900

886901
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
887902

888-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
903+
AsyncPreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
889904
return getAsyncCqlOperations().query(statementHandler, statementHandler, callbackHandler);
890905
}
891906

@@ -900,7 +915,7 @@ private <T> ListenableFuture<T> doExecute(Statement<?> statement, Function<Async
900915

901916
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
902917

903-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
918+
AsyncPreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
904919
return getAsyncCqlOperations().query(statementHandler, statementHandler,
905920
(AsyncResultSetExtractor<T>) resultSet -> new AsyncResult<>(mappingFunction.apply(resultSet)));
906921
}
@@ -1021,19 +1036,33 @@ protected T adapt(@Nullable S adapteeResult) {
10211036
}
10221037
}
10231038

1039+
/**
1040+
* General callback interface used to create and bind prepared CQL statements.
1041+
* <p>
1042+
* This interface prepares the CQL statement and sets values on a {@link PreparedStatement} as union-type comprised
1043+
* from {@link AsyncPreparedStatementCreator}, {@link PreparedStatementBinder}, and {@link CqlProvider}.
1044+
*
1045+
* @since 3.3.3
1046+
*/
1047+
public interface AsyncPreparedStatementHandler
1048+
extends AsyncPreparedStatementCreator, PreparedStatementBinder, CqlProvider {
1049+
1050+
}
1051+
10241052
/**
10251053
* Utility class to prepare a {@link SimpleStatement} and bind values associated with the statement to a
10261054
* {@link BoundStatement}.
10271055
*
10281056
* @since 3.2
10291057
*/
1030-
private class PreparedStatementHandler
1031-
implements AsyncPreparedStatementCreator, PreparedStatementBinder, CqlProvider {
1058+
public static class PreparedStatementHandler implements AsyncPreparedStatementHandler {
10321059

10331060
private final SimpleStatement statement;
1061+
private final PersistenceExceptionTranslator exceptionTranslator;
10341062

1035-
public PreparedStatementHandler(Statement<?> statement) {
1063+
public PreparedStatementHandler(Statement<?> statement, PersistenceExceptionTranslator exceptionTranslator) {
10361064
this.statement = PreparedStatementDelegate.getStatementForPrepare(statement);
1065+
this.exceptionTranslator = exceptionTranslator;
10371066
}
10381067

10391068
/*
@@ -1042,7 +1071,17 @@ public PreparedStatementHandler(Statement<?> statement) {
10421071
*/
10431072
@Override
10441073
public ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
1045-
return new CassandraFutureAdapter<>(session.prepareAsync(statement), exceptionTranslator);
1074+
return new CassandraFutureAdapter<>(doPrepare(session), exceptionTranslator);
1075+
}
1076+
1077+
/**
1078+
* Invokes the statement preparation.
1079+
*
1080+
* @param session
1081+
* @return
1082+
*/
1083+
protected CompletionStage<PreparedStatement> doPrepare(CqlSession session) {
1084+
return session.prepareAsync(statement);
10461085
}
10471086

10481087
/*

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

+16-3
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,19 @@ public ExecutableDelete delete(Class<?> domainType) {
886886
// Implementation hooks and utility methods
887887
// -------------------------------------------------------------------------
888888

889+
/**
890+
* Create a new statement-based {@link PreparedStatementHandler} using the statement passed in.
891+
* <p>
892+
* This method allows for the creation to be overridden by subclasses.
893+
*
894+
* @param statement the statement to be prepared.
895+
* @return the new {@link PreparedStatementHandler} to use.
896+
* @since 3.3.3
897+
*/
898+
protected PreparedStatementHandler createPreparedStatementHandler(Statement<?> statement) {
899+
return new PreparedStatementHandler(statement);
900+
}
901+
889902
private <T> EntityWriteResult<T> executeSave(T entity, CqlIdentifier tableName, SimpleStatement statement) {
890903
return executeSave(entity, tableName, statement, ignore -> {});
891904
}
@@ -922,7 +935,7 @@ private <T> List<T> doQuery(Statement<?> statement, RowMapper<T> rowMapper) {
922935

923936
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
924937

925-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
938+
PreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
926939
return getCqlOperations().query(statementHandler, statementHandler, rowMapper);
927940
}
928941

@@ -937,7 +950,7 @@ private <T> Stream<T> doQueryForStream(Statement<?> statement, RowMapper<T> rowM
937950

938951
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
939952

940-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
953+
PreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
941954
return getCqlOperations().queryForStream(statementHandler, statementHandler, rowMapper);
942955
}
943956

@@ -956,7 +969,7 @@ private <T> T doExecute(Statement<?> statement, Function<ResultSet, T> mappingFu
956969

957970
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
958971

959-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
972+
PreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
960973
return getCqlOperations().query(statementHandler, statementHandler, mappingFunction::apply);
961974
}
962975

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

+31-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package org.springframework.data.cassandra.core;
1717

18-
import org.springframework.data.projection.EntityProjection;
1918
import reactor.core.publisher.Flux;
2019
import reactor.core.publisher.Mono;
2120
import reactor.core.publisher.SynchronousSink;
@@ -62,6 +61,7 @@
6261
import org.springframework.data.domain.SliceImpl;
6362
import org.springframework.data.mapping.callback.EntityCallbacks;
6463
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
64+
import org.springframework.data.projection.EntityProjection;
6565
import org.springframework.data.projection.ProjectionFactory;
6666
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
6767
import org.springframework.lang.Nullable;
@@ -852,6 +852,19 @@ public ReactiveUpdate update(Class<?> domainType) {
852852
// Implementation hooks and utility methods
853853
// -------------------------------------------------------------------------
854854

855+
/**
856+
* Create a new statement-based {@link ReactivePreparedStatementHandler} using the statement passed in.
857+
* <p>
858+
* This method allows for the creation to be overridden by subclasses.
859+
*
860+
* @param statement the statement to be prepared.
861+
* @return the new {@link PreparedStatementHandler} to use.
862+
* @since 3.3.3
863+
*/
864+
protected ReactivePreparedStatementHandler createPreparedStatementHandler(Statement<?> statement) {
865+
return new PreparedStatementHandler(statement);
866+
}
867+
855868
private <T> Mono<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName, SimpleStatement statement) {
856869
return executeSave(entity, tableName, statement, (writeResult, sink) -> sink.next(writeResult));
857870
}
@@ -888,7 +901,7 @@ private <T> Flux<T> doQuery(Statement<?> statement, RowMapper<T> rowMapper) {
888901

889902
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
890903

891-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
904+
ReactivePreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
892905
return getReactiveCqlOperations().query(statementHandler, statementHandler, rowMapper);
893906
}
894907

@@ -899,7 +912,7 @@ private <T> Mono<T> doExecute(Statement<?> statement, Function<ReactiveResultSet
899912

900913
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
901914

902-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
915+
ReactivePreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
903916
return getReactiveCqlOperations()
904917
.query(statementHandler, statementHandler, rs -> Mono.just(mappingFunction.apply(rs))).next();
905918
}
@@ -912,7 +925,7 @@ private <T> Mono<T> doExecuteAndFlatMap(Statement<?> statement,
912925

913926
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
914927

915-
PreparedStatementHandler statementHandler = new PreparedStatementHandler(statement);
928+
ReactivePreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
916929
return getReactiveCqlOperations().query(statementHandler, statementHandler, mappingFunction::apply).next();
917930
}
918931

@@ -1014,14 +1027,26 @@ protected <T> Mono<T> maybeCallBeforeSave(T object, CqlIdentifier tableName, Sta
10141027
return Mono.just(object);
10151028
}
10161029

1030+
/**
1031+
* General callback interface used to create and bind prepared CQL statements.
1032+
* <p>
1033+
* This interface prepares the CQL statement and sets values on a {@link PreparedStatement} as union-type comprised
1034+
* from {@link ReactivePreparedStatementCreator}, {@link PreparedStatementBinder}, and {@link CqlProvider}.
1035+
*
1036+
* @since 3.3.3
1037+
*/
1038+
public interface ReactivePreparedStatementHandler
1039+
extends ReactivePreparedStatementCreator, PreparedStatementBinder, CqlProvider {
1040+
1041+
}
1042+
10171043
/**
10181044
* Utility class to prepare a {@link SimpleStatement} and bind values associated with the statement to a
10191045
* {@link BoundStatement}.
10201046
*
10211047
* @since 3.2
10221048
*/
1023-
private static class PreparedStatementHandler
1024-
implements ReactivePreparedStatementCreator, PreparedStatementBinder, CqlProvider {
1049+
public static class PreparedStatementHandler implements ReactivePreparedStatementHandler {
10251050

10261051
private final SimpleStatement statement;
10271052

0 commit comments

Comments
 (0)