/* * Copyright 2016-2017 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 org.springframework.cassandra.core.session.SessionFactory; 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.PreparedStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; 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.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; /** * <b>This is the central class in the CQL core package for asynchronous Cassandra data access.</b> 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. * <p> * 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. * <p> * 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. * <p> * 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. * <p> * All CQL operations performed by this class are logged at debug level, using * "org.springframework.cassandra.core.CqlTemplate" as log category. * <p> * <b>NOTE: An instance of this class is thread-safe once configured.</b> * * @author Mark Paluch * @author John Blum * @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 { /** * Create a new, uninitialized {@link AsyncCqlTemplate}. Note: The {@link SessionFactory} has to be set before using * the instance. * * @see #setSessionFactory(SessionFactory) */ public AsyncCqlTemplate() {} /** * Create a new {@link AsyncCqlTemplate} with the given {@link Session}. * * @param session the active Cassandra {@link Session}, must not be {@literal null}. * @throws IllegalStateException if {@link Session} is {@literal null}. * @see com.datastax.driver.core.Session */ public AsyncCqlTemplate(Session session) { Assert.notNull(session, "Session must not be null"); setSession(session); } /** * Constructs a new {@link AsyncCqlTemplate} with the given {@link SessionFactory}. * * @param sessionFactory the active Cassandra {@link SessionFactory}. * @since 2.0 * @see SessionFactory */ public AsyncCqlTemplate(SessionFactory sessionFactory) { Assert.notNull(sessionFactory, "SessionFactory must not be null"); setSessionFactory(sessionFactory); } // ------------------------------------------------------------------------- // 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 <T> ListenableFuture<T> execute(AsyncSessionCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); try { return action.doInSession(getCurrentSession()); } catch (DriverException e) { throw translateException("SessionCallback", toCql(action), e); } } // ------------------------------------------------------------------------- // Methods dealing with static CQL // ------------------------------------------------------------------------- /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String) */ @Override public ListenableFuture<Boolean> 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 <T> ListenableFuture<T> query(String cql, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException { Assert.hasText(cql, "CQL must not be empty"); Assert.notNull(resultSetExtractor, "ResultSetExtractor must not be null"); try { if (logger.isDebugEnabled()) { logger.debug("Executing CQL Statement [{}]", cql); } SimpleStatement simpleStatement = applyStatementSettings(new SimpleStatement(cql)); ResultSetFuture results = getCurrentSession().executeAsync(simpleStatement); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>( new GuavaListenableFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", cql, ex)), resultSetExtractor::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<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException { ListenableFuture<?> results = query(cql, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowMapper) */ @Override public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException { return query(cql, newResultSetExtractor(rowMapper)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String) */ @Override public ListenableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException { return query(cql, newResultSetExtractor(newColumnMapRowMapper())); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String, java.lang.Class) */ @Override public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException { return query(cql, newResultSetExtractor(newSingleColumnRowMapper(elementType))); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForMap(java.lang.String) */ @Override public ListenableFuture<Map<String, Object>> queryForMap(String cql) throws DataAccessException { return queryForObject(cql, newColumnMapRowMapper()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, java.lang.Class) */ @Override public <T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType) throws DataAccessException { return queryForObject(cql, newSingleColumnRowMapper(requiredType)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, org.springframework.cassandra.core.RowMapper) */ @Override public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException { ListenableFuture<List<T>> results = query(cql, newResultSetExtractor(rowMapper)); return new ExceptionTranslatingListenableFutureAdapter<>( new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForResultSet(java.lang.String) */ @Override public ListenableFuture<ResultSet> 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<Boolean> 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 <T> ListenableFuture<T> query(Statement statement, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException { Assert.notNull(statement, "CQL Statement must not be null"); Assert.notNull(resultSetExtractor, "ResultSetExtractor must not be null"); try { if (logger.isDebugEnabled()) { logger.debug("Executing CQL Statement [{}]", statement); } ResultSetFuture results = getCurrentSession().executeAsync(applyStatementSettings(statement)); return new ExceptionTranslatingListenableFutureAdapter<>( new MappingListenableFutureAdapter<>( new GuavaListenableFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", statement.toString(), ex)), resultSetExtractor::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<Void> query(Statement statement, RowCallbackHandler rowCallbackHandler) throws DataAccessException { ListenableFuture<?> result = query(statement, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(result, o -> null), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(com.datastax.driver.core.Statement, org.springframework.cassandra.core.RowMapper) */ @Override public <T> ListenableFuture<List<T>> query(Statement statement, RowMapper<T> rowMapper) throws DataAccessException { return query(statement, newResultSetExtractor(rowMapper)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(com.datastax.driver.core.Statement) */ @Override public ListenableFuture<List<Map<String, Object>>> queryForList(Statement statement) throws DataAccessException { return query(statement, newResultSetExtractor(newColumnMapRowMapper())); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(com.datastax.driver.core.Statement, java.lang.Class) */ @Override public <T> ListenableFuture<List<T>> queryForList(Statement statement, Class<T> elementType) throws DataAccessException { return query(statement, newResultSetExtractor(newSingleColumnRowMapper(elementType))); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForMap(com.datastax.driver.core.Statement) */ @Override public ListenableFuture<Map<String, Object>> queryForMap(Statement statement) throws DataAccessException { return queryForObject(statement, newColumnMapRowMapper()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(com.datastax.driver.core.Statement, java.lang.Class) */ @Override public <T> ListenableFuture<T> queryForObject(Statement statement, Class<T> requiredType) throws DataAccessException { return queryForObject(statement, newSingleColumnRowMapper(requiredType)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(com.datastax.driver.core.Statement, org.springframework.cassandra.core.RowMapper) */ @Override public <T> ListenableFuture<T> queryForObject(Statement statement, RowMapper<T> rowMapper) throws DataAccessException { ListenableFuture<List<T>> results = query(statement, newResultSetExtractor(rowMapper)); return new ExceptionTranslatingListenableFutureAdapter<>( new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForResultSet(com.datastax.driver.core.Statement) */ @Override public ListenableFuture<ResultSet> queryForResultSet(Statement statement) throws DataAccessException { return query(statement, rs -> rs); } // ------------------------------------------------------------------------- // Methods dealing with com.datastax.driver.core.PreparedStatement // ------------------------------------------------------------------------- /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(org.springframework.cassandra.core.AsyncPreparedStatementCreator) */ @Override public ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator) throws DataAccessException { return query(preparedStatementCreator, ResultSet::wasApplied); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String, java.lang.Object[]) */ @Override public ListenableFuture<Boolean> execute(String cql, Object... args) throws DataAccessException { return execute(cql, newPreparedStatementBinder(args)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder) */ @Override public ListenableFuture<Boolean> execute(String cql, PreparedStatementBinder preparedStatementBinder) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, ResultSet::wasApplied); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(java.lang.String, org.springframework.cassandra.core.PreparedStatementCallback) */ @Override public <T> ListenableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException { return execute(newAsyncPreparedStatementCreator(cql), action); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#execute(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementCallback) */ @Override public <T> ListenableFuture<T> execute(AsyncPreparedStatementCreator preparedStatementCreator, PreparedStatementCallback<T> action) throws DataAccessException { Assert.notNull(preparedStatementCreator, "PreparedStatementCreator must not be null"); Assert.notNull(action, "PreparedStatementCallback object must not be null"); try { if (logger.isDebugEnabled()) { logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator); } Session currentSession = getCurrentSession(); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>( preparedStatementCreator.createPreparedStatement(currentSession), preparedStatement -> { try { return action.doInPreparedStatement(currentSession, applyStatementSettings(preparedStatement)); } catch (DriverException e) { throw translateException("PreparedStatementCallback", preparedStatement.toString(), e); } }), getExceptionTranslator()); } catch (DriverException e) { throw translateException("PreparedStatementCallback", toCql(preparedStatementCreator), e); } } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.ResultSetExtractor) */ @Override public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException { return query(preparedStatementCreator, null, resultSetExtractor); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.RowCallbackHandler) */ @Override public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator, RowCallbackHandler rowCallbackHandler) throws DataAccessException { ListenableFuture<?> results = query(preparedStatementCreator, null, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(org.springframework.cassandra.core.AsyncPreparedStatementCreator, org.springframework.cassandra.core.RowMapper) */ @Override public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator, RowMapper<T> rowMapper) throws DataAccessException { return query(preparedStatementCreator, null, newResultSetExtractor(rowMapper)); } /* * (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 <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException { Assert.notNull(preparedStatementCreator, "AsyncPreparedStatementCreator must not be null"); Assert.notNull(resultSetExtractor, "ResultSetExtractor object must not be null"); try { if (logger.isDebugEnabled()) { logger.debug("Preparing statement [{}] using {}", toCql(preparedStatementCreator), preparedStatementCreator); } Session session = getCurrentSession(); PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("Query", toCql(preparedStatementCreator), ex); ListenableFuture<BoundStatement> statementFuture = new MappingListenableFutureAdapter<>( preparedStatementCreator.createPreparedStatement(session), preparedStatement -> { if (logger.isDebugEnabled()) { logger.debug("Executing prepared statement [{}]", preparedStatement); } return applyStatementSettings(preparedStatementBinder != null ? preparedStatementBinder.bindValues(preparedStatement) : preparedStatement.bind()); }); SettableListenableFuture<T> settableListenableFuture = new SettableListenableFuture<>(); statementFuture.addCallback( boundStatement -> Futures.addCallback(session.executeAsync(boundStatement), new FutureCallback<ResultSet>() { @Override public void onSuccess(ResultSet result) { try { settableListenableFuture.set(resultSetExtractor.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", toCql(preparedStatementCreator), e); } } /* * (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<Void> query(AsyncPreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, RowCallbackHandler rowCallbackHandler) throws DataAccessException { ListenableFuture<?> results = query(preparedStatementCreator, preparedStatementBinder, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, 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.RowMapper) */ @Override public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator, PreparedStatementBinder preparedStatementBinder, RowMapper<T> rowMapper) throws DataAccessException { return query(preparedStatementCreator, preparedStatementBinder, newResultSetExtractor(rowMapper)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.ResultSetExtractor, java.lang.Object[]) */ @Override public <T> ListenableFuture<T> query(String cql, ResultSetExtractor<T> resultSetExtractor, Object... args) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), resultSetExtractor); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowCallbackHandler, java.lang.Object[]) */ @Override public ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler, Object... args) throws DataAccessException { ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.RowMapper, java.lang.Object[]) */ @Override public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(rowMapper)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.ResultSetExtractor) */ @Override public <T> ListenableFuture<T> query(String cql, PreparedStatementBinder preparedStatementBinder, ResultSetExtractor<T> resultSetExtractor) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, resultSetExtractor); } /* * (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<Void> query(String cql, PreparedStatementBinder preparedStatementBinder, RowCallbackHandler rowCallbackHandler) throws DataAccessException { ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, newResultSetExtractor(rowCallbackHandler)); return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowMapper) */ @Override public <T> ListenableFuture<List<T>> query(String cql, PreparedStatementBinder preparedStatementBinder, RowMapper<T> rowMapper) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), preparedStatementBinder, newResultSetExtractor(rowMapper)); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String, java.lang.Object[]) */ @Override public ListenableFuture<List<Map<String, Object>>> queryForList(String cql, Object... args) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(newColumnMapRowMapper())); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForList(java.lang.String, java.lang.Class, java.lang.Object[]) */ @Override public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args) throws DataAccessException { return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(newSingleColumnRowMapper(elementType))); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForMap(java.lang.String, java.lang.Object[]) */ @Override public ListenableFuture<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException { return queryForObject(cql, newColumnMapRowMapper(), args); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, java.lang.Class, java.lang.Object[]) */ @Override public <T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args) throws DataAccessException { return queryForObject(cql, newSingleColumnRowMapper(requiredType), args); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForObject(java.lang.String, org.springframework.cassandra.core.RowMapper, java.lang.Object[]) */ @Override public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException { ListenableFuture<List<T>> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), newResultSetExtractor(rowMapper, 1)); return new ExceptionTranslatingListenableFutureAdapter<>( new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator()); } /* * (non-Javadoc) * @see org.springframework.cassandra.core.AsyncCqlOperations#queryForResultSet(java.lang.String, java.lang.Object[]) */ @Override public ListenableFuture<ResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException { return query(cql, rs -> rs, args); } // ------------------------------------------------------------------------- // Implementation hooks and helper methods // ------------------------------------------------------------------------- /** * 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)); } /** * 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); } /** * 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) { return (ex instanceof DriverException ? translate(task, cql, (DriverException) ex) : null); } private Session getCurrentSession() { return getSessionFactory().getSession(); } private static class SimpleAsyncPreparedStatementCreator implements AsyncPreparedStatementCreator, CqlProvider { private final PersistenceExceptionTranslator persistenceExceptionTranslator; private final String cql; private SimpleAsyncPreparedStatementCreator(String cql, PersistenceExceptionTranslator persistenceExceptionTranslator) { Assert.hasText(cql, "CQL must not be empty"); this.cql = cql; this.persistenceExceptionTranslator = persistenceExceptionTranslator; } @Override public String getCql() { return this.cql; } @Override public ListenableFuture<PreparedStatement> createPreparedStatement(Session session) throws DriverException { return new GuavaListenableFutureAdapter<>(session.prepareAsync(getCql()), this.persistenceExceptionTranslator); } } private static class MappingListenableFutureAdapter<T, S> extends org.springframework.util.concurrent.ListenableFutureAdapter<T, S> { private final Function<S, T> mapper; private MappingListenableFutureAdapter(ListenableFuture<S> adaptee, Function<S, T> mapper) { super(adaptee); this.mapper = mapper; } @Override protected T adapt(S adapteeResult) throws ExecutionException { return mapper.apply(adapteeResult); } } }