diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncCqlOperations.java b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncCqlOperations.java
new file mode 100644
index 000000000..3984586f8
--- /dev/null
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncCqlOperations.java
@@ -0,0 +1,722 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.cassandra.core;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.IncorrectResultSizeDataAccessException;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Statement;
+
+/**
+ * Interface specifying a basic set of CQL asynchronously executed operations. Exposes similar methods as {@link CqlTemplate}, but returns
+ * result handles or accepts callbacks as opposed to concrete results. Implemented by {@link AsyncCqlTemplate}. Not
+ * often used directly, but a useful option to enhance testability, as it can easily be mocked or stubbed.
+ *
+ * @author Mark Paluch
+ * @since 2.0
+ * @see AsyncCqlTemplate
+ */
+public interface AsyncCqlOperations {
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with a plain com.datastax.driver.core.Session
+ // -------------------------------------------------------------------------
+
+ /**
+ * Execute a CQL data access operation, implemented as callback action working on a
+ * {@link com.datastax.driver.core.Session}. This allows for implementing arbitrary data access operations, within
+ * Spring's managed CQL environment: that is, converting CQL
+ * {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
+ *
+ * The callback action can return a result object, for example a domain object or a collection of domain objects.
+ *
+ * @param action the callback object that specifies the action.
+ * @return a result object returned by the action, or {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture execute(AsyncSessionCallback action) throws DataAccessException;
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with static CQL
+ // -------------------------------------------------------------------------
+
+ /**
+ * Issue a single CQL execute, typically a DDL statement, insert, update or delete statement.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @return boolean value whether the statement was applied.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture execute(String cql) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rse object that will extract all rows of results, must not be {@literal null}.
+ * @return an arbitrary result object, as returned by the ResultSetExtractor.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #query(String, ResultSetExtractor, Object...)
+ */
+ ListenableFuture query(String cql, ResultSetExtractor rse) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
+ * {@link RowCallbackHandler}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@code null} as argument array.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query
+ * @see #query(String, RowCallbackHandler, Object[])
+ */
+ ListenableFuture query(String cql, RowCallbackHandler rch) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the result {@link List}, containing mapped objects.
+ * @throws DataAccessException if there is any problem executing the query
+ * @see #query(String, RowMapper, Object[])
+ */
+ ListenableFuture> query(String cql, RowMapper rowMapper) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@link #queryForObject(String, RowMapper, Object...)} method with
+ * {@literal null} as argument array.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the single mapped object.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForObject(String, RowMapper, Object[])
+ */
+ ListenableFuture queryForObject(String cql, RowMapper rowMapper) throws DataAccessException;
+
+ /**
+ * Execute a query for a result object, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@link #queryForObject(String, Class, Object...)} method with
+ * {@literal null} as argument array.
+ *
+ * This method is useful for running static CQL with a known outcome. The query is expected to be a single row/single
+ * column query; the returned result will be directly mapped to the corresponding object type.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param requiredType the type that the result object is expected to match, must not be {@literal null}.
+ * @return the result object of the required type, or {@link Mono#empty()} in case of CQL NULL.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row, or does not return
+ * exactly one column in that row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForObject(String, Class, Object[])
+ */
+ ListenableFuture queryForObject(String cql, Class requiredType) throws DataAccessException;
+
+ /**
+ * Execute a query for a result Map, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@link #queryForMap(String, Object...)} method with {@literal null}
+ * as argument array.
+ *
+ * The query is expected to be a single row query; the result row will be mapped to a Map (one entry for each column,
+ * using the column name as the key).
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @return the result Map (one entry for each column, using the column name as the key), must not be {@literal null}.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForMap(String, Object[])
+ * @see ColumnMapRowMapper
+ */
+ ListenableFuture> queryForMap(String cql) throws DataAccessException;
+
+ /**
+ * Execute a query for a result {@link List}, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code queryForList} method with {@literal null} as argument array.
+ *
+ * The results will be mapped to a {@link List} (one item for each row) of result objects, each of them matching the
+ * specified element type.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param elementType the required type of element in the result {@link List} (for example, {@code Integer.class}),
+ * must not be {@literal null}.
+ * @return a {@link List} of objects that match the specified element type.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForList(String, Class, Object[])
+ * @see SingleColumnRowMapper
+ */
+ ListenableFuture> queryForList(String cql, Class elementType) throws DataAccessException;
+
+ /**
+ * Execute a query for a result {@link List}, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code queryForList} method with {@literal null} as argument array.
+ *
+ * The results will be mapped to a {@link List} (one item for each row) of {@link Map}s (one entry for each column
+ * using the column name as the key). Each item in the {@link List} will be of the form returned by this interface's
+ * queryForMap() methods.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @return a {@link List} that contains a {@link Map} per row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForList(String, Object[])
+ */
+ ListenableFuture>> queryForList(String cql) throws DataAccessException;
+
+ /**
+ * Execute a query for a ResultSet, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code queryForResultSet} method with {@literal null} as argument
+ * array.
+ *
+ * The results will be mapped to an {@link ResultSet}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @return a {@link ResultSet} representation.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForResultSet(String, Object[])
+ */
+ ListenableFuture queryForResultSet(String cql) throws DataAccessException;
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with com.datastax.driver.core.Statement
+ // -------------------------------------------------------------------------
+
+ /**
+ * Issue a single CQL execute, typically a DDL statement, insert, update or delete statement.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @return boolean value whether the statement was applied.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture execute(Statement statement) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @param rse object that will extract all rows of results, must not be {@literal null}.
+ * @return an arbitrary result object, as returned by the ResultSetExtractor.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #query(String, ResultSetExtractor, Object...)
+ */
+ ListenableFuture query(Statement statement, ResultSetExtractor rse) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
+ * {@link RowCallbackHandler}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@code null} as argument array.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query
+ * @see #query(String, RowCallbackHandler, Object[])
+ */
+ ListenableFuture query(Statement statement, RowCallbackHandler rch) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the result {@link List}, containing mapped objects.
+ * @throws DataAccessException if there is any problem executing the query
+ * @see #query(String, RowMapper, Object[])
+ */
+ ListenableFuture> query(Statement statement, RowMapper rowMapper) throws DataAccessException;
+
+ /**
+ * Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@link #queryForObject(String, RowMapper, Object...)} method with
+ * {@literal null} as argument array.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the single mapped object.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForObject(String, RowMapper, Object[])
+ */
+ ListenableFuture queryForObject(Statement statement, RowMapper rowMapper) throws DataAccessException;
+
+ /**
+ * Execute a query for a result object, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@link #queryForObject(String, Class, Object...)} method with
+ * {@literal null} as argument array.
+ *
+ * This method is useful for running static CQL with a known outcome. The query is expected to be a single row/single
+ * column query; the returned result will be directly mapped to the corresponding object type.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @param requiredType the type that the result object is expected to match, must not be {@literal null}.
+ * @return the result object of the required type, or {@link Mono#empty()} in case of CQL NULL.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row, or does not return
+ * exactly one column in that row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForObject(String, Class, Object[])
+ */
+ ListenableFuture queryForObject(Statement statement, Class requiredType) throws DataAccessException;
+
+ /**
+ * Execute a query for a result Map, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@link #queryForMap(String, Object...)} method with {@literal null}
+ * as argument array.
+ *
+ * The query is expected to be a single row query; the result row will be mapped to a Map (one entry for each column,
+ * using the column name as the key).
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @return the result Map (one entry for each column, using the column name as the key), must not be {@literal null}.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForMap(String, Object[])
+ * @see ColumnMapRowMapper
+ */
+ ListenableFuture> queryForMap(Statement statement) throws DataAccessException;
+
+ /**
+ * Execute a query for a result {@link List}, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code queryForList} method with {@literal null} as argument array.
+ *
+ * The results will be mapped to a {@link List} (one item for each row) of result objects, each of them matching the
+ * specified element type.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @param elementType the required type of element in the result {@link List} (for example, {@code Integer.class}),
+ * must not be {@literal null}.
+ * @return a {@link List} of objects that match the specified element type.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForList(String, Class, Object[])
+ * @see SingleColumnRowMapper
+ */
+ ListenableFuture> queryForList(Statement statement, Class elementType) throws DataAccessException;
+
+ /**
+ * Execute a query for a result {@link List}, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code queryForList} method with {@literal null} as argument array.
+ *
+ * The results will be mapped to a {@link List} (one item for each row) of {@link Map}s (one entry for each column
+ * using the column name as the key). Each item in the {@link List} will be of the form returned by this interface's
+ * queryForMap() methods.
+ *
+ * @param statement static CQL {@link Statement} to execute, must not be empty or {@literal null}.
+ * @return a {@link List} that contains a {@link Map} per row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForList(String, Object[])
+ */
+ ListenableFuture>> queryForList(Statement statement) throws DataAccessException;
+
+ /**
+ * Execute a query for a ResultSet, given static CQL.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code queryForResultSet} method with {@literal null} as argument
+ * array.
+ *
+ * The results will be mapped to an {@link ResultSet}.
+ *
+ * @param statement static CQL {@link Statement}, must not be {@literal null}.
+ * @return a {@link ResultSet} representation.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForResultSet(String, Object[])
+ */
+ ListenableFuture queryForResultSet(Statement statement) throws DataAccessException;
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with prepared statements
+ // -------------------------------------------------------------------------
+
+ /**
+ * Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
+ * This allows for implementing arbitrary data access operations on a single {@link PreparedStatement}, within
+ * Spring's managed CQL environment: that is, participating in Spring-managed transactions and converting CQL
+ * {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
+ *
+ * The callback action can return a result object, for example a domain object or a collection of domain objects.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param action callback object that specifies the action, must not be {@literal null}.
+ * @return a result object returned by the action, or {@literal null}.
+ * @throws DataAccessException if there is any problem
+ */
+ ListenableFuture execute(AsyncPreparedStatementCreator psc, PreparedStatementCallback action)
+ throws DataAccessException;
+
+ /**
+ * Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
+ * This allows for implementing arbitrary data access operations on a single Statement, within Spring's managed CQL
+ * environment: that is, participating in Spring-managed transactions and converting CQL
+ * {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
+ *
+ * The callback action can return a result object, for example a domain object or a collection of domain objects.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param action callback object that specifies the action, must not be {@literal null}.
+ * @return a result object returned by the action, or {@literal null}
+ * @throws DataAccessException if there is any problem TODO: Lambda-usage clashes with execute(cql,
+ * PreparedStatementBinder)
+ */
+ ListenableFuture execute(String cql, PreparedStatementCallback action) throws DataAccessException;
+
+ /**
+ * Query using a prepared statement, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param rse object that will extract results, must not be {@literal null}.
+ * @return an arbitrary result object, as returned by the {@link ResultSetExtractor}
+ * @throws DataAccessException if there is any problem
+ */
+ ListenableFuture query(AsyncPreparedStatementCreator psc, ResultSetExtractor rse) throws DataAccessException;
+
+ /**
+ * Query using a prepared statement, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @param rse object that will extract results, must not be {@literal null}.
+ * @return an arbitrary result object, as returned by the {@link ResultSetExtractor}.
+ * @throws DataAccessException if there is any problem
+ */
+ ListenableFuture query(String cql, PreparedStatementBinder psb, ResultSetExtractor rse)
+ throws DataAccessException;
+
+ /**
+ * Query using a prepared statement and a {@link PreparedStatementBinder} implementation that knows how to bind values
+ * to the query, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @param rse object that will extract results, must not be {@literal null}.
+ * @return an arbitrary result object, as returned by the {@link ResultSetExtractor}.
+ * @throws DataAccessException if there is any problem
+ */
+ ListenableFuture query(AsyncPreparedStatementCreator psc, PreparedStatementBinder psb, ResultSetExtractor rse)
+ throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, reading the
+ * {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rse object that will extract results, must not be {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type).
+ * @return an arbitrary result object, as returned by the {@link ResultSetExtractor}
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture query(String cql, ResultSetExtractor rse, Object... args) throws DataAccessException;
+
+ /**
+ * Query using a prepared statement, reading the {@link ResultSet} on a per-row basis with a
+ * {@link RowCallbackHandler}.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture query(AsyncPreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a {@link PreparedStatementBinder} implementation that
+ * knows how to bind values to the query, reading the {@link ResultSet} on a per-row basis with a
+ * {@link RowCallbackHandler}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch)
+ throws DataAccessException;
+
+ /**
+ * Query using a prepared statement and a {@link PreparedStatementBinder} implementation that knows how to bind values
+ * to the query, reading the {@link ResultSet} on a per-row basis with a {@link RowCallbackHandler}.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture query(AsyncPreparedStatementCreator psc, PreparedStatementBinder psb, RowCallbackHandler rch)
+ throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, reading the
+ * {@link ResultSet} on a per-row basis with a {@link RowCallbackHandler}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type)
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture query(String cql, RowCallbackHandler rch, Object... args) throws DataAccessException;
+
+ /**
+ * Query using a prepared statement, mapping each row to a Java object via a {@link RowMapper}.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the result {@link List}, containing mapped objects.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture> query(AsyncPreparedStatementCreator psc, RowMapper rowMapper) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a {@link PreparedStatementBinder} implementation that
+ * knows how to bind values to the query, mapping each row to a Java object via a {@link RowMapper}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the result {@link List}, containing mapped objects.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture> query(String cql, PreparedStatementBinder psb, RowMapper rowMapper)
+ throws DataAccessException;
+
+ /**
+ * Query using a prepared statement and a {@link PreparedStatementBinder} implementation that knows how to bind values
+ * to the query, mapping each row to a Java object via a {@link RowMapper}.
+ *
+ * @param psc object that can create a {@link PreparedStatement} given a {@link com.datastax.driver.core.Session},
+ * must not be {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @return the result {@link List}, containing mapped objects.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture> query(AsyncPreparedStatementCreator psc, PreparedStatementBinder psb, RowMapper rowMapper)
+ throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, mapping each
+ * row to a Java object via a {@link RowMapper}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rowMapper object that will map one object per row
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type)
+ * @return the result {@link List}, containing mapped objects
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture> query(String cql, RowMapper rowMapper, Object... args) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, mapping a
+ * single result row to a Java object via a {@link RowMapper}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rowMapper object that will map one object per row, must not be {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type)
+ * @return the single mapped object
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row.
+ * @throws DataAccessException if there is any problem executing the query.
+ */
+ ListenableFuture queryForObject(String cql, RowMapper rowMapper, Object... args) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
+ * result object.
+ *
+ * The query is expected to be a single row/single column query; the returned result will be directly mapped to the
+ * corresponding object type.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param requiredType the type that the result object is expected to match, must not be {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the PreparedStatement to guess the corresponding CQL
+ * type)
+ * @return the result object of the required type, or {@link Mono#empty()} in case of CQL NULL.
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row, or does not return
+ * exactly one column in that row.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForObject(String, Class)
+ */
+ ListenableFuture queryForObject(String cql, Class requiredType, Object... args) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
+ * result Map. The queryForMap() methods defined by this interface are appropriate when you don't have a domain model.
+ * Otherwise, consider using one of the queryForObject() methods.
+ *
+ * The query is expected to be a single row query; the result row will be mapped to a Map (one entry for each column,
+ * using the column name as the key).
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type).
+ * @return the result Map (one entry for each column, using the column name as the key).
+ * @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForMap(String)
+ * @see ColumnMapRowMapper
+ */
+ ListenableFuture> queryForMap(String cql, Object... args) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
+ * result {@link List}.
+ *
+ * The results will be mapped to a {@link List} (one item for each row) of result objects, each of them matching the
+ * specified element type.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param elementType the required type of element in the result {@link List} (for example, {@code Integer.class}),
+ * must not be {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type).
+ * @return a {@link List} of objects that match the specified element type.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForList(String, Class)
+ * @see SingleColumnRowMapper
+ */
+ ListenableFuture> queryForList(String cql, Class elementType, Object... args)
+ throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
+ * result {@link List}.
+ *
+ * The results will be mapped to a {@link List} (one item for each row) of {@link Map}s (one entry for each column,
+ * using the column name as the key). Each item in the {@link List} will be of the form returned by this interface's
+ * queryForMap() methods.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type).
+ * @return a {@link List} that contains a {@link Map} per row
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForList(String)
+ */
+ ListenableFuture>> queryForList(String cql, Object... args) throws DataAccessException;
+
+ /**
+ * Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
+ * ResultSet.
+ *
+ * The results will be mapped to an {@link ResultSet}.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type).
+ * @return a {@link ResultSet} representation.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #queryForResultSet(String)
+ */
+ ListenableFuture queryForResultSet(String cql, Object... args) throws DataAccessException;
+
+ /**
+ * Issue a single CQL execute operation (such as an insert, update or delete statement) using a
+ * {@link AsyncPreparedStatementCreator} to provide CQL and any required parameters.
+ *
+ * @param psc object that provides CQL and any necessary parameters, must not be {@literal null}.
+ * @return boolean value whether the statement was applied.
+ * @throws DataAccessException if there is any problem issuing the execution.
+ */
+ // TODO: Interferes with execute(session callback lambda)
+ ListenableFuture execute(AsyncPreparedStatementCreator psc) throws DataAccessException;
+
+ /**
+ * Issue an statement using a {@link PreparedStatementBinder} to set bind parameters, with given CQL. Simpler than
+ * using a {@link AsyncPreparedStatementCreator} as this method will create the {@link PreparedStatement}: The
+ * {@link PreparedStatementBinder} just needs to set parameters.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param psb object that knows how to set values on the prepared statement. If this is {@literal null}, the CQL will
+ * be assumed to contain no bind parameters. Even if there are no bind parameters, this object may be used to
+ * set fetch size and other performance options.
+ * @return boolean value whether the statement was applied.
+ * @throws DataAccessException if there is any problem issuing the execution.
+ */
+ ListenableFuture execute(String cql, PreparedStatementBinder psb) throws DataAccessException;
+
+ /**
+ * Issue a single CQL operation (such as an insert, update or delete statement) via a prepared statement, binding the
+ * given arguments.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param args arguments to bind to the query (leaving it to the {@link PreparedStatement} to guess the corresponding
+ * CQL type).
+ * @return boolean value whether the statement was applied.
+ * @throws DataAccessException if there is any problem issuing the execution.
+ */
+ ListenableFuture execute(String cql, Object... args) throws DataAccessException;
+
+}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncCqlTemplate.java b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncCqlTemplate.java
new file mode 100644
index 000000000..e36340f89
--- /dev/null
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncCqlTemplate.java
@@ -0,0 +1,973 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.cassandra.core;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Function;
+import java.util.stream.StreamSupport;
+
+import org.springframework.cassandra.support.CassandraAccessor;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.support.DataAccessUtils;
+import org.springframework.dao.support.PersistenceExceptionTranslator;
+import org.springframework.util.Assert;
+import org.springframework.util.concurrent.ListenableFuture;
+import org.springframework.util.concurrent.SettableListenableFuture;
+
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.ConsistencyLevel;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.SimpleStatement;
+import com.datastax.driver.core.Statement;
+import com.datastax.driver.core.exceptions.DriverException;
+import com.datastax.driver.core.policies.RetryPolicy;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * This is the central class in the CQL core package for asynchronous Cassandra data access. It simplifies the
+ * use of CQL and helps to avoid common errors. It executes core CQL workflow, leaving application code to provide CQL
+ * and extract results. This class executes CQL queries or updates, initiating iteration over {@link ResultSet}s and
+ * catching {@link DriverException} exceptions and translating them to the generic, more informative exception hierarchy
+ * defined in the {@code org.springframework.dao} package.
+ *
+ * Code using this class need only implement callback interfaces, giving them a clearly defined contract. The
+ * {@link PreparedStatementCreator} callback interface creates a prepared statement given a Connection, providing CQL
+ * and any necessary parameters. The {@link ResultSetExtractor} interface extracts values from a {@link ResultSet}. See
+ * also {@link PreparedStatementBinder} and {@link RowMapper} for two popular alternative callback interfaces.
+ *
+ * Can be used within a service implementation via direct instantiation with a {@link Session} reference, or get
+ * prepared in an application context and given to services as bean reference. Note: The {@link Session} should always
+ * be configured as a bean in the application context, in the first case given to the service directly, in the second
+ * case to the prepared template.
+ *
+ * Because this class is parameterizable by the callback interfaces and the
+ * {@link org.springframework.dao.support.PersistenceExceptionTranslator} interface, there should be no need to subclass
+ * it.
+ *
+ * All CQL operations performed by this class are logged at debug level, using
+ * "org.springframework.cassandra.core.CqlTemplate" as log category.
+ *
+ * NOTE: An instance of this class is thread-safe once configured.
+ *
+ * @author Mark Paluch
+ * @see ListenableFuture
+ * @see PreparedStatementCreator
+ * @see PreparedStatementBinder
+ * @see PreparedStatementCallback
+ * @see ResultSetExtractor
+ * @see RowCallbackHandler
+ * @see RowMapper
+ * @see org.springframework.dao.support.PersistenceExceptionTranslator
+ */
+public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOperations {
+
+ /**
+ * Placeholder for default values.
+ */
+ private final static Statement DEFAULTS = QueryBuilder.select().from("DEFAULT");
+
+ /**
+ * If this variable is set to a non-negative value, it will be used for setting the {@code fetchSize} property on
+ * statements used for query processing.
+ */
+ private int fetchSize = -1;
+
+ /**
+ * If this variable is set to a value, it will be used for setting the {@code retryPolicy} property on statements used
+ * for query processing.
+ */
+ private com.datastax.driver.core.policies.RetryPolicy retryPolicy;
+
+ /**
+ * If this variable is set to a value, it will be used for setting the {@code consistencyLevel} property on statements
+ * used for query processing.
+ */
+ private com.datastax.driver.core.ConsistencyLevel consistencyLevel;
+
+ /**
+ * Construct a new {@link AsyncCqlTemplate}. Note: The {@link Session} has to be set before using the instance.
+ *
+ * @see #setSession(Session)
+ */
+ public AsyncCqlTemplate() {}
+
+ /**
+ * Construct a new {@link AsyncCqlTemplate}, given a {@link Session}.
+ *
+ * @param session the active Cassandra {@link Session}.
+ */
+ public AsyncCqlTemplate(Session session) {
+
+ Assert.notNull(session, "Session must not be null");
+
+ setSession(session);
+ }
+
+ /**
+ * Set the fetch size for this {@link AsyncCqlTemplate}. This is important for processing large result sets: Setting
+ * this higher than the default value will increase processing speed at the cost of memory consumption; setting this
+ * lower can avoid transferring row data that will never be read by the application. Default is -1, indicating to use
+ * the CQL driver's default configuration (i.e. to not pass a specific fetch size setting on to the driver).
+ *
+ * @see Statement#setFetchSize(int)
+ */
+ public void setFetchSize(int fetchSize) {
+ this.fetchSize = fetchSize;
+ }
+
+ /**
+ * @return the fetch size specified for this {@link AsyncCqlTemplate}.
+ */
+ public int getFetchSize() {
+ return this.fetchSize;
+ }
+
+ /**
+ * Set the retry policy for this {@link AsyncCqlTemplate}. This is important for defining behavior when a request
+ * fails.
+ *
+ * @see Statement#setRetryPolicy(com.datastax.driver.core.policies.RetryPolicy)
+ * @see com.datastax.driver.core.policies.RetryPolicy
+ */
+ public void setRetryPolicy(com.datastax.driver.core.policies.RetryPolicy retryPolicy) {
+ this.retryPolicy = retryPolicy;
+ }
+
+ /**
+ * @return the {@link com.datastax.driver.core.policies.RetryPolicy} specified for this {@link AsyncCqlTemplate}.
+ */
+ public com.datastax.driver.core.policies.RetryPolicy getRetryPolicy() {
+ return retryPolicy;
+ }
+
+ /**
+ * Set the consistency level for this {@link AsyncCqlTemplate}. Consistency level defines the number of nodes involved
+ * into query processing. Relaxed consistency level settings use fewer nodes but eventual consistency is more likely
+ * to occur while a higher consistency level involves more nodes to obtain results with a higher consistency
+ * guarantee.
+ *
+ * @see Statement#setConsistencyLevel(com.datastax.driver.core.ConsistencyLevel)
+ * @see com.datastax.driver.core.policies.RetryPolicy
+ */
+ public void setConsistencyLevel(com.datastax.driver.core.ConsistencyLevel consistencyLevel) {
+ this.consistencyLevel = consistencyLevel;
+ }
+
+ /**
+ * @return the {@link com.datastax.driver.core.ConsistencyLevel} specified for this {@link AsyncCqlTemplate}.
+ */
+ public com.datastax.driver.core.ConsistencyLevel getConsistencyLevel() {
+ return consistencyLevel;
+ }
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with a plain com.datastax.driver.core.Session
+ // -------------------------------------------------------------------------
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(org.springframework.cassandra.core.AsyncSessionCallback)
+ */
+ @Override
+ public ListenableFuture execute(AsyncSessionCallback action) throws DataAccessException {
+
+ Assert.notNull(action, "Callback object must not be null");
+
+ try {
+ return action.doInSession(getSession());
+ } catch (DriverException e) {
+ throw translateException("SessionCallback", getCql(action), e);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with static CQL
+ // -------------------------------------------------------------------------
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String)
+ */
+ @Override
+ public ListenableFuture execute(String cql) throws DataAccessException {
+
+ Assert.hasText(cql, "CQL must not be empty");
+
+ return new MappingListenableFutureAdapter<>(queryForResultSet(cql), ResultSet::wasApplied);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.ResultSetExtractor)
+ */
+ @Override
+ public ListenableFuture query(String cql, ResultSetExtractor rse) throws DataAccessException {
+
+ Assert.hasText(cql, "CQL must not be empty");
+ Assert.notNull(rse, "ResultSetExtractor must not be null");
+
+ try {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Executing CQL Statement [{}]", cql);
+ }
+
+ SimpleStatement simpleStatement = new SimpleStatement(cql);
+
+ applyStatementSettings(simpleStatement);
+
+ return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
+ new GuavaListenableFutureAdapter<>(getSession().executeAsync(simpleStatement),
+ ex -> translateExceptionIfPossible("Query", cql, ex)),
+ rse::extractData), getExceptionTranslator());
+ } catch (DriverException e) {
+ throw translateException("Query", cql, e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowCallbackHandler)
+ */
+ @Override
+ public ListenableFuture query(String cql, RowCallbackHandler rch) throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(cql, new RowCallbackHandlerResultSetExtractor(rch)), o -> null),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture> query(String cql, RowMapper rowMapper) throws DataAccessException {
+ return query(cql, new RowMapperResultSetExtractor<>(rowMapper));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture queryForObject(String cql, RowMapper rowMapper) throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(cql, rowMapper), DataAccessUtils::requiredSingleResult),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, java.lang.Class)
+ */
+ @Override
+ public ListenableFuture queryForObject(String cql, Class requiredType) throws DataAccessException {
+ return queryForObject(cql, getSingleColumnRowMapper(requiredType));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForMap(java.lang.String)
+ */
+ @Override
+ public ListenableFuture> queryForMap(String cql) throws DataAccessException {
+ return queryForObject(cql, getColumnMapRowMapper());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String, java.lang.Class)
+ */
+ @Override
+ public ListenableFuture> queryForList(String cql, Class elementType) throws DataAccessException {
+ return query(cql, getSingleColumnRowMapper(elementType));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String)
+ */
+ @Override
+ public ListenableFuture>> queryForList(String cql) throws DataAccessException {
+ return query(cql, getColumnMapRowMapper());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForResultSet(java.lang.String)
+ */
+ @Override
+ public ListenableFuture queryForResultSet(String cql) throws DataAccessException {
+ return query(cql, rs -> rs);
+ }
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with com.datastax.driver.core.Statement
+ // -------------------------------------------------------------------------
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(com.datastax.driver.core.Statement)
+ */
+ @Override
+ public ListenableFuture execute(Statement statement) throws DataAccessException {
+
+ Assert.notNull(statement, "CQL Statement must not be null");
+
+ return new MappingListenableFutureAdapter<>(queryForResultSet(statement), ResultSet::wasApplied);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.cassandra.core.ResultSetExtractor)
+ */
+ @Override
+ public ListenableFuture query(Statement statement, ResultSetExtractor rse) throws DataAccessException {
+
+ Assert.notNull(statement, "CQL Statement must not be null");
+ Assert.notNull(rse, "ResultSetExtractor must not be null");
+
+ try {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Executing CQL Statement [{}]", statement);
+ }
+
+ applyStatementSettings(statement);
+
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(new GuavaListenableFutureAdapter<>(getSession().executeAsync(statement),
+ ex -> translateExceptionIfPossible("Query", statement.toString(), ex)), rse::extractData),
+ getExceptionTranslator());
+ } catch (DriverException e) {
+ throw translateException("Query", statement.toString(), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.cassandra.core.RowCallbackHandler)
+ */
+ @Override
+ public ListenableFuture query(Statement statement, RowCallbackHandler rch) throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
+ query(statement, new RowCallbackHandlerResultSetExtractor(rch)), o -> null), getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture> query(Statement statement, RowMapper rowMapper) throws DataAccessException {
+ return query(statement, new RowMapperResultSetExtractor<>(rowMapper));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(com.datastax.driver.core.Statement, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture queryForObject(Statement statement, RowMapper rowMapper)
+ throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(statement, rowMapper), DataAccessUtils::requiredSingleResult),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(com.datastax.driver.core.Statement, java.lang.Class)
+ */
+ @Override
+ public ListenableFuture queryForObject(Statement statement, Class requiredType) throws DataAccessException {
+ return queryForObject(statement, getSingleColumnRowMapper(requiredType));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForMap(com.datastax.driver.core.Statement)
+ */
+ @Override
+ public ListenableFuture> queryForMap(Statement statement) throws DataAccessException {
+ return queryForObject(statement, getColumnMapRowMapper());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(com.datastax.driver.core.Statement, java.lang.Class)
+ */
+ @Override
+ public ListenableFuture> queryForList(Statement statement, Class elementType)
+ throws DataAccessException {
+ return query(statement, getSingleColumnRowMapper(elementType));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(com.datastax.driver.core.Statement)
+ */
+ @Override
+ public ListenableFuture>> queryForList(Statement statement) throws DataAccessException {
+ return query(statement, getColumnMapRowMapper());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForResultSet(com.datastax.driver.core.Statement)
+ */
+ @Override
+ public ListenableFuture queryForResultSet(Statement statement) throws DataAccessException {
+ return query(statement, rs -> rs);
+ }
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with prepared statements
+ // -------------------------------------------------------------------------
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementCallback)
+ */
+ @Override
+ public ListenableFuture execute(AsyncPreparedStatementCreator psc, PreparedStatementCallback action)
+ throws DataAccessException {
+
+ Assert.notNull(psc, "PreparedStatementCreator must not be null");
+ Assert.notNull(action, "PreparedStatementCallback object must not be null");
+
+ try {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Preparing statement [{}] using {}", getCql(psc), psc);
+ }
+
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(psc.createPreparedStatement(getSession()), preparedStatement -> {
+
+ try {
+ applyStatementSettings(preparedStatement);
+ return action.doInPreparedStatement(preparedStatement);
+ } catch (DriverException e) {
+ throw translateException("PreparedStatementCallback", preparedStatement.toString(), e);
+ }
+ }), getExceptionTranslator());
+
+ } catch (DriverException e) {
+ throw translateException("PreparedStatementCallback", getCql(psc), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.ResultSetExtractor)
+ */
+ @Override
+ public ListenableFuture query(AsyncPreparedStatementCreator psc, PreparedStatementBinder psb,
+ ResultSetExtractor rse) throws DataAccessException {
+
+ Assert.notNull(psc, "AsyncPreparedStatementCreator must not be null");
+ Assert.notNull(rse, "ResultSetExtractor object must not be null");
+
+ try {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Preparing statement [{}] using {}", getCql(psc), psc);
+ }
+
+ Session session = getSession();
+
+ PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("Query", getCql(psc), ex);
+
+ ListenableFuture psFuture = new MappingListenableFutureAdapter<>(
+ psc.createPreparedStatement(session), ps -> {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Executing prepared statement [{}]", ps);
+ }
+
+ BoundStatement boundStatement = psb != null ? psb.bindValues(ps) : ps.bind();
+
+ applyStatementSettings(boundStatement);
+
+ return boundStatement;
+ });
+
+ SettableListenableFuture settableListenableFuture = new SettableListenableFuture();
+ psFuture.addCallback(boundStatement -> {
+
+ Futures.addCallback(session.executeAsync(boundStatement), new FutureCallback() {
+ @Override
+ public void onSuccess(ResultSet result) {
+ try {
+ settableListenableFuture.set(rse.extractData(result));
+ } catch (DriverException e) {
+ settableListenableFuture.setException(exceptionTranslator.translateExceptionIfPossible(e));
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable ex) {
+
+ if (ex instanceof DriverException) {
+ settableListenableFuture
+ .setException(exceptionTranslator.translateExceptionIfPossible((DriverException) ex));
+ } else {
+ settableListenableFuture.setException(ex);
+ }
+ }
+ });
+
+ }, ex -> {
+ if (ex instanceof DriverException) {
+ settableListenableFuture.setException(exceptionTranslator.translateExceptionIfPossible((DriverException) ex));
+ } else {
+ settableListenableFuture.setException(ex);
+ }
+ });
+
+ return settableListenableFuture;
+
+ } catch (DriverException e) {
+ throw translateException("Query", getCql(psc), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String, org.springframework.cassandra.core.PreparedStatementCallback)
+ */
+ @Override
+ public ListenableFuture execute(String cql, PreparedStatementCallback action) throws DataAccessException {
+ return execute(newAsyncPreparedStatementCreator(cql), action);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.ResultSetExtractor)
+ */
+ @Override
+ public ListenableFuture query(AsyncPreparedStatementCreator psc, ResultSetExtractor rse)
+ throws DataAccessException {
+ return query(psc, null, rse);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.ResultSetExtractor)
+ */
+ @Override
+ public ListenableFuture query(String cql, PreparedStatementBinder psb, ResultSetExtractor rse)
+ throws DataAccessException {
+ return query(newAsyncPreparedStatementCreator(cql), psb, rse);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.ResultSetExtractor, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture query(String cql, ResultSetExtractor rse, Object... args)
+ throws DataAccessException {
+ return query(cql, newArgPreparedStatementBinder(args), rse);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.RowCallbackHandler)
+ */
+ @Override
+ public ListenableFuture query(AsyncPreparedStatementCreator psc, RowCallbackHandler rch)
+ throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(psc, new RowCallbackHandlerResultSetExtractor(rch)), o -> null),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowCallbackHandler)
+ */
+ @Override
+ public ListenableFuture query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch)
+ throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(cql, psb, new RowCallbackHandlerResultSetExtractor(rch)), o -> null),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowCallbackHandler)
+ */
+ @Override
+ public ListenableFuture query(AsyncPreparedStatementCreator psc, PreparedStatementBinder psb,
+ RowCallbackHandler rch) throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(psc, psb, new RowCallbackHandlerResultSetExtractor(rch)), o -> null),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowCallbackHandler, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture query(String cql, RowCallbackHandler rch, Object... args) throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(
+ new MappingListenableFutureAdapter<>(query(newAsyncPreparedStatementCreator(cql),
+ newArgPreparedStatementBinder(args), new RowCallbackHandlerResultSetExtractor(rch)), o -> null),
+ getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture> query(AsyncPreparedStatementCreator psc, RowMapper rowMapper)
+ throws DataAccessException {
+ return query(psc, new RowMapperResultSetExtractor<>(rowMapper));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture> query(String cql, PreparedStatementBinder psb, RowMapper rowMapper)
+ throws DataAccessException {
+ return query(cql, psb, new RowMapperResultSetExtractor<>(rowMapper));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowMapper)
+ */
+ @Override
+ public ListenableFuture> query(AsyncPreparedStatementCreator psc, PreparedStatementBinder psb,
+ RowMapper rowMapper) throws DataAccessException {
+ return query(psc, psb, new RowMapperResultSetExtractor<>(rowMapper));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowMapper, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture> query(String cql, RowMapper rowMapper, Object... args)
+ throws DataAccessException {
+ return query(cql, newArgPreparedStatementBinder(args), new RowMapperResultSetExtractor<>(rowMapper));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, org.springframework.cassandra.core.RowMapper, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture queryForObject(String cql, RowMapper rowMapper, Object... args)
+ throws DataAccessException {
+ return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
+ query(cql, newArgPreparedStatementBinder(args), new RowMapperResultSetExtractor<>(rowMapper, 1)),
+ DataAccessUtils::requiredSingleResult), getExceptionTranslator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, java.lang.Class, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture queryForObject(String cql, Class requiredType, Object... args)
+ throws DataAccessException {
+ return queryForObject(cql, getSingleColumnRowMapper(requiredType), args);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForMap(java.lang.String, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture> queryForMap(String cql, Object... args) throws DataAccessException {
+ return queryForObject(cql, getColumnMapRowMapper(), args);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String, java.lang.Class, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture> queryForList(String cql, Class elementType, Object... args)
+ throws DataAccessException {
+ return query(cql, getSingleColumnRowMapper(elementType), args);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture>> queryForList(String cql, Object... args)
+ throws DataAccessException {
+ return query(cql, getColumnMapRowMapper(), args);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForResultSet(java.lang.String, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture queryForResultSet(String cql, Object... args) throws DataAccessException {
+ return query(cql, rs -> rs, args);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(org.springframework.cassandra.core.AsyncPreparedStatementCreator)
+ */
+ @Override
+ public ListenableFuture execute(AsyncPreparedStatementCreator psc) throws DataAccessException {
+ return query(psc, ResultSet::wasApplied);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder)
+ */
+ @Override
+ public ListenableFuture execute(String cql, PreparedStatementBinder psb) throws DataAccessException {
+ return query(newAsyncPreparedStatementCreator(cql), psb, ResultSet::wasApplied);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String, java.lang.Object[])
+ */
+ @Override
+ public ListenableFuture execute(String cql, Object... args) throws DataAccessException {
+ return execute(cql, newArgPreparedStatementBinder(args));
+ }
+
+ // -------------------------------------------------------------------------
+ // Implementation hooks and helper methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Translate the given {@link DriverException} into a generic {@link DataAccessException}.
+ *
+ * @param task readable text describing the task being attempted
+ * @param cql CQL query or update that caused the problem (may be {@code null})
+ * @param ex the offending {@code RuntimeException}.
+ * @return the translated {@link DataAccessException} or {@literal null} if translation not possible.
+ * @see CqlProvider
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ protected DataAccessException translateExceptionIfPossible(String task, String cql, RuntimeException ex) {
+
+ if (ex instanceof DriverException) {
+ return translate(task, cql, (DriverException) ex);
+ }
+
+ return null;
+ }
+
+ /**
+ * Translate the given {@link DriverException} into a generic {@link DataAccessException}.
+ *
+ * @param task readable text describing the task being attempted
+ * @param cql CQL query or update that caused the problem (may be {@code null})
+ * @param ex the offending {@code RuntimeException}.
+ * @return the exception translation {@link Function}
+ * @see CqlProvider
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ protected DataAccessException translateException(String task, String cql, DriverException ex) {
+ return translate(task, cql, ex);
+ }
+
+ /**
+ * Create a new RowMapper for reading columns as key-value pairs.
+ *
+ * @return the RowMapper to use
+ * @see ColumnMapRowMapper
+ */
+ protected RowMapper> getColumnMapRowMapper() {
+ return new ColumnMapRowMapper();
+ }
+
+ /**
+ * Create a new RowMapper for reading result objects from a single column.
+ *
+ * @param requiredType the type that each result object is expected to match
+ * @return the RowMapper to use
+ * @see SingleColumnRowMapper
+ */
+ protected RowMapper getSingleColumnRowMapper(Class requiredType) {
+ return SingleColumnRowMapper.newInstance(requiredType);
+ }
+
+ /**
+ * Prepare the given CQL Statement (or {@link com.datastax.driver.core.PreparedStatement}), applying statement
+ * settings such as fetch size, retry policy, and consistency level.
+ *
+ * @param stmt the CQL Statement to prepare
+ * @see #setFetchSize(int)
+ * @see #setRetryPolicy(RetryPolicy)
+ * @see #setConsistencyLevel(ConsistencyLevel)
+ */
+ protected void applyStatementSettings(Statement stmt) {
+
+ int fetchSize = getFetchSize();
+ if (fetchSize != -1 && stmt.getFetchSize() == DEFAULTS.getFetchSize()) {
+ stmt.setFetchSize(fetchSize);
+ }
+
+ RetryPolicy retryPolicy = getRetryPolicy();
+ if (retryPolicy != null && stmt.getRetryPolicy() == DEFAULTS.getRetryPolicy()) {
+ stmt.setRetryPolicy(retryPolicy);
+ }
+
+ ConsistencyLevel consistencyLevel = getConsistencyLevel();
+ if (consistencyLevel != null && stmt.getConsistencyLevel() == DEFAULTS.getConsistencyLevel()) {
+ stmt.setConsistencyLevel(consistencyLevel);
+ }
+ }
+
+ /**
+ * Prepare the given CQL Statement (or {@link com.datastax.driver.core.PreparedStatement}), applying statement
+ * settings such as retry policy and consistency level.
+ *
+ * @param stmt the CQL Statement to prepare
+ * @see #setRetryPolicy(RetryPolicy)
+ * @see #setConsistencyLevel(ConsistencyLevel)
+ */
+ protected void applyStatementSettings(PreparedStatement stmt) {
+
+ RetryPolicy retryPolicy = getRetryPolicy();
+ if (retryPolicy != null) {
+ stmt.setRetryPolicy(retryPolicy);
+ }
+
+ ConsistencyLevel consistencyLevel = getConsistencyLevel();
+ if (consistencyLevel != null) {
+ stmt.setConsistencyLevel(consistencyLevel);
+ }
+ }
+
+ /**
+ * Create a new arg-based PreparedStatementSetter using the args passed in. By default, we'll create an
+ * {@link ArgumentPreparedStatementBinder}. This method allows for the creation to be overridden by subclasses.
+ *
+ * @param args object array with arguments
+ * @return the new {@link PreparedStatementBinder} to use
+ */
+ protected PreparedStatementBinder newArgPreparedStatementBinder(Object[] args) {
+ return new ArgumentPreparedStatementBinder(args);
+ }
+
+ /**
+ * Create a new CQL-based AsyncPreparedStatementCreator using the CQL passed in. By default, we'll create an
+ * {@link SimpleAsyncPreparedStatementCreator}. This method allows for the creation to be overridden by subclasses.
+ *
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @return the new {@link AsyncPreparedStatementCreator} to use
+ */
+ protected AsyncPreparedStatementCreator newAsyncPreparedStatementCreator(String cql) {
+ return new SimpleAsyncPreparedStatementCreator(cql,
+ ex -> translateExceptionIfPossible("PrepareStatement", cql, ex));
+ }
+
+ /**
+ * Determine CQL from potential provider object.
+ *
+ * @param cqlProvider object that's potentially a {@link CqlProvider}
+ * @return the CQL string, or {@code null}
+ * @see CqlProvider
+ */
+ private static String getCql(Object cqlProvider) {
+
+ if (cqlProvider instanceof CqlProvider) {
+ return ((CqlProvider) cqlProvider).getCql();
+ } else {
+ return null;
+ }
+ }
+
+ private static class SimpleAsyncPreparedStatementCreator implements AsyncPreparedStatementCreator, CqlProvider {
+
+ private final String cql;
+ private final PersistenceExceptionTranslator persistenceExceptionTranslator;
+
+ SimpleAsyncPreparedStatementCreator(String cql, PersistenceExceptionTranslator persistenceExceptionTranslator) {
+
+ Assert.notNull(cql, "CQL must not be null");
+
+ this.cql = cql;
+ this.persistenceExceptionTranslator = persistenceExceptionTranslator;
+ }
+
+ @Override
+ public ListenableFuture createPreparedStatement(Session session) throws DriverException {
+
+ return new GuavaListenableFutureAdapter<>(session.prepareAsync(cql), persistenceExceptionTranslator);
+ }
+
+ @Override
+ public String getCql() {
+ return cql;
+ }
+ }
+
+ private static class MappingListenableFutureAdapter
+ extends org.springframework.util.concurrent.ListenableFutureAdapter {
+
+ private final Function mapper;
+
+ public MappingListenableFutureAdapter(ListenableFuture adaptee, Function mapper) {
+ super(adaptee);
+ this.mapper = mapper;
+ }
+
+ @Override
+ protected T adapt(S adapteeResult) throws ExecutionException {
+ return mapper.apply(adapteeResult);
+ }
+ }
+
+ /**
+ * Adapter to enable use of a {@link RowCallbackHandler} inside a {@link ResultSetExtractor}.
+ */
+ private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor {
+
+ private final RowCallbackHandler rch;
+
+ public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
+ this.rch = rch;
+ }
+
+ @Override
+ public Object extractData(ResultSet rs) {
+
+ StreamSupport.stream(rs.spliterator(), false).forEach(rch::processRow);
+ return null;
+ }
+ }
+}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncPreparedStatementCreator.java b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncPreparedStatementCreator.java
new file mode 100644
index 000000000..b799c9466
--- /dev/null
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncPreparedStatementCreator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.cassandra.core;
+
+import org.springframework.util.concurrent.ListenableFuture;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.exceptions.DriverException;
+
+/**
+ * One of the two central callback interfaces used by the {@link AsyncCqlTemplate} class. This interface prepares a CQL
+ * statement returning a {@link org.springframework.util.concurrent.ListenableFuture} given a {@link Session}, provided
+ * by the {@link CqlTemplate} class.
+ *
+ * Implementations may either create new prepared statements or reuse cached instances. Implementations do not need to
+ * concern themselves with {@link DriverException}s that may be thrown from operations they attempt. The
+ * {@link AsyncCqlTemplate} class will catch and handle {@link DriverException}s appropriately.
+ *
+ * A {@link AsyncPreparedStatementCreator} should also implement the {@link CqlProvider} interface if it is able to
+ * provide the CQL it uses for {@link PreparedStatement} creation. This allows for better contextual information in case
+ * of exceptions.
+ *
+ * @author Mark Paluch
+ * @since 2.0
+ */
+public interface AsyncPreparedStatementCreator {
+
+ /**
+ * Create a statement in this session. Allows implementations to use {@link PreparedStatement}s. The
+ * {@link CqlTemplate} will attempt to cache the {@link PreparedStatement}s for future use without the overhead of
+ * re-preparing on the entire cluster.
+ *
+ * @param session Session to use to create statement, must not be {@literal null}.
+ * @return a prepared statement
+ * @throws DriverException there is no need to catch DriverException that may be thrown in the implementation of this
+ * method. The {@link CqlTemplate} class will handle them.
+ */
+ ListenableFuture createPreparedStatement(Session session) throws DriverException;
+}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncSessionCallback.java b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncSessionCallback.java
new file mode 100644
index 000000000..ea9a1281b
--- /dev/null
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/AsyncSessionCallback.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.cassandra.core;
+
+import org.springframework.dao.DataAccessException;
+
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.exceptions.DriverException;
+import org.springframework.util.concurrent.ListenableFuture;
+
+/**
+ * Generic callback interface for code that operates asynchronously on a Cassandra {@link Session}. Allows to execute any number of
+ * operations on a single session, using any type and number of statements.
+ *
+ * This is particularly useful for delegating to existing data access code that expects a {@link Session} to work on and
+ * throws {@link DriverException}. For newly written code, it is strongly recommended to use {@link CqlTemplate}'s more
+ * specific operations, for example a {@code query} or {@code update} variant.
+ *
+ * @author David Webb
+ * @author Mark Paluch
+ * @see AsyncCqlTemplate#execute(AsyncSessionCallback)
+ * @see AsyncCqlTemplate#query
+ */
+public interface AsyncSessionCallback {
+
+ /**
+ * Gets called by {@link CqlTemplate#execute} with an active Cassandra {@link Session}. Does not need to care about
+ * activating or closing the {@link Session}.
+ *
+ * Allows for returning a result object created within the callback, i.e. a domain object or a collection of domain
+ * objects. Note that there's special support for single step actions: see {@link CqlTemplate#queryForObject} etc. A
+ * thrown {@link RuntimeException} is treated as application exception: it gets propagated to the caller of the
+ * template.
+ *
+ * @param session active Cassandra Session, must not be {@literal null}.
+ * @return a result object, or {@code null} if none.
+ * @throws DriverException if thrown by a Session method, to be auto-converted to a {@link DataAccessException}.
+ * @throws DataAccessException in case of custom exceptions.
+ * @see AsyncCqlTemplate#queryForObject(String, Class)
+ * @see AsyncCqlTemplate#queryForResultSet(String)
+ */
+ ListenableFuture doInSession(Session session) throws DriverException, DataAccessException;
+
+}
diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java b/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java
index 8d2c3c67b..cbb4a832f 100644
--- a/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java
+++ b/spring-cql/src/main/java/org/springframework/cassandra/core/CqlOperations.java
@@ -1,12 +1,12 @@
/*
- * Copyright 2013-2016 the original author or authors.
- *
+ * Copyright 2016 the original author or authors.
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,1239 +16,759 @@
package org.springframework.cassandra.core;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import org.springframework.cassandra.core.cql.CqlIdentifier;
-import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification;
-import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
-import org.springframework.cassandra.core.keyspace.CreateIndexSpecification;
-import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification;
-import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
-import org.springframework.cassandra.core.keyspace.DropIndexSpecification;
-import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification;
-import org.springframework.cassandra.core.keyspace.DropTableSpecification;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.ResultSetFuture;
-import com.datastax.driver.core.Session;
+import com.datastax.driver.core.Row;
import com.datastax.driver.core.Statement;
-import com.datastax.driver.core.querybuilder.Batch;
-import com.datastax.driver.core.querybuilder.Delete;
-import com.datastax.driver.core.querybuilder.Insert;
-import com.datastax.driver.core.querybuilder.Select;
-import com.datastax.driver.core.querybuilder.Truncate;
-import com.datastax.driver.core.querybuilder.Update;
/**
- * Operations for interacting with Cassandra at the lowest level. This interface provides Exception Translation.
- *
- * @author David Webb
- * @author Matthew Adams
- * @author John Blum
+ * Interface specifying a basic set of CQL operations. Implemented by {@link CqlTemplate}. Not often used directly, but
+ * a useful option to enhance testability, as it can easily be mocked or stubbed.
+ *
+ * @author Mark Paluch
+ * @since 2.0
+ * @see CqlTemplate
*/
public interface CqlOperations {
+ // -------------------------------------------------------------------------
+ // Methods dealing with a plain com.datastax.driver.core.Session
+ // -------------------------------------------------------------------------
+
/**
- * Convenient method that delegates to {@link ResultSetFuture#getUninterruptibly()} but translates exceptions if any
- * are thrown.
+ * Execute a CQL data access operation, implemented as callback action working on a
+ * {@link com.datastax.driver.core.Session}. This allows for implementing arbitrary data access operations, within
+ * Spring's managed CQL environment: that is, converting CQL
+ * {@link com.datastax.driver.core.exceptions.DriverException}s into Spring's {@link DataAccessException} hierarchy.
+ *
+ * The callback action can return a result object, for example a domain object or a collection of domain objects.
*
- * @param rsf The {@link ResultSetFuture} from which to get the {@link ResultSet}.
- * @return The {@link ResultSet}
+ * @param action the callback object that specifies the action.
+ * @return a result object returned by the action, or {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query.
*/
- ResultSet getResultSetUninterruptibly(ResultSetFuture rsf);
+ T execute(SessionCallback action) throws DataAccessException;
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with static CQL
+ // -------------------------------------------------------------------------
/**
- * Convenient method that delegates to {@link ResultSetFuture#getUninterruptibly()} but translates exceptions if any
- * are thrown.
+ * Issue a single CQL execute, typically a DDL statement, insert, update or delete statement.
*
- * @param rsf The {@link ResultSetFuture} from which to get the {@link ResultSet}.
- * @param timeout The timeout to wait in milliseconds. A nonpositive value means wait indefinitely.
- * @return The {@link ResultSet}
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @return boolean value whether the statement was applied.
+ * @throws DataAccessException if there is any problem executing the query.
*/
- ResultSet getResultSetUninterruptibly(ResultSetFuture rsf, long millis);
+ boolean execute(String cql) throws DataAccessException;
/**
- * Convenient method that delegates to {@link ResultSetFuture#getUninterruptibly()} but translates exceptions if any
- * are thrown.
+ * Execute a query given static CQL, reading the {@link ResultSet} with a {@link ResultSetExtractor}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
*
- * @param rsf The {@link ResultSetFuture} from which to get the {@link ResultSet}.
- * @param timeout The timeout to wait. A nonpositive value means wait indefinitely.
- * @param unit The {@link TimeUnit} of the timeout.
- * @return The {@link ResultSet}
- */
- ResultSet getResultSetUninterruptibly(ResultSetFuture rsf, long timeout, TimeUnit unit);
-
- /**
- * Executes the supplied {@link SessionCallback} in the current Template Session. The implementation of
- * SessionCallback can decide whether or not to execute() or executeAsync() the operation.
- *
- * @param sessionCallback
- * @return Type defined in the SessionCallback
- */
- T execute(SessionCallback sessionCallback) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query and returns nothing.
- *
- * @param cql
- */
- void execute(String cql) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query and returns nothing.
- *
- * @param cql
- * @param options may be null
- */
- void execute(String cql, QueryOptions options) throws DataAccessException;
-
- /**
- * Executes the supplied Query and returns nothing.
- *
- * @param query The {@link Statement} to execute
- */
- void execute(Statement query) throws DataAccessException;
-
- /**
- * Executes the supplied Delete Query and returns nothing.
- *
- * @param query The {@link Delete} to execute
- */
- void execute(Delete delete) throws DataAccessException;
-
- /**
- * Executes the supplied Insert Query and returns nothing.
- *
- * @param query The {@link Insert} to execute
- */
- void execute(Insert insert) throws DataAccessException;
-
- /**
- * Executes the supplied Update Query and returns nothing.
- *
- * @param query The {@link Update} to execute
- */
- void execute(Update update) throws DataAccessException;
-
- /**
- * Executes the supplied Batch Query and returns nothing.
- *
- * @param query The {@link Batch} to execute
- */
- void execute(Batch batch) throws DataAccessException;
-
- /**
- * Executes the supplied Truncate Query and returns nothing.
- *
- * @param query The {@link Truncate} to execute
- */
- void execute(Truncate truncate) throws DataAccessException;
-
- /**
- * Executes the supplied Query Asynchronously and returns nothing.
- *
- * @param cql The CQL String to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(String cql) throws DataAccessException;
-
- /**
- * Executes the supplied Query Asynchronously and returns nothing.
- *
- * @param cql The CQL String to execute
- * @param options The {@link QueryOptions} to use. Only applies to cql statements that can use QueryOptions.
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(String cql, QueryOptions options) throws DataAccessException;
-
- /**
- * Executes the supplied Query Asynchronously and returns nothing.
- *
- * @param cql The CQL String to execute
- * @param listener The {@link Runnable} to register with the {@link ResultSetFuture}
- * @return A {@link Cancellable} that can be used to cancel the query.
- * @see queryAsyncronously for Reads
- */
- Cancellable executeAsynchronously(String cql, Runnable listener) throws DataAccessException;
-
- /**
- * Executes the supplied Query Asynchronously and returns nothing.
- *
- * @param cql The CQL String to execute
- * @param listener The {@link Runnable} to register with the {@link ResultSetFuture}
- * @param executor The {@link Executor} to regsiter with the {@link ResultSetFuture}
- * @return A {@link Cancellable} that can be used to cancel the query.
- * @see queryAsyncronously for Reads
- */
- Cancellable executeAsynchronously(String cql, Runnable listener, Executor executor) throws DataAccessException;
-
- /**
- * Executes the supplied Query Asynchronously and returns nothing.
- *
- * @param cql The CQL String to execute
- * @param listener The {@link AsynchronousQueryListener} to register with the {@link ResultSetFuture}
- * @return A {@link Cancellable} that can be used to cancel the query.
- * @see queryAsyncronously for Reads
- */
- Cancellable executeAsynchronously(String cql, AsynchronousQueryListener listener) throws DataAccessException;
-
- /**
- * Executes the supplied Query Asynchronously and returns nothing.
- *
- * @param cql The CQL String to execute
- * @param listener The {@link AsynchronousQueryListener} to register with the {@link ResultSetFuture}
- * @param executor The {@link Executor} to regsiter with the {@link ResultSetFuture}
- * @return A {@link Cancellable} that can be used to cancel the query.
- * @see queryAsyncronously for Reads
- */
- Cancellable executeAsynchronously(String cql, AsynchronousQueryListener listener, Executor executor)
- throws DataAccessException;
-
- /**
- * Executes the supplied CQL Truncate Asynchronously and returns nothing.
- *
- * @param query The {@link Truncate} to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(Truncate truncate) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Delete Asynchronously and returns nothing.
- *
- * @param query The {@link Delete} to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(Delete delete) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Insert Asynchronously and returns nothing.
- *
- * @param query The {@link Insert} to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(Insert insert) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Update Asynchronously and returns nothing.
- *
- * @param query The {@link Update} to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(Update update) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Batch Asynchronously and returns nothing.
- *
- * @param query The {@link Batch} to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(Batch batch) throws DataAccessException;
-
- Cancellable executeAsynchronously(Truncate truncate, AsynchronousQueryListener listener) throws DataAccessException;
-
- Cancellable executeAsynchronously(Delete delete, AsynchronousQueryListener listener) throws DataAccessException;
-
- Cancellable executeAsynchronously(Insert insert, AsynchronousQueryListener listener) throws DataAccessException;
-
- Cancellable executeAsynchronously(Update update, AsynchronousQueryListener listener) throws DataAccessException;
-
- Cancellable executeAsynchronously(Batch batch, AsynchronousQueryListener listener) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query Asynchronously and returns nothing.
- *
- * @param query The {@link Statement} to execute
- * @return A {@link ResultSetFuture} that can be used to cancel the query.
- */
- ResultSetFuture executeAsynchronously(Statement query) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query Asynchronously and returns nothing.
- *
- * @param query The {@link Statement} to execute
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable executeAsynchronously(Statement query, Runnable runnable) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query Asynchronously and returns nothing.
- *
- * @param query The {@link Statement} to execute
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable executeAsynchronously(Statement query, AsynchronousQueryListener listener) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query Asynchronously and returns nothing.
- *
- * @param query The {@link Statement} to execute
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable executeAsynchronously(Statement query, Runnable runnable, Executor executor) throws DataAccessException;
-
- /**
- * Executes the supplied CQL Query Asynchronously and returns nothing.
- *
- * @param query The {@link Statement} to execute
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable executeAsynchronously(Statement query, AsynchronousQueryListener listener, Executor executor)
- throws DataAccessException;
-
- /**
- * Executes the provided CQL Query, and extracts the results with the ResultSetExtractor. This uses default Query
- * Options when extracting the ResultSet.
- *
- * @param cql The Query
- * @param rse The implementation for extracting the ResultSet
- * @param timeout Time to wait for results
- * @param timeUnit Time unit to wait for results
- * @return
- */
- T queryAsynchronously(String cql, ResultSetExtractor rse, Long timeout, TimeUnit timeUnit);
-
- /**
- * Executes the provided CQL Query, and extracts the results with the ResultSetExtractor.
- *
- * @param cql The Query
- * @param rse The implementation for extracting the ResultSet
- * @param timeout Time to wait for results
- * @param timeUnit Time unit to wait for results
- * @param options Query Options
- * @return
- */
- T queryAsynchronously(String cql, ResultSetExtractor rse, Long timeout, TimeUnit timeUnit,
- QueryOptions options);
-
- /**
- * Executes the provided CQL Query and returns the ResultSetFuture for user processing.
- *
- * @param cql The Query
- * @return
- */
- ResultSetFuture queryAsynchronously(String cql);
-
- /**
- * Executes the provided CQL Select and returns the ResultSetFuture for user processing.
- *
- * @param cql The {@link Select}
- * @return
- */
- ResultSetFuture queryAsynchronously(Select select);
-
- /**
- * Executes the provided CQL Query and returns the ResultSetFuture for user processing.
- *
- * @param cql The Query
- * @param options Query Options
- * @return
- */
- ResultSetFuture queryAsynchronously(String cql, QueryOptions options);
-
- /**
- * Executes the provided CQL Query with the provided {@link Runnable}, which is started after the query has completed.
- *
- * A more useful method than this one is {@link #queryAsynchronously(String, AsynchronousQueryListener)}, where you're
- * given the {@link ResultSetFuture} after the query has been executed.
- *
- * @param cql The Query
- * @param listener {@link Runnable} listener for handling the query in a separate thread
- * @return A {@link Cancellable} that can be used to cancel the query.
- * @see #queryAsynchronously(String, AsynchronousQueryListener)
- */
- Cancellable queryAsynchronously(String cql, Runnable listener);
-
- /**
- * Executes the provided CQL Select with the provided {@link Runnable}, which is started after the query has
- * completed.
- *
- * A more useful method than this one is {@link #queryAsynchronously(Select, AsynchronousQueryListener)}, where you're
- * given the {@link ResultSetFuture} after the query has been executed.
- *
- * @param select The Select Query
- * @param listener {@link Runnable} listener for handling the query in a separate thread
- * @return A {@link Cancellable} that can be used to cancel the query.
- * @see #queryAsynchronously(Select, AsynchronousQueryListener)
- */
- Cancellable queryAsynchronously(Select select, Runnable listener);
-
- /**
- * Executes the provided CQL Query with the provided listener. This is preferred over the same method that takes a
- * {@link Runnable}. The {@link AsynchronousQueryListener} gives you access to the {@link ResultSetFuture} once the
- * query is completed for optimal flexibility.
- *
- * @param cql The Query
- * @param listener {@link AsynchronousQueryListener} for handling the query's {@link ResultSetFuture} in a separate
- * thread
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, AsynchronousQueryListener listener);
-
- /**
- * Executes the provided CQL Select with the provided listener. This is preferred over the same method that takes a
- * {@link Runnable}. The {@link AsynchronousQueryListener} gives you access to the {@link ResultSetFuture} once the
- * query is completed for optimal flexibility.
- *
- * @param select The Select
- * @param listener {@link AsynchronousQueryListener} for handling the query's {@link ResultSetFuture} in a separate
- * thread
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(Select select, AsynchronousQueryListener listener);
-
- /**
- * Executes the provided CQL Query with the Runnable implementations using the Query Options.
- *
- * @param cql The Query
- * @param options Query Option
- * @param listener Runnable Listener for handling the query in a separate thread
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, Runnable listener, QueryOptions options);
-
- /**
- * Executes the provided CQL Query with the provided Listener and Query Options. This is preferred over the same
- * method that takes a plain Runnable. The {@link AsynchronousQueryListener} gives you access to the
- * {@link ResultSetFuture} once the query is completed for optimal flexibility.
- *
- * @param cql The Query
- * @param options Query Option
- * @param listener Runnable Listener for handling the query in a separate thread
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, AsynchronousQueryListener listener, QueryOptions options);
-
- /**
- * Executes the provided CQL Query with the provided Executor and Runnable implementations.
- *
- * @param cql The Query
- * @param listener Runnable Listener for handling the query in a separate thread
- * @param executor To execute the Runnable Listener
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, Runnable listener, Executor executor);
-
- /**
- * Executes the provided CQL Select with the provided Executor and Runnable implementations.
- *
- * @param select The Select Query
- * @param listener Runnable Listener for handling the query in a separate thread
- * @param executor To execute the Runnable Listener
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(Select select, Runnable listener, Executor executor);
-
- /**
- * Executes the provided CQL Query with the provided listener and executor. This is preferred over the same method
- * that takes a plain Runnable. The {@link AsynchronousQueryListener} gives you access to the {@link ResultSetFuture}
- * once the query is completed for optimal flexibility.
- *
- * @param cql The Query
- * @param options Query Option
- * @param listener Runnable Listener for handling the query in a separate thread
- * @param executor To execute the Runnable Listener
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, AsynchronousQueryListener listener, Executor executor);
-
- /**
- * Executes the provided Select Query with the provided listener and executor. This is preferred over the same method
- * that takes a plain Runnable. The {@link AsynchronousQueryListener} gives you access to the {@link ResultSetFuture}
- * once the query is completed for optimal flexibility.
- *
- * @param select The Select Query
- * @param listener Runnable Listener for handling the query in a separate thread
- * @param executor To execute the Runnable Listener
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(Select select, AsynchronousQueryListener listener, Executor executor);
-
- /**
- * Executes the provided CQL Query with the provided Executor and Runnable implementations.
- *
- * @param cql The Query
- * @param options Query Option
- * @param listener Runnable Listener for handling the query in a separate thread
- * @param executor To execute the Runnable Listener
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, Runnable listener, QueryOptions options, Executor executor);
-
- /**
- * Executes the provided CQL Query with the provided Listener, Executor and Query Options. This is preferred over the
- * same method that takes a plain Runnable. The {@link AsynchronousQueryListener} gives you access to the
- * {@link ResultSetFuture} once the query is completed for optimal flexibility.
- *
- * @param cql
- * @param listener
- * @param options
- * @param executor
- * @return A {@link Cancellable} that can be used to cancel the query.
- */
- Cancellable queryAsynchronously(String cql, AsynchronousQueryListener listener, QueryOptions options,
- Executor executor);
-
- /**
- * Executes the provided CQL query and returns the {@link ResultSet}.
- *
- * @param cql The query
- * @return The {@link ResultSet}
- */
- ResultSet query(String cql);
-
- /**
- * Executes the provided Select query and returns the {@link ResultSet}.
- *
- * @param select The Select Query
- * @return The {@link ResultSet}
- */
- ResultSet query(Select select);
-
- /**
- * Executes the provided CQL query with the given {@link QueryOptions} and returns the {@link ResultSet}.
- *
- * @param cql The query
- * @param options The {@link QueryOptions}; may be null.
- * @return The {@link ResultSet}
- */
- ResultSet query(String cql, QueryOptions options);
-
- /**
- * Executes the provided CQL Query, and extracts the results with the ResultSetExtractor.
- *
- * @param cql The Query
- * @param rse The implementation for extracting the ResultSet
- * @return Type specified in the ResultSetExtractor
- * @throws DataAccessException
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rse object that will extract all rows of results, must not be {@literal null}.
+ * @return an arbitrary result object, as returned by the ResultSetExtractor.
+ * @throws DataAccessException if there is any problem executing the query.
+ * @see #query(String, ResultSetExtractor, Object...)
*/
T query(String cql, ResultSetExtractor rse) throws DataAccessException;
/**
- * Executes the provided Select Query, and extracts the results with the ResultSetExtractor.
+ * Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
+ * {@link RowCallbackHandler}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@code null} as argument array.
*
- * @param select The SelectQuery
- * @param rse The implementation for extracting the ResultSet
- * @return Type specified in the ResultSetExtractor
- * @throws DataAccessException
- */
- T query(Select select, ResultSetExtractor rse) throws DataAccessException;
-
- /**
- * Executes the provided CQL Query, and extracts the results with the ResultSetExtractor.
- *
- * @param cql The Query
- * @param rse The implementation for extracting the ResultSet
- * @param options Query Options
- * @return
- * @throws DataAccessException
- */
- T query(String cql, ResultSetExtractor rse, QueryOptions options) throws DataAccessException;
-
- /**
- * Executes the provided CQL Query, and then processes the results with the RowCallbackHandler.
- *
- * @param cql The Query
- * @param rch The implementation for processing the rows returned.
- * @throws DataAccessException
+ * @param cql static CQL to execute, must not be empty or {@literal null}.
+ * @param rch object that will extract results, one row at a time, must not be {@literal null}.
+ * @throws DataAccessException if there is any problem executing the query
+ * @see #query(String, RowCallbackHandler, Object[])
*/
void query(String cql, RowCallbackHandler rch) throws DataAccessException;
/**
- * Executes the provided Select Query, and then processes the results with the RowCallbackHandler.
+ * Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
+ *
+ * Uses a CQL Statement, not a {@link PreparedStatement}. If you want to execute a static query with a
+ * {@link PreparedStatement}, use the overloaded {@code query} method with {@literal null} as argument array.
*
- * @param select The Select Query
- * @param rch The implementation for processing the rows returned.
- * @throws DataAccessException
- */
- void query(Select select, RowCallbackHandler rch) throws DataAccessException;
-
- /**
- * Executes the provided CQL Query, and then processes the results with the