Deprecate ListenableFuture-based Template API and introduce CompletableFuture Template API replacement.
We now provide a CompletableFuture-based asynchronous Template API for CQL and entity operations. The ListenableFuture-future API is now deprecated and moved to legacy subpackages for easier migration. Closes #1294
This commit is contained in:
@@ -86,7 +86,7 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the a {@link CassandraManagedTypes} object holding the initial entity set.
|
||||
* Returns the given {@link CassandraManagedTypes} object holding the initial entity set.
|
||||
*
|
||||
* @return new instance of {@link CassandraManagedTypes}.
|
||||
* @throws ClassNotFoundException
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.cassandra.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@@ -27,7 +28,6 @@ import org.springframework.data.cassandra.core.query.CassandraPageRequest;
|
||||
import org.springframework.data.cassandra.core.query.Query;
|
||||
import org.springframework.data.cassandra.core.query.Update;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
@@ -74,7 +74,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> select(String cql, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> select(String cql, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
|
||||
@@ -86,7 +86,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the completion handle
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<Void> select(String cql, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
<T> CompletableFuture<Void> select(String cql, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -97,14 +97,14 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOne(String cql, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<T> selectOne(String cql, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute the a Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* Execute the given Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* into Spring's DAO exception hierarchy.
|
||||
*
|
||||
* @param statement a Cassandra {@link Statement}, must not be {@literal null}.
|
||||
@@ -112,7 +112,7 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @since 3.2
|
||||
*/
|
||||
ListenableFuture<AsyncResultSet> execute(Statement<?> statement) throws DataAccessException;
|
||||
CompletableFuture<AsyncResultSet> execute(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items to a {@link List} of entities.
|
||||
@@ -122,7 +122,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities. A sliced
|
||||
@@ -134,7 +134,7 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see CassandraPageRequest
|
||||
*/
|
||||
<T> ListenableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
|
||||
@@ -146,7 +146,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the completion handle
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
<T> CompletableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -157,7 +157,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with org.springframework.data.cassandra.core.query.Query
|
||||
@@ -171,7 +171,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> select(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> select(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities.
|
||||
@@ -182,7 +182,7 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see CassandraPageRequest
|
||||
*/
|
||||
<T> ListenableFuture<Slice<T>> slice(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<Slice<T>> slice(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
|
||||
@@ -194,7 +194,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the completion handle
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<Void> select(Query query, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
<T> CompletableFuture<Void> select(Query query, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -205,7 +205,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOne(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<T> selectOne(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the queried entities and return {@literal true} if the update was applied.
|
||||
@@ -215,7 +215,7 @@ public interface AsyncCassandraOperations {
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> update(Query query, Update update, Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Boolean> update(Query query, Update update, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Remove entities (rows)/columns from the table by {@link Query}.
|
||||
@@ -225,7 +225,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return {@literal true} if the deletion was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> delete(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Boolean> delete(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with entities
|
||||
@@ -238,7 +238,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the number of existing entities.
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
*/
|
||||
ListenableFuture<Long> count(Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Long> count(Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Returns the number of rows for the given entity class applying {@link Query}. This overridden method allows users
|
||||
@@ -251,7 +251,7 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
* @since 2.1
|
||||
*/
|
||||
ListenableFuture<Long> count(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Long> count(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Determine whether a row of {@code entityClass} with the given {@code id} exists.
|
||||
@@ -263,7 +263,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return {@literal true} if the object exists.
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> exists(Object id, Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Boolean> exists(Object id, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Determine whether the result for {@code entityClass} {@link Query} yields at least one row.
|
||||
@@ -274,7 +274,7 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
* @since 2.1
|
||||
*/
|
||||
ListenableFuture<Boolean> exists(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Boolean> exists(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute the Select by {@code id} for the given {@code entityClass}.
|
||||
@@ -286,7 +286,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOneById(Object id, Class<T> entityClass) throws DataAccessException;
|
||||
<T> CompletableFuture<T> selectOneById(Object id, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Insert the given entity and return the entity if the insert was applied.
|
||||
@@ -295,7 +295,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the inserted entity.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> insert(T entity) throws DataAccessException;
|
||||
<T> CompletableFuture<T> insert(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Insert the given entity applying {@link WriteOptions} and return the entity if the insert was applied.
|
||||
@@ -306,7 +306,7 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see InsertOptions#empty()
|
||||
*/
|
||||
<T> ListenableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) throws DataAccessException;
|
||||
<T> CompletableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity and return the entity if the update was applied.
|
||||
@@ -315,7 +315,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return the updated entity.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> update(T entity) throws DataAccessException;
|
||||
<T> CompletableFuture<T> update(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity applying {@link WriteOptions} and return the entity if the update was applied.
|
||||
@@ -326,19 +326,19 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see UpdateOptions#empty()
|
||||
*/
|
||||
<T> ListenableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
<T> CompletableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity and return the entity if the delete was applied.
|
||||
* Delete the given entity and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return the deleted entity.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> delete(T entity) throws DataAccessException;
|
||||
<T> CompletableFuture<T> delete(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link QueryOptions} and return the entity if the delete was applied.
|
||||
* Delete the given entity applying {@link QueryOptions} and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
@@ -346,10 +346,10 @@ public interface AsyncCassandraOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see QueryOptions#empty()
|
||||
*/
|
||||
ListenableFuture<WriteResult> delete(Object entity, QueryOptions options) throws DataAccessException;
|
||||
CompletableFuture<WriteResult> delete(Object entity, QueryOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link DeleteOptions} and return the entity if the delete was applied.
|
||||
* Delete the given entity applying {@link DeleteOptions} and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
@@ -358,7 +358,7 @@ public interface AsyncCassandraOperations {
|
||||
* @see DeleteOptions#empty()
|
||||
* @since 2.2
|
||||
*/
|
||||
default ListenableFuture<WriteResult> delete(Object entity, DeleteOptions options) throws DataAccessException {
|
||||
default CompletableFuture<WriteResult> delete(Object entity, DeleteOptions options) throws DataAccessException {
|
||||
return delete(entity, (QueryOptions) options);
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ public interface AsyncCassandraOperations {
|
||||
* @return {@literal true} if the deletion was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> deleteById(Object id, Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Boolean> deleteById(Object id, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code TRUNCATE} query to remove all entities of a given class.
|
||||
@@ -380,6 +380,6 @@ public interface AsyncCassandraOperations {
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Void> truncate(Class<?> entityClass) throws DataAccessException;
|
||||
CompletableFuture<Void> truncate(Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -35,14 +36,12 @@ import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.dao.support.DataAccessUtils;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.cassandra.SessionFactory;
|
||||
import org.springframework.data.cassandra.core.EntityOperations.AdaptibleEntity;
|
||||
import org.springframework.data.cassandra.core.convert.CassandraConverter;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.cql.*;
|
||||
import org.springframework.data.cassandra.core.cql.session.DefaultSessionFactory;
|
||||
import org.springframework.data.cassandra.core.cql.util.CassandraFutureAdapter;
|
||||
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
|
||||
import org.springframework.data.cassandra.core.mapping.SimpleUserTypeResolver;
|
||||
@@ -63,9 +62,7 @@ import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
@@ -119,8 +116,6 @@ public class AsyncCassandraTemplate
|
||||
|
||||
private final CassandraConverter converter;
|
||||
|
||||
private final CqlExceptionTranslator exceptionTranslator;
|
||||
|
||||
private final EntityOperations entityOperations;
|
||||
|
||||
private final StatementFactory statementFactory;
|
||||
@@ -189,7 +184,6 @@ public class AsyncCassandraTemplate
|
||||
this.converter = converter;
|
||||
this.cqlOperations = asyncCqlTemplate;
|
||||
this.entityOperations = new EntityOperations(converter);
|
||||
this.exceptionTranslator = asyncCqlTemplate.getExceptionTranslator();
|
||||
this.statementFactory = new StatementFactory(converter);
|
||||
this.eventDelegate = new EntityLifecycleEventDelegate();
|
||||
}
|
||||
@@ -314,7 +308,7 @@ public class AsyncCassandraTemplate
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> select(String cql, Class<T> entityClass) {
|
||||
public <T> CompletableFuture<List<T>> select(String cql, Class<T> entityClass) {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
|
||||
@@ -322,7 +316,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<Void> select(String cql, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
public <T> CompletableFuture<Void> select(String cql, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
@@ -333,7 +327,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> selectOne(String cql, Class<T> entityClass) {
|
||||
public <T> CompletableFuture<T> selectOne(String cql, Class<T> entityClass) {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -346,7 +340,7 @@ public class AsyncCassandraTemplate
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> execute(Statement<?> statement) throws DataAccessException {
|
||||
public CompletableFuture<AsyncResultSet> execute(Statement<?> statement) throws DataAccessException {
|
||||
|
||||
Assert.notNull(statement, "Statement must not be null");
|
||||
|
||||
@@ -354,7 +348,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) {
|
||||
public <T> CompletableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) {
|
||||
|
||||
Assert.notNull(statement, "Statement must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -365,7 +359,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
public <T> CompletableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(statement, "Statement must not be null");
|
||||
@@ -380,22 +374,21 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) {
|
||||
return new MappingListenableFutureAdapter<>(select(statement, entityClass),
|
||||
list -> list.isEmpty() ? null : list.get(0));
|
||||
public <T> CompletableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) {
|
||||
return select(statement, entityClass).thenApply(list -> list.isEmpty() ? null : list.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) {
|
||||
public <T> CompletableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) {
|
||||
|
||||
Assert.notNull(statement, "Statement must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
|
||||
ListenableFuture<AsyncResultSet> resultSet = doQueryForResultSet(statement);
|
||||
CompletableFuture<AsyncResultSet> resultSet = doQueryForResultSet(statement);
|
||||
|
||||
Function<Row, T> mapper = getMapper(entityClass, entityClass, EntityQueryUtils.getTableName(statement));
|
||||
|
||||
return new MappingListenableFutureAdapter<>(resultSet,
|
||||
return resultSet.thenApply(
|
||||
rs -> EntityQueryUtils.readSlice(rs, (row, rowNum) -> mapper.apply(row), 0, getEffectivePageSize(statement)));
|
||||
}
|
||||
|
||||
@@ -404,7 +397,7 @@ public class AsyncCassandraTemplate
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> select(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
public <T> CompletableFuture<List<T>> select(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -413,7 +406,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<Void> select(Query query, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
public <T> CompletableFuture<Void> select(Query query, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
@@ -425,7 +418,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> selectOne(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
public <T> CompletableFuture<T> selectOne(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -435,7 +428,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<Slice<T>> slice(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
public <T> CompletableFuture<Slice<T>> slice(Query query, Class<T> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -444,7 +437,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> update(Query query, org.springframework.data.cassandra.core.query.Update update,
|
||||
public CompletableFuture<Boolean> update(Query query, org.springframework.data.cassandra.core.query.Update update,
|
||||
Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
@@ -456,7 +449,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> delete(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
public CompletableFuture<Boolean> delete(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -464,7 +457,7 @@ public class AsyncCassandraTemplate
|
||||
return doDelete(query, entityClass, getTableName(entityClass));
|
||||
}
|
||||
|
||||
private ListenableFuture<Boolean> doDelete(Query query, Class<?> entityClass, CqlIdentifier tableName) {
|
||||
private CompletableFuture<Boolean> doDelete(Query query, Class<?> entityClass, CqlIdentifier tableName) {
|
||||
|
||||
StatementBuilder<Delete> builder = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass),
|
||||
tableName);
|
||||
@@ -472,10 +465,9 @@ public class AsyncCassandraTemplate
|
||||
|
||||
maybeEmitEvent(() -> new BeforeDeleteEvent<>(delete, entityClass, tableName));
|
||||
|
||||
ListenableFuture<Boolean> future = doExecute(delete, AsyncResultSet::wasApplied);
|
||||
CompletableFuture<Boolean> future = doExecute(delete, AsyncResultSet::wasApplied);
|
||||
|
||||
future.addCallback(success -> maybeEmitEvent(() -> new AfterDeleteEvent<>(delete, entityClass, tableName)),
|
||||
e -> {});
|
||||
future.thenAccept(success -> maybeEmitEvent(() -> new AfterDeleteEvent<>(delete, entityClass, tableName)));
|
||||
|
||||
return future;
|
||||
}
|
||||
@@ -485,7 +477,7 @@ public class AsyncCassandraTemplate
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Long> count(Class<?> entityClass) {
|
||||
public CompletableFuture<Long> count(Class<?> entityClass) {
|
||||
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
|
||||
@@ -493,7 +485,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Long> count(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
public CompletableFuture<Long> count(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -501,14 +493,14 @@ public class AsyncCassandraTemplate
|
||||
return doCount(query, entityClass, getTableName(entityClass));
|
||||
}
|
||||
|
||||
ListenableFuture<Long> doCount(Query query, Class<?> entityClass, CqlIdentifier tableName) {
|
||||
CompletableFuture<Long> doCount(Query query, Class<?> entityClass, CqlIdentifier tableName) {
|
||||
|
||||
StatementBuilder<com.datastax.oss.driver.api.querybuilder.select.Select> countStatement = getStatementFactory()
|
||||
.count(query, getRequiredPersistentEntity(entityClass), tableName);
|
||||
|
||||
SimpleStatement statement = countStatement.build();
|
||||
|
||||
ListenableFuture<Long> result = doExecute(statement, it -> {
|
||||
CompletableFuture<Long> result = doExecute(statement, it -> {
|
||||
|
||||
SingleColumnRowMapper<Long> mapper = SingleColumnRowMapper.newInstance(Long.class);
|
||||
|
||||
@@ -516,11 +508,11 @@ public class AsyncCassandraTemplate
|
||||
return mapper.mapRow(row, 0);
|
||||
});
|
||||
|
||||
return new MappingListenableFutureAdapter<>(result, it -> it != null ? it : 0L);
|
||||
return result.thenApply(it -> it != null ? it : 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> exists(Object id, Class<?> entityClass) {
|
||||
public CompletableFuture<Boolean> exists(Object id, Class<?> entityClass) {
|
||||
|
||||
Assert.notNull(id, "Id must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -534,7 +526,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> exists(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
public CompletableFuture<Boolean> exists(Query query, Class<?> entityClass) throws DataAccessException {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -546,7 +538,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> selectOneById(Object id, Class<T> entityClass) {
|
||||
public <T> CompletableFuture<T> selectOneById(Object id, Class<T> entityClass) {
|
||||
|
||||
Assert.notNull(id, "Id must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -556,17 +548,16 @@ public class AsyncCassandraTemplate
|
||||
StatementBuilder<Select> select = getStatementFactory().selectOneById(id, entity, tableName);
|
||||
Function<Row, T> mapper = getMapper(entityClass, entityClass, tableName);
|
||||
|
||||
return new MappingListenableFutureAdapter<>(doQuery(select.build(), (row, rowNum) -> mapper.apply(row)),
|
||||
it -> it.isEmpty() ? null : it.get(0));
|
||||
return doQuery(select.build(), (row, rowNum) -> mapper.apply(row)).thenApply(it -> it.isEmpty() ? null : it.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> insert(T entity) {
|
||||
return new MappingListenableFutureAdapter<>(insert(entity, InsertOptions.empty()), EntityWriteResult::getEntity);
|
||||
public <T> CompletableFuture<T> insert(T entity) {
|
||||
return insert(entity, InsertOptions.empty()).thenApply(EntityWriteResult::getEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) {
|
||||
public <T> CompletableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
Assert.notNull(options, "InsertOptions must not be null");
|
||||
@@ -574,7 +565,8 @@ public class AsyncCassandraTemplate
|
||||
return doInsert(entity, options, getTableName(entity.getClass()));
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> doInsert(T entity, WriteOptions options, CqlIdentifier tableName) {
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> doInsert(T entity, WriteOptions options,
|
||||
CqlIdentifier tableName) {
|
||||
|
||||
AdaptibleEntity<T> source = getEntityOperations().forEntity(maybeCallBeforeConvert(entity, tableName),
|
||||
getConverter().getConversionService());
|
||||
@@ -594,7 +586,7 @@ public class AsyncCassandraTemplate
|
||||
return doInsert(builder.build(), entityToUse, source, tableName);
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> doInsertVersioned(SimpleStatement insert, T entity,
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> doInsertVersioned(SimpleStatement insert, T entity,
|
||||
AdaptibleEntity<T> source, CqlIdentifier tableName) {
|
||||
|
||||
return executeSave(entity, tableName, insert, result -> {
|
||||
@@ -608,19 +600,19 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> doInsert(SimpleStatement insert, T entity,
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> doInsert(SimpleStatement insert, T entity,
|
||||
AdaptibleEntity<T> source, CqlIdentifier tableName) {
|
||||
|
||||
return executeSave(entity, tableName, insert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> update(T entity) {
|
||||
return new MappingListenableFutureAdapter<>(update(entity, UpdateOptions.empty()), EntityWriteResult::getEntity);
|
||||
public <T> CompletableFuture<T> update(T entity) {
|
||||
return update(entity, UpdateOptions.empty()).thenApply(EntityWriteResult::getEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) {
|
||||
public <T> CompletableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
Assert.notNull(options, "UpdateOptions must not be null");
|
||||
@@ -635,7 +627,7 @@ public class AsyncCassandraTemplate
|
||||
: doUpdate(entityToUpdate, options, tableName, persistentEntity);
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> doUpdateVersioned(T entity, UpdateOptions options,
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> doUpdateVersioned(T entity, UpdateOptions options,
|
||||
CqlIdentifier tableName, CassandraPersistentEntity<?> persistentEntity) {
|
||||
|
||||
AdaptibleEntity<T> source = getEntityOperations().forEntity(entity, getConverter().getConversionService());
|
||||
@@ -655,7 +647,7 @@ public class AsyncCassandraTemplate
|
||||
});
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> doUpdate(T entity, UpdateOptions options, CqlIdentifier tableName,
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> doUpdate(T entity, UpdateOptions options, CqlIdentifier tableName,
|
||||
CassandraPersistentEntity<?> persistentEntity) {
|
||||
|
||||
StatementBuilder<Update> update = getStatementFactory().update(entity, options, persistentEntity, tableName);
|
||||
@@ -664,12 +656,12 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> delete(T entity) {
|
||||
return new MappingListenableFutureAdapter<>(delete(entity, QueryOptions.empty()), writeResult -> entity);
|
||||
public <T> CompletableFuture<T> delete(T entity) {
|
||||
return delete(entity, QueryOptions.empty()).thenApply(writeResult -> entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<WriteResult> delete(Object entity, QueryOptions options) {
|
||||
public CompletableFuture<WriteResult> delete(Object entity, QueryOptions options) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null");
|
||||
Assert.notNull(options, "QueryOptions must not be null");
|
||||
@@ -682,7 +674,7 @@ public class AsyncCassandraTemplate
|
||||
: doDelete(entity, options, tableName);
|
||||
}
|
||||
|
||||
private ListenableFuture<WriteResult> doDeleteVersioned(Object entity, QueryOptions options,
|
||||
private CompletableFuture<WriteResult> doDeleteVersioned(Object entity, QueryOptions options,
|
||||
AdaptibleEntity<Object> source, CqlIdentifier tableName) {
|
||||
|
||||
StatementBuilder<Delete> delete = getStatementFactory().delete(entity, options, getConverter(), tableName);
|
||||
@@ -698,7 +690,7 @@ public class AsyncCassandraTemplate
|
||||
});
|
||||
}
|
||||
|
||||
private ListenableFuture<WriteResult> doDelete(Object entity, QueryOptions options, CqlIdentifier tableName) {
|
||||
private CompletableFuture<WriteResult> doDelete(Object entity, QueryOptions options, CqlIdentifier tableName) {
|
||||
|
||||
StatementBuilder<Delete> delete = getStatementFactory().delete(entity, options, getConverter(), tableName);
|
||||
|
||||
@@ -706,7 +698,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> deleteById(Object id, Class<?> entityClass) {
|
||||
public CompletableFuture<Boolean> deleteById(Object id, Class<?> entityClass) {
|
||||
|
||||
Assert.notNull(id, "Id must not be null");
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
@@ -719,15 +711,14 @@ public class AsyncCassandraTemplate
|
||||
|
||||
maybeEmitEvent(() -> new BeforeDeleteEvent<>(delete, entityClass, tableName));
|
||||
|
||||
ListenableFuture<Boolean> future = doExecute(delete, AsyncResultSet::wasApplied);
|
||||
future.addCallback(success -> maybeEmitEvent(() -> new AfterDeleteEvent<>(delete, entityClass, tableName)),
|
||||
e -> {});
|
||||
CompletableFuture<Boolean> future = doExecute(delete, AsyncResultSet::wasApplied);
|
||||
future.thenAccept(success -> maybeEmitEvent(() -> new AfterDeleteEvent<>(delete, entityClass, tableName)));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> truncate(Class<?> entityClass) {
|
||||
public CompletableFuture<Void> truncate(Class<?> entityClass) {
|
||||
|
||||
Assert.notNull(entityClass, "Entity type must not be null");
|
||||
|
||||
@@ -737,11 +728,10 @@ public class AsyncCassandraTemplate
|
||||
|
||||
maybeEmitEvent(() -> new BeforeDeleteEvent<>(statement, entityClass, tableName));
|
||||
|
||||
ListenableFuture<Boolean> future = doExecute(statement, AsyncResultSet::wasApplied);
|
||||
future.addCallback(success -> maybeEmitEvent(() -> new AfterDeleteEvent<>(statement, entityClass, tableName)),
|
||||
e -> {});
|
||||
CompletableFuture<Boolean> future = doExecute(statement, AsyncResultSet::wasApplied);
|
||||
future.thenAccept(success -> maybeEmitEvent(() -> new AfterDeleteEvent<>(statement, entityClass, tableName)));
|
||||
|
||||
return new MappingListenableFutureAdapter<>(future, aBoolean -> null);
|
||||
return future.thenApply(aBoolean -> null);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -758,24 +748,24 @@ public class AsyncCassandraTemplate
|
||||
* @since 3.3.3
|
||||
*/
|
||||
protected AsyncPreparedStatementHandler createPreparedStatementHandler(Statement<?> statement) {
|
||||
return new PreparedStatementHandler(statement, exceptionTranslator);
|
||||
return new PreparedStatementHandler(statement);
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
|
||||
SimpleStatement statement) {
|
||||
|
||||
return executeSave(entity, tableName, statement, ignore -> {});
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
|
||||
private <T> CompletableFuture<EntityWriteResult<T>> executeSave(T entity, CqlIdentifier tableName,
|
||||
SimpleStatement statement, Consumer<WriteResult> beforeAfterSaveEvent) {
|
||||
|
||||
maybeEmitEvent(() -> new BeforeSaveEvent<>(entity, tableName, statement));
|
||||
T entityToSave = maybeCallBeforeSave(entity, tableName, statement);
|
||||
|
||||
ListenableFuture<AsyncResultSet> result = doQueryForResultSet(statement);
|
||||
CompletableFuture<AsyncResultSet> result = doQueryForResultSet(statement);
|
||||
|
||||
return new MappingListenableFutureAdapter<>(result, resultSet -> {
|
||||
return result.thenApply(resultSet -> {
|
||||
|
||||
EntityWriteResult<T> writeResult = new EntityWriteResult<>(
|
||||
Collections.singletonList(resultSet.getExecutionInfo()), resultSet.wasApplied(), getFirstPage(resultSet),
|
||||
@@ -789,14 +779,14 @@ public class AsyncCassandraTemplate
|
||||
});
|
||||
}
|
||||
|
||||
private ListenableFuture<WriteResult> executeDelete(Object entity, CqlIdentifier tableName, SimpleStatement statement,
|
||||
Consumer<WriteResult> resultConsumer) {
|
||||
private CompletableFuture<WriteResult> executeDelete(Object entity, CqlIdentifier tableName,
|
||||
SimpleStatement statement, Consumer<WriteResult> resultConsumer) {
|
||||
|
||||
maybeEmitEvent(() -> new BeforeDeleteEvent<>(statement, entity.getClass(), tableName));
|
||||
|
||||
ListenableFuture<AsyncResultSet> result = doQueryForResultSet(statement);
|
||||
CompletableFuture<AsyncResultSet> result = doQueryForResultSet(statement);
|
||||
|
||||
return new MappingListenableFutureAdapter<>(result, resultSet -> {
|
||||
return result.thenApply(resultSet -> {
|
||||
|
||||
WriteResult writeResult = new WriteResult(Collections.singletonList(resultSet.getExecutionInfo()),
|
||||
resultSet.wasApplied(), getFirstPage(resultSet));
|
||||
@@ -809,7 +799,7 @@ public class AsyncCassandraTemplate
|
||||
});
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<List<T>> doQuery(Statement<?> statement, RowMapper<T> rowMapper) {
|
||||
private <T> CompletableFuture<List<T>> doQuery(Statement<?> statement, RowMapper<T> rowMapper) {
|
||||
|
||||
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
|
||||
|
||||
@@ -820,7 +810,7 @@ public class AsyncCassandraTemplate
|
||||
return getAsyncCqlOperations().query(statement, rowMapper);
|
||||
}
|
||||
|
||||
private ListenableFuture<Void> doQuery(Statement<?> statement, RowCallbackHandler callbackHandler) {
|
||||
private CompletableFuture<Void> doQuery(Statement<?> statement, RowCallbackHandler callbackHandler) {
|
||||
|
||||
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
|
||||
|
||||
@@ -831,20 +821,22 @@ public class AsyncCassandraTemplate
|
||||
return getAsyncCqlOperations().query(statement, callbackHandler);
|
||||
}
|
||||
|
||||
private ListenableFuture<AsyncResultSet> doQueryForResultSet(Statement<?> statement) {
|
||||
private CompletableFuture<AsyncResultSet> doQueryForResultSet(Statement<?> statement) {
|
||||
return doExecute(statement, Function.identity());
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<T> doExecute(Statement<?> statement, Function<AsyncResultSet, T> mappingFunction) {
|
||||
private <T> CompletableFuture<T> doExecute(Statement<?> statement, Function<AsyncResultSet, T> mappingFunction) {
|
||||
|
||||
if (PreparedStatementDelegate.canPrepare(isUsePreparedStatements(), statement, log)) {
|
||||
|
||||
AsyncPreparedStatementHandler statementHandler = createPreparedStatementHandler(statement);
|
||||
|
||||
return getAsyncCqlOperations().query(statementHandler, statementHandler,
|
||||
(AsyncResultSetExtractor<T>) resultSet -> new AsyncResult<>(mappingFunction.apply(resultSet)));
|
||||
(AsyncResultSetExtractor<T>) resultSet -> CompletableFuture
|
||||
.completedFuture(mappingFunction.apply(resultSet)));
|
||||
}
|
||||
|
||||
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().queryForResultSet(statement), mappingFunction);
|
||||
return getAsyncCqlOperations().queryForResultSet(statement).thenApply(mappingFunction);
|
||||
}
|
||||
|
||||
private static List<Row> getFirstPage(AsyncResultSet resultSet) {
|
||||
@@ -871,8 +863,8 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
class GetConfiguredPageSize implements AsyncSessionCallback<Integer>, CqlProvider {
|
||||
@Override
|
||||
public ListenableFuture<Integer> doInSession(CqlSession session) {
|
||||
return AsyncResult.forValue(getConfiguredPageSize(session));
|
||||
public CompletableFuture<Integer> doInSession(CqlSession session) {
|
||||
return CompletableFuture.completedFuture(getConfiguredPageSize(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -881,7 +873,7 @@ public class AsyncCassandraTemplate
|
||||
}
|
||||
}
|
||||
|
||||
return getAsyncCqlOperations().execute(new GetConfiguredPageSize()).completable().join();
|
||||
return getAsyncCqlOperations().execute(new GetConfiguredPageSize()).join();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -936,22 +928,6 @@ public class AsyncCassandraTemplate
|
||||
return object;
|
||||
}
|
||||
|
||||
static class MappingListenableFutureAdapter<T, S>
|
||||
extends org.springframework.util.concurrent.ListenableFutureAdapter<T, S> {
|
||||
|
||||
private final Function<S, T> mapper;
|
||||
|
||||
MappingListenableFutureAdapter(ListenableFuture<S> adaptee, Function<S, T> mapper) {
|
||||
super(adaptee);
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T adapt(@Nullable S adapteeResult) {
|
||||
return this.mapper.apply(adapteeResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General callback interface used to create and bind prepared CQL statements.
|
||||
* <p>
|
||||
@@ -971,19 +947,17 @@ public class AsyncCassandraTemplate
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public static class PreparedStatementHandler implements AsyncPreparedStatementHandler {
|
||||
static class PreparedStatementHandler implements AsyncPreparedStatementHandler {
|
||||
|
||||
private final SimpleStatement statement;
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
|
||||
public PreparedStatementHandler(Statement<?> statement, PersistenceExceptionTranslator exceptionTranslator) {
|
||||
public PreparedStatementHandler(Statement<?> statement) {
|
||||
this.statement = PreparedStatementDelegate.getStatementForPrepare(statement);
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
|
||||
return new CassandraFutureAdapter<>(doPrepare(session), exceptionTranslator);
|
||||
public CompletableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
|
||||
return doPrepare(session).toCompletableFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,7 +141,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute the a Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* Execute the given Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* into Spring's DAO exception hierarchy.
|
||||
*
|
||||
* @param statement a Cassandra {@link Statement}, must not be {@literal null}.
|
||||
@@ -373,7 +373,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
|
||||
<T> EntityWriteResult<T> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity and return the entity if the delete was applied.
|
||||
* Delete the given entity and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
@@ -381,7 +381,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
|
||||
void delete(Object entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link QueryOptions} and return the entity if the delete was applied.
|
||||
* Delete the given entity applying {@link QueryOptions} and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
@@ -392,7 +392,7 @@ public interface CassandraOperations extends FluentCassandraOperations {
|
||||
WriteResult delete(Object entity, QueryOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link DeleteOptions} and return the entity if the delete was applied.
|
||||
* Delete the given entity applying {@link DeleteOptions} and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
|
||||
@@ -32,12 +32,12 @@ public class EntityWriteResult<T> extends WriteResult {
|
||||
|
||||
private final T entity;
|
||||
|
||||
EntityWriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows, T entity) {
|
||||
protected EntityWriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows, T entity) {
|
||||
super(executionInfo, wasApplied, rows);
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
EntityWriteResult(ResultSet resultSet, T entity) {
|
||||
protected EntityWriteResult(ResultSet resultSet, T entity) {
|
||||
super(resultSet);
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute the a Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* Execute the given Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* into Spring's DAO exception hierarchy.
|
||||
*
|
||||
* @param statement a Cassandra {@link Statement}, must not be {@literal null}.
|
||||
@@ -318,7 +318,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
|
||||
<T> Mono<EntityWriteResult<T>> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity and emit the entity if the delete was applied.
|
||||
* Delete the given entity and emit the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return the deleted entity.
|
||||
@@ -327,7 +327,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
|
||||
<T> Mono<T> delete(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link QueryOptions} and emit the entity if the delete was applied.
|
||||
* Delete the given entity applying {@link QueryOptions} and emit the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
@@ -338,7 +338,7 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
|
||||
Mono<WriteResult> delete(Object entity, QueryOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link QueryOptions} and emit the entity if the delete was applied.
|
||||
* Delete the given entity applying {@link QueryOptions} and emit the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
|
||||
@@ -211,7 +211,7 @@ public class StatementFactory {
|
||||
* @param tableName must not be {@literal null}.
|
||||
* @return the select builder.
|
||||
*/
|
||||
StatementBuilder<Select> selectOneById(Object id, CassandraPersistentEntity<?> persistentEntity,
|
||||
public StatementBuilder<Select> selectOneById(Object id, CassandraPersistentEntity<?> persistentEntity,
|
||||
CqlIdentifier tableName) {
|
||||
|
||||
Where where = new Where();
|
||||
@@ -287,7 +287,7 @@ public class StatementFactory {
|
||||
* @param persistentEntity the {@link CassandraPersistentEntity} to write insert values.
|
||||
* @return the select builder.
|
||||
*/
|
||||
StatementBuilder<RegularInsert> insert(Object objectToInsert, WriteOptions options,
|
||||
public StatementBuilder<RegularInsert> insert(Object objectToInsert, WriteOptions options,
|
||||
CassandraPersistentEntity<?> persistentEntity, CqlIdentifier tableName) {
|
||||
|
||||
Assert.notNull(tableName, "TableName must not be null");
|
||||
@@ -420,7 +420,7 @@ public class StatementFactory {
|
||||
* @param tableName must not be {@literal null}.
|
||||
* @return the update builder.
|
||||
*/
|
||||
StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> update(Object objectToUpdate,
|
||||
public StatementBuilder<com.datastax.oss.driver.api.querybuilder.update.Update> update(Object objectToUpdate,
|
||||
WriteOptions options, CassandraPersistentEntity<?> entity, CqlIdentifier tableName) {
|
||||
|
||||
Assert.notNull(tableName, "TableName must not be null");
|
||||
@@ -459,7 +459,7 @@ public class StatementFactory {
|
||||
* @param tableName must not be {@literal null}.
|
||||
* @return the delete builder.
|
||||
*/
|
||||
StatementBuilder<Delete> deleteById(Object id, CassandraPersistentEntity<?> persistentEntity,
|
||||
public StatementBuilder<Delete> deleteById(Object id, CassandraPersistentEntity<?> persistentEntity,
|
||||
CqlIdentifier tableName) {
|
||||
|
||||
Where where = new Where();
|
||||
@@ -529,7 +529,7 @@ public class StatementFactory {
|
||||
* @param tableName must not be {@literal null}.
|
||||
* @return the delete builder.
|
||||
*/
|
||||
StatementBuilder<Delete> delete(Object entity, QueryOptions options, EntityWriter<Object, Object> entityWriter,
|
||||
public StatementBuilder<Delete> delete(Object entity, QueryOptions options, EntityWriter<Object, Object> entityWriter,
|
||||
CqlIdentifier tableName) {
|
||||
|
||||
Assert.notNull(tableName, "TableName must not be null");
|
||||
|
||||
@@ -40,14 +40,14 @@ public class WriteResult {
|
||||
|
||||
private final List<Row> rows;
|
||||
|
||||
WriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows) {
|
||||
protected WriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows) {
|
||||
|
||||
this.executionInfo = executionInfo;
|
||||
this.wasApplied = wasApplied;
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
WriteResult(ResultSet resultSet) {
|
||||
protected WriteResult(ResultSet resultSet) {
|
||||
|
||||
this.executionInfo = resultSet.getExecutionInfos();
|
||||
this.wasApplied = resultSet.wasApplied();
|
||||
|
||||
@@ -17,17 +17,17 @@ package org.springframework.data.cassandra.core.cql;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.ResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -58,7 +58,7 @@ public interface AsyncCqlOperations {
|
||||
* @return a result object returned by the action, or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> execute(AsyncSessionCallback<T> action) throws DataAccessException;
|
||||
<T> CompletableFuture<T> execute(AsyncSessionCallback<T> action) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with static CQL
|
||||
@@ -71,7 +71,7 @@ public interface AsyncCqlOperations {
|
||||
* @return boolean value whether the statement was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(String cql) throws DataAccessException;
|
||||
CompletableFuture<Boolean> execute(String cql) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Issue a single CQL operation (such as an insert, update or delete statement) via a prepared statement, binding the
|
||||
@@ -83,7 +83,7 @@ public interface AsyncCqlOperations {
|
||||
* @return boolean value whether the statement was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(String cql, Object... args) throws DataAccessException;
|
||||
CompletableFuture<Boolean> execute(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Issue an statement using a {@link PreparedStatementBinder} to set bind parameters, with given CQL. Simpler than
|
||||
@@ -97,7 +97,7 @@ public interface AsyncCqlOperations {
|
||||
* @return boolean value whether the statement was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException;
|
||||
CompletableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
|
||||
@@ -112,7 +112,7 @@ public interface AsyncCqlOperations {
|
||||
* @return a result object returned by the action, or {@literal null}
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException;
|
||||
<T> CompletableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
@@ -126,7 +126,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, AsyncResultSetExtractor, Object...)
|
||||
*/
|
||||
<T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
<T> CompletableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
|
||||
@@ -140,7 +140,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, RowCallbackHandler, Object[])
|
||||
*/
|
||||
ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
CompletableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
|
||||
@@ -154,7 +154,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, RowMapper, Object[])
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, reading the
|
||||
@@ -167,7 +167,7 @@ public interface AsyncCqlOperations {
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
|
||||
<T> CompletableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -180,7 +180,7 @@ public interface AsyncCqlOperations {
|
||||
* CQL type)
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler, Object... args)
|
||||
CompletableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler, Object... args)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -194,7 +194,7 @@ public interface AsyncCqlOperations {
|
||||
* @return the result {@link List}, containing mapped objects
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query using a prepared statement, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
@@ -207,7 +207,7 @@ public interface AsyncCqlOperations {
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
<T> CompletableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -222,8 +222,8 @@ public interface AsyncCqlOperations {
|
||||
* @param rowCallbackHandler 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<Void> query(String cql, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler)
|
||||
throws DataAccessException;
|
||||
CompletableFuture<Void> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a {@link PreparedStatementBinder} implementation that
|
||||
@@ -237,7 +237,7 @@ public interface AsyncCqlOperations {
|
||||
* @return the result {@link List}, containing mapped objects.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
|
||||
<T> CompletableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -255,7 +255,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForList(String, Object[])
|
||||
*/
|
||||
ListenableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException;
|
||||
CompletableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
|
||||
@@ -272,7 +272,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForList(String)
|
||||
*/
|
||||
ListenableFuture<List<Map<String, Object>>> queryForList(String cql, Object... args) throws DataAccessException;
|
||||
CompletableFuture<List<Map<String, Object>>> queryForList(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
@@ -291,7 +291,7 @@ public interface AsyncCqlOperations {
|
||||
* @see #queryForList(String, Class, Object[])
|
||||
* @see SingleColumnRowMapper
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
|
||||
@@ -310,7 +310,7 @@ public interface AsyncCqlOperations {
|
||||
* @see #queryForList(String, Class)
|
||||
* @see SingleColumnRowMapper
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args)
|
||||
<T> CompletableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -330,7 +330,7 @@ public interface AsyncCqlOperations {
|
||||
* @see #queryForMap(String, Object[])
|
||||
* @see ColumnMapRowMapper
|
||||
*/
|
||||
ListenableFuture<Map<String, Object>> queryForMap(String cql) throws DataAccessException;
|
||||
CompletableFuture<Map<String, Object>> queryForMap(String cql) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
|
||||
@@ -349,7 +349,7 @@ public interface AsyncCqlOperations {
|
||||
* @see #queryForMap(String)
|
||||
* @see ColumnMapRowMapper
|
||||
*/
|
||||
ListenableFuture<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException;
|
||||
CompletableFuture<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result object, given static CQL.
|
||||
@@ -369,7 +369,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForObject(String, Class, Object[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType) throws DataAccessException;
|
||||
<T> CompletableFuture<T> queryForObject(String cql, Class<T> requiredType) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
|
||||
@@ -388,7 +388,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForObject(String, Class)
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args) throws DataAccessException;
|
||||
<T> CompletableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
|
||||
@@ -404,7 +404,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForObject(String, RowMapper, Object[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
<T> CompletableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, mapping a
|
||||
@@ -418,7 +418,8 @@ public interface AsyncCqlOperations {
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not return exactly one row.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
|
||||
<T> CompletableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a ResultSet, given static CQL.
|
||||
@@ -434,7 +435,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForResultSet(String, Object[])
|
||||
*/
|
||||
ListenableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException;
|
||||
CompletableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query given CQL to create a prepared statement from CQL and a list of arguments to bind to the query, expecting a
|
||||
@@ -449,7 +450,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForResultSet(String)
|
||||
*/
|
||||
ListenableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException;
|
||||
CompletableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
|
||||
@@ -462,7 +463,7 @@ public interface AsyncCqlOperations {
|
||||
* @return boolean value whether the statement was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException;
|
||||
CompletableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
@@ -476,7 +477,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, AsyncResultSetExtractor, Object...)
|
||||
*/
|
||||
<T> ListenableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
<T> CompletableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -491,7 +492,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, RowCallbackHandler, Object[])
|
||||
*/
|
||||
ListenableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
|
||||
CompletableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -506,7 +507,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, RowMapper, Object[])
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
@@ -523,7 +524,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForList(String, Object[])
|
||||
*/
|
||||
ListenableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException;
|
||||
CompletableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
@@ -542,7 +543,7 @@ public interface AsyncCqlOperations {
|
||||
* @see #queryForList(String, Class, Object[])
|
||||
* @see SingleColumnRowMapper
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType) throws DataAccessException;
|
||||
<T> CompletableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result Map, given static CQL.
|
||||
@@ -561,7 +562,7 @@ public interface AsyncCqlOperations {
|
||||
* @see #queryForMap(String, Object[])
|
||||
* @see ColumnMapRowMapper
|
||||
*/
|
||||
ListenableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException;
|
||||
CompletableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result object, given static CQL.
|
||||
@@ -581,7 +582,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForObject(String, Class, Object[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType) throws DataAccessException;
|
||||
<T> CompletableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
|
||||
@@ -597,7 +598,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForObject(String, RowMapper, Object[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
<T> CompletableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a ResultSet, given static CQL.
|
||||
@@ -613,7 +614,7 @@ public interface AsyncCqlOperations {
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForResultSet(String, Object[])
|
||||
*/
|
||||
ListenableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException;
|
||||
CompletableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.PreparedStatement
|
||||
@@ -627,7 +628,7 @@ public interface AsyncCqlOperations {
|
||||
* @return boolean value whether the statement was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator) throws DataAccessException;
|
||||
CompletableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a CQL data access operation, implemented as callback action working on a CQL {@link PreparedStatement}.
|
||||
@@ -643,7 +644,7 @@ public interface AsyncCqlOperations {
|
||||
* @return a result object returned by the action, or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> execute(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
<T> CompletableFuture<T> execute(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
PreparedStatementCallback<T> action) throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -655,7 +656,7 @@ public interface AsyncCqlOperations {
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
<T> CompletableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -667,7 +668,7 @@ public interface AsyncCqlOperations {
|
||||
* @param rowCallbackHandler 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<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
CompletableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -679,7 +680,7 @@ public interface AsyncCqlOperations {
|
||||
* @return the result {@link List}, containing mapped objects.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator, RowMapper<T> rowMapper)
|
||||
<T> CompletableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator, RowMapper<T> rowMapper)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -695,7 +696,7 @@ public interface AsyncCqlOperations {
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
<T> CompletableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -710,7 +711,7 @@ public interface AsyncCqlOperations {
|
||||
* @param rowCallbackHandler 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<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
CompletableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
|
||||
/**
|
||||
@@ -726,6 +727,6 @@ public interface AsyncCqlOperations {
|
||||
* @return the result {@link List}, containing mapped objects.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
<T> CompletableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core.cql;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
@@ -26,11 +27,8 @@ import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.DataAccessUtils;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.cassandra.SessionFactory;
|
||||
import org.springframework.data.cassandra.core.cql.util.CassandraFutureAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
@@ -68,7 +66,8 @@ import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
* @see ListenableFuture
|
||||
* @since 2.0
|
||||
* @see CompletableFuture
|
||||
* @see PreparedStatementCreator
|
||||
* @see PreparedStatementBinder
|
||||
* @see PreparedStatementCallback
|
||||
@@ -119,7 +118,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> execute(AsyncSessionCallback<T> action) throws DataAccessException {
|
||||
public <T> CompletableFuture<T> execute(AsyncSessionCallback<T> action) throws DataAccessException {
|
||||
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
|
||||
@@ -135,15 +134,15 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(String cql) throws DataAccessException {
|
||||
public CompletableFuture<Boolean> execute(String cql) throws DataAccessException {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
|
||||
return new MappingListenableFutureAdapter<>(queryForResultSet(cql), AsyncResultSet::wasApplied);
|
||||
return queryForResultSet(cql).thenApply(AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
public <T> CompletableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
@@ -156,59 +155,58 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
|
||||
CompletionStage<T> results = getCurrentSession().executeAsync(applyStatementSettings(newStatement(cql)))
|
||||
.thenApply(resultSetExtractor::extractData) //
|
||||
.thenCompose(ListenableFuture::completable);
|
||||
.thenCompose(Function.identity());
|
||||
|
||||
return new CassandraFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", cql, ex));
|
||||
return results.exceptionallyCompose(exceptionComposition(ex -> translateExceptionIfPossible("Query", cql, ex)))
|
||||
.toCompletableFuture();
|
||||
} catch (DriverException e) {
|
||||
throw translateException("Query", cql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
public CompletableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(cql, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new MappingListenableFutureAdapter<>(results, o -> null);
|
||||
CompletableFuture<?> results = query(cql, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return results.thenApply(o -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
public <T> CompletableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(cql, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException {
|
||||
public CompletableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException {
|
||||
return query(cql, newAsyncResultSetExtractor(newColumnMapRowMapper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException {
|
||||
public <T> CompletableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException {
|
||||
return query(cql, newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Map<String, Object>> queryForMap(String cql) throws DataAccessException {
|
||||
public CompletableFuture<Map<String, Object>> queryForMap(String cql) throws DataAccessException {
|
||||
return queryForObject(cql, newColumnMapRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType) throws DataAccessException {
|
||||
public <T> CompletableFuture<T> queryForObject(String cql, Class<T> requiredType) throws DataAccessException {
|
||||
return queryForObject(cql, newSingleColumnRowMapper(requiredType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
public <T> CompletableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
|
||||
ListenableFuture<List<T>> results = query(cql, newAsyncResultSetExtractor(rowMapper));
|
||||
CompletableFuture<List<T>> results = query(cql, newAsyncResultSetExtractor(rowMapper));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(
|
||||
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
|
||||
return results.thenApply(DataAccessUtils::requiredSingleResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException {
|
||||
public CompletableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException {
|
||||
return query(cql, AsyncCqlTemplate::toResultSet);
|
||||
}
|
||||
|
||||
@@ -217,15 +215,15 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException {
|
||||
public CompletableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException {
|
||||
|
||||
Assert.notNull(statement, "CQL Statement must not be null");
|
||||
|
||||
return new MappingListenableFutureAdapter<>(queryForResultSet(statement), AsyncResultSet::wasApplied);
|
||||
return queryForResultSet(statement).thenApply(AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
public <T> CompletableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(statement, "CQL Statement must not be null");
|
||||
@@ -239,64 +237,64 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
CompletionStage<T> results = getCurrentSession() //
|
||||
.executeAsync(applyStatementSettings(statement)) //
|
||||
.thenApply(resultSetExtractor::extractData) //
|
||||
.thenCompose(ListenableFuture::completable);
|
||||
.thenCompose(Function.identity());
|
||||
|
||||
return new CassandraFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", toCql(statement), ex));
|
||||
return results
|
||||
.exceptionallyCompose(exceptionComposition(ex -> translateExceptionIfPossible("Query", toCql(statement), ex)))
|
||||
.toCompletableFuture();
|
||||
} catch (DriverException e) {
|
||||
throw translateException("Query", toCql(statement), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
|
||||
public CompletableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> result = query(statement, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
CompletableFuture<?> result = query(statement, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(result, o -> null),
|
||||
getExceptionTranslator());
|
||||
return result.thenApply(it -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper)
|
||||
public <T> CompletableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper)
|
||||
throws DataAccessException {
|
||||
return query(statement, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException {
|
||||
public CompletableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException {
|
||||
return query(statement, newAsyncResultSetExtractor(newColumnMapRowMapper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType)
|
||||
public <T> CompletableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType)
|
||||
throws DataAccessException {
|
||||
return query(statement, newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException {
|
||||
public CompletableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException {
|
||||
return queryForObject(statement, newColumnMapRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType)
|
||||
public <T> CompletableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType)
|
||||
throws DataAccessException {
|
||||
return queryForObject(statement, newSingleColumnRowMapper(requiredType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper)
|
||||
public <T> CompletableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<List<T>> results = query(statement, newAsyncResultSetExtractor(rowMapper));
|
||||
CompletableFuture<List<T>> results = query(statement, newAsyncResultSetExtractor(rowMapper));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(
|
||||
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
|
||||
return results.thenApply(DataAccessUtils::requiredSingleResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException {
|
||||
public CompletableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException {
|
||||
return query(statement, AsyncCqlTemplate::toResultSet);
|
||||
}
|
||||
|
||||
@@ -305,33 +303,32 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator)
|
||||
public CompletableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator)
|
||||
throws DataAccessException {
|
||||
|
||||
return new MappingListenableFutureAdapter<>(query(preparedStatementCreator, AsyncCqlTemplate::toResultSet),
|
||||
AsyncResultSet::wasApplied);
|
||||
return query(preparedStatementCreator, AsyncCqlTemplate::toResultSet).thenApply(AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(String cql, Object... args) throws DataAccessException {
|
||||
public CompletableFuture<Boolean> execute(String cql, Object... args) throws DataAccessException {
|
||||
return execute(cql, newPreparedStatementBinder(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb)
|
||||
public CompletableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb)
|
||||
throws DataAccessException {
|
||||
|
||||
return new MappingListenableFutureAdapter<>(
|
||||
query(newAsyncPreparedStatementCreator(cql), psb, AsyncCqlTemplate::toResultSet), AsyncResultSet::wasApplied);
|
||||
return query(newAsyncPreparedStatementCreator(cql), psb, AsyncCqlTemplate::toResultSet)
|
||||
.thenApply(AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException {
|
||||
public <T> CompletableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException {
|
||||
return execute(newAsyncPreparedStatementCreator(cql), action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> execute(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public <T> CompletableFuture<T> execute(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
PreparedStatementCallback<T> action) throws DataAccessException {
|
||||
|
||||
Assert.notNull(preparedStatementCreator, "PreparedStatementCreator must not be null");
|
||||
@@ -346,14 +343,15 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
}
|
||||
|
||||
CqlSession currentSession = getCurrentSession();
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
|
||||
preparedStatementCreator.createPreparedStatement(currentSession), preparedStatement -> {
|
||||
try {
|
||||
return action.doInPreparedStatement(currentSession, preparedStatement);
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
}
|
||||
}), getExceptionTranslator());
|
||||
|
||||
return preparedStatementCreator.createPreparedStatement(currentSession).thenApply(preparedStatement -> {
|
||||
try {
|
||||
return action.doInPreparedStatement(currentSession, preparedStatement);
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
}
|
||||
}).toCompletableFuture()
|
||||
.exceptionallyCompose(exceptionComposition(exceptionTranslator::translateExceptionIfPossible));
|
||||
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
@@ -361,31 +359,31 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public <T> CompletableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
|
||||
|
||||
return query(preparedStatementCreator, null, resultSetExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public CompletableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(preparedStatementCreator, null, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
CompletableFuture<?> results = query(preparedStatementCreator, null,
|
||||
newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
return results.thenApply(o -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public <T> CompletableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
RowMapper<T> rowMapper) throws DataAccessException {
|
||||
|
||||
return query(preparedStatementCreator, null, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public <T> CompletableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
|
||||
|
||||
Assert.notNull(preparedStatementCreator, "AsyncPreparedStatementCreator must not be null");
|
||||
@@ -403,8 +401,8 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
|
||||
CqlSession session = getCurrentSession();
|
||||
|
||||
ListenableFuture<Statement<?>> statementFuture = new MappingListenableFutureAdapter<>(
|
||||
preparedStatementCreator.createPreparedStatement(session), preparedStatement -> {
|
||||
CompletionStage<Statement<?>> statementFuture = preparedStatementCreator.createPreparedStatement(session)
|
||||
.thenApply(preparedStatement -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Executing prepared statement [%s]", toCql(preparedStatement)));
|
||||
}
|
||||
@@ -412,84 +410,81 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
return applyStatementSettings(psb != null ? psb.bindValues(preparedStatement) : preparedStatement.bind());
|
||||
});
|
||||
|
||||
CompletableFuture<T> result = statementFuture.completable() //
|
||||
CompletableFuture<T> result = statementFuture.toCompletableFuture() //
|
||||
.thenCompose(session::executeAsync) //
|
||||
.thenApply(resultSetExtractor::extractData) //
|
||||
.thenCompose(ListenableFuture::completable);
|
||||
.thenCompose(Function.identity());
|
||||
|
||||
return new CassandraFutureAdapter<>(result, exceptionTranslator);
|
||||
return result.exceptionallyCompose(exceptionComposition(this::translateExceptionIfPossible));
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public CompletableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
CompletableFuture<?> results = query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
return results.thenApply(o -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
public <T> CompletableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
|
||||
public <T> CompletableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
|
||||
throws DataAccessException {
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), resultSetExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler, Object... args)
|
||||
public CompletableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
CompletableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
return results.thenApply(o -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
public <T> CompletableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
throws DataAccessException {
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
public <T> CompletableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), psb, resultSetExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
public CompletableFuture<Void> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), psb,
|
||||
CompletableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), psb,
|
||||
newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
return results.thenApply(o -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
|
||||
public <T> CompletableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
|
||||
throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), psb, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<Map<String, Object>>> queryForList(String cql, Object... args)
|
||||
public CompletableFuture<List<Map<String, Object>>> queryForList(String cql, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
@@ -497,7 +492,7 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args)
|
||||
public <T> CompletableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
@@ -505,30 +500,29 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException {
|
||||
public CompletableFuture<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException {
|
||||
return queryForObject(cql, newColumnMapRowMapper(), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args)
|
||||
public <T> CompletableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
return queryForObject(cql, newSingleColumnRowMapper(requiredType), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
public <T> CompletableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<List<T>> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
CompletableFuture<List<T>> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(rowMapper));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(
|
||||
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
|
||||
return results.thenApply(DataAccessUtils::requiredSingleResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException {
|
||||
public CompletableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException {
|
||||
return query(cql, AsyncCqlTemplate::toResultSet, args);
|
||||
}
|
||||
|
||||
@@ -612,12 +606,30 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
return sessionFactory.getSession();
|
||||
}
|
||||
|
||||
private static ListenableFuture<AsyncResultSet> toResultSet(AsyncResultSet resultSet) {
|
||||
private static <T> Function<Throwable, CompletionStage<T>> exceptionComposition(
|
||||
Function<RuntimeException, Exception> exceptionTranslator) {
|
||||
return throwable -> {
|
||||
|
||||
SettableListenableFuture<AsyncResultSet> future = new SettableListenableFuture<>();
|
||||
future.set(resultSet);
|
||||
Throwable toTranslate = throwable;
|
||||
Throwable translated = null;
|
||||
if (toTranslate instanceof ExecutionException e) {
|
||||
toTranslate = e.getCause();
|
||||
}
|
||||
|
||||
return future;
|
||||
if (toTranslate instanceof CompletionException e) {
|
||||
toTranslate = e.getCause();
|
||||
}
|
||||
|
||||
if (toTranslate instanceof RuntimeException e) {
|
||||
translated = exceptionTranslator.apply(e);
|
||||
}
|
||||
|
||||
return CompletableFuture.failedFuture(translated != null ? translated : toTranslate);
|
||||
};
|
||||
}
|
||||
|
||||
private static CompletableFuture<AsyncResultSet> toResultSet(AsyncResultSet resultSet) {
|
||||
return CompletableFuture.completedFuture(resultSet);
|
||||
}
|
||||
|
||||
private static RuntimeException translateException(PersistenceExceptionTranslator exceptionTranslator,
|
||||
@@ -641,8 +653,10 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
|
||||
return new CassandraFutureAdapter<>(session.prepareAsync(this.statement), exceptionTranslator);
|
||||
public CompletableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
|
||||
return session.prepareAsync(this.statement)
|
||||
.exceptionallyCompose(exceptionComposition(exceptionTranslator::translateExceptionIfPossible))
|
||||
.toCompletableFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -663,26 +677,8 @@ public class AsyncCqlTemplate extends CassandraAccessor implements AsyncCqlOpera
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ListenableFuture<Void> extractData(AsyncResultSet resultSet) {
|
||||
public CompletableFuture<Void> extractData(AsyncResultSet resultSet) {
|
||||
return AsyncResultStream.from(resultSet).forEach(rowCallbackHandler::processRow);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.core.cql;
|
||||
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
@@ -23,8 +23,8 @@ import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
|
||||
/**
|
||||
* 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 CqlSession},
|
||||
* provided by the {@link CqlTemplate} class.
|
||||
* statement returning a {@link CompletionStage} given a {@link CqlSession}, provided by the {@link AsyncCqlTemplate}
|
||||
* class.
|
||||
* <p>
|
||||
* 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
|
||||
@@ -52,5 +52,5 @@ public interface AsyncPreparedStatementCreator {
|
||||
* @throws DriverException there is no need to catch DriverException that may be thrown in the implementation of this
|
||||
* method. The {@link AsyncCqlTemplate} class will handle them.
|
||||
*/
|
||||
ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException;
|
||||
CompletionStage<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.core.cql;
|
||||
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
|
||||
/**
|
||||
* Callback interface used by {@link AsyncCqlTemplate}'s query methods. Implementations of this interface perform the
|
||||
@@ -52,6 +52,5 @@ public interface AsyncResultSetExtractor<T> {
|
||||
* there's no need to catch {@link DriverException})
|
||||
* @throws DataAccessException in case of custom exceptions
|
||||
*/
|
||||
@Nullable
|
||||
ListenableFuture<T> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException;
|
||||
CompletableFuture<T> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException;
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.core.cql;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collector;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
/**
|
||||
* Asynchronous supplied sequence of elements supporting sequential operations over a {@link AsyncResultSet a result
|
||||
@@ -84,11 +84,11 @@ class AsyncResultStream<T> {
|
||||
* @param collector the {@link Collector} describing the reduction
|
||||
* @return the result of the reduction
|
||||
*/
|
||||
<R, A> ListenableFuture<R> collect(Collector<? super T, A, R> collector) {
|
||||
<R, A> CompletableFuture<R> collect(Collector<? super T, A, R> collector) {
|
||||
|
||||
Assert.notNull(collector, "Collector must not be null");
|
||||
|
||||
SettableListenableFuture<R> future = new SettableListenableFuture<>();
|
||||
CompletableFuture<R> future = new CompletableFuture<>();
|
||||
CollectState<A, R> collectState = new CollectState<>(collector);
|
||||
|
||||
collectState.collectAsync(future, this.resultSet);
|
||||
@@ -106,11 +106,11 @@ class AsyncResultStream<T> {
|
||||
*
|
||||
* @param action a non-interfering action to perform on the elements.
|
||||
*/
|
||||
ListenableFuture<Void> forEach(Consumer<T> action) {
|
||||
CompletableFuture<Void> forEach(Consumer<T> action) {
|
||||
|
||||
Assert.notNull(action, "Action must not be null");
|
||||
|
||||
SettableListenableFuture<Void> future = new SettableListenableFuture<>();
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
ForwardLoopState loopState = new ForwardLoopState(action);
|
||||
|
||||
loopState.forEachAsync(future, this.resultSet);
|
||||
@@ -140,7 +140,7 @@ class AsyncResultStream<T> {
|
||||
* @param target
|
||||
* @param resultSet
|
||||
*/
|
||||
void forEachAsync(SettableListenableFuture<Void> target, AsyncResultSet resultSet) {
|
||||
void forEachAsync(CompletableFuture<Void> target, AsyncResultSet resultSet) {
|
||||
|
||||
if (target.isCancelled()) {
|
||||
return;
|
||||
@@ -149,12 +149,12 @@ class AsyncResultStream<T> {
|
||||
try {
|
||||
peekRow(resultSet.currentPage());
|
||||
} catch (RuntimeException e) {
|
||||
target.setException(e);
|
||||
target.completeExceptionally(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resultSet.hasMorePages()) {
|
||||
target.set(null);
|
||||
target.complete(null);
|
||||
} else {
|
||||
|
||||
CompletionStage<AsyncResultSet> nextPage = resultSet.fetchNextPage();
|
||||
@@ -162,7 +162,7 @@ class AsyncResultStream<T> {
|
||||
nextPage.whenComplete((nextResultSet, throwable) -> {
|
||||
|
||||
if (throwable != null) {
|
||||
target.setException(throwable);
|
||||
target.completeExceptionally(throwable);
|
||||
} else {
|
||||
forEachAsync(target, nextResultSet);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ class AsyncResultStream<T> {
|
||||
* @param target
|
||||
* @param resultSet
|
||||
*/
|
||||
void collectAsync(SettableListenableFuture<R> target, AsyncResultSet resultSet) {
|
||||
void collectAsync(CompletableFuture<R> target, AsyncResultSet resultSet) {
|
||||
|
||||
if (target.isCancelled()) {
|
||||
return;
|
||||
@@ -211,12 +211,12 @@ class AsyncResultStream<T> {
|
||||
try {
|
||||
collectPage(resultSet.currentPage());
|
||||
} catch (RuntimeException e) {
|
||||
target.setException(e);
|
||||
target.completeExceptionally(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resultSet.hasMorePages()) {
|
||||
target.set(finish());
|
||||
target.complete(finish());
|
||||
} else {
|
||||
|
||||
CompletionStage<AsyncResultSet> nextPage = resultSet.fetchNextPage();
|
||||
@@ -224,7 +224,7 @@ class AsyncResultStream<T> {
|
||||
nextPage.whenComplete((nextResultSet, throwable) -> {
|
||||
|
||||
if (throwable != null) {
|
||||
target.setException(throwable);
|
||||
target.completeExceptionally(throwable);
|
||||
} else {
|
||||
collectAsync(target, nextResultSet);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
package org.springframework.data.cassandra.core.cql;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
@@ -57,7 +57,7 @@ public class AsyncRowMapperResultSetExtractor<T> implements AsyncResultSetExtrac
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<T>> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException {
|
||||
public CompletableFuture<List<T>> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException {
|
||||
return AsyncResultStream.from(resultSet).map(rowMapper).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,24 @@
|
||||
*/
|
||||
package org.springframework.data.cassandra.core.cql;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
|
||||
/**
|
||||
* Generic callback interface for code that operates asynchronously on a Cassandra {@link CqlSession}. Allows to execute
|
||||
* any number of operations on a single session, using any type and number of statements.
|
||||
* <p>
|
||||
* This is particularly useful for delegating to existing data access code that expects a {@link CqlSession} 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.
|
||||
* and throws {@link DriverException}. For newly written code, it is strongly recommended to use
|
||||
* {@link AsyncCqlTemplate}'s more specific operations, for example a {@code query} or {@code update} variant.
|
||||
*
|
||||
* @author David Webb
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
* @see AsyncCqlTemplate#execute(AsyncSessionCallback)
|
||||
* @see AsyncCqlTemplate#query
|
||||
* @see AsyncCqlTemplate#execute(String)
|
||||
@@ -39,20 +41,20 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
public interface AsyncSessionCallback<T> {
|
||||
|
||||
/**
|
||||
* Gets called by {@link CqlTemplate#execute} with an active Cassandra {@link CqlSession}. Does not need to care about
|
||||
* activating or closing the {@link CqlSession}.
|
||||
* Gets called by {@link AsyncCqlTemplate#execute} with an active Cassandra {@link CqlSession}. Does not need to care
|
||||
* about activating or closing the {@link CqlSession}.
|
||||
* <p>
|
||||
* 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
|
||||
* objects. Note that there's special support for single step actions: see {@link AsyncCqlTemplate#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 ListenableFuture<Void>} if none.
|
||||
* @return a result object, or {@code CompletableFuture<Void>} 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<T> doInSession(CqlSession session) throws DriverException, DataAccessException;
|
||||
CompletableFuture<T> doInSession(CqlSession session) throws DriverException, DataAccessException;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.datastax.oss.driver.api.core.cql.ResultSet;
|
||||
|
||||
/**
|
||||
* Convenient converter that can be used to convert a single-row-single-column, single-row-multi-column, or multi-row
|
||||
* {@link ResultSet} into the a value of a given type. The majority of the expected usage is to convert a
|
||||
* {@link ResultSet} into the given value of a given type. The majority of the expected usage is to convert a
|
||||
* single-row-single-column result set into the target type.
|
||||
* <p>
|
||||
* The algorithm is:
|
||||
|
||||
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.cassandra.core.cql.ColumnMapRowMapper;
|
||||
import org.springframework.data.cassandra.core.cql.CqlOperations;
|
||||
import org.springframework.data.cassandra.core.cql.CqlTemplate;
|
||||
import org.springframework.data.cassandra.core.cql.PreparedStatementBinder;
|
||||
import org.springframework.data.cassandra.core.cql.PreparedStatementCallback;
|
||||
import org.springframework.data.cassandra.core.cql.RowCallbackHandler;
|
||||
import org.springframework.data.cassandra.core.cql.RowMapper;
|
||||
import org.springframework.data.cassandra.core.cql.SingleColumnRowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.ResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.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
|
||||
* @author John Blum
|
||||
* @since 4.0
|
||||
* @see AsyncCqlTemplate
|
||||
* @see CqlOperations
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant
|
||||
* {@link org.springframework.data.cassandra.core.cql.AsyncCqlOperations}.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
public interface AsyncCqlOperations {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with a plain com.datastax.oss.driver.api.core.CqlSession
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute a CQL data access operation, implemented as callback action working on a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}. This allows for implementing arbitrary data access operations,
|
||||
* within Spring's managed CQL environment: that is, converting CQL
|
||||
* {@link com.datastax.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
<T> ListenableFuture<T> execute(AsyncSessionCallback<T> 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 {@literal null} or empty.
|
||||
* @return boolean value whether the statement was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(String cql) 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 {@literal null} or empty.
|
||||
* @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 executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(String cql, Object... args) 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 {@literal null} or empty.
|
||||
* @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 executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb) 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.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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 executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @param resultSetExtractor object that will extract all rows of results, must not be {@literal null}.
|
||||
* @return an arbitrary result object, as returned by the AsyncResultSetExtractor.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, AsyncResultSetExtractor, Object...)
|
||||
*/
|
||||
<T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
|
||||
* {@link RowCallbackHandler}.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @param rowCallbackHandler 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<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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[])
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) 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 AsyncResultSetExtractor}.
|
||||
*
|
||||
* @param cql static CQL to execute, must not be {@literal null} or empty.
|
||||
* @param resultSetExtractor 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 AsyncResultSetExtractor}
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
|
||||
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 {@literal null} or empty.
|
||||
* @param rowCallbackHandler 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<Void> query(String cql, RowCallbackHandler rowCallbackHandler, 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 each
|
||||
* row to a Java object via a {@link RowMapper}.
|
||||
*
|
||||
* @param cql static CQL to execute, must not be {@literal null} or empty.
|
||||
* @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.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query using a prepared statement, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
*
|
||||
* @param cql static CQL to execute, must not be {@literal null} or empty.
|
||||
* @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 resultSetExtractor object that will extract results, must not be {@literal null}.
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) 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 {@literal null} or empty.
|
||||
* @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 rowCallbackHandler 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<Void> query(String cql, @Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler)
|
||||
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 {@literal null} or empty.
|
||||
* @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.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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<List<Map<String, Object>>> queryForList(String cql) 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}.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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<List<Map<String, Object>>> queryForList(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType) 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}.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result Map, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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<Map<String, Object>> queryForMap(String cql) 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result object, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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 {@literal null} 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[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType) 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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 {@literal null} 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)
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) 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 {@literal null} or empty.
|
||||
* @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.
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a ResultSet, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* The results will be mapped to an {@link ResultSet}.
|
||||
*
|
||||
* @param cql static CQL to execute, must not be {@literal null} or empty.
|
||||
* @return a {@link ResultSet} representation.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #queryForResultSet(String, Object[])
|
||||
*/
|
||||
ListenableFuture<AsyncResultSet> queryForResultSet(String cql) 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.
|
||||
* <p>
|
||||
* The results will be mapped to an {@link ResultSet}.
|
||||
*
|
||||
* @param cql static CQL to execute, must not be {@literal null} or empty.
|
||||
* @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<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.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<Boolean> execute(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
* <p>
|
||||
* 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 resultSetExtractor object that will extract all rows of results, must not be {@literal null}.
|
||||
* @return an arbitrary result object, as returned by the AsyncResultSetExtractor.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see #query(String, AsyncResultSetExtractor, Object...)
|
||||
*/
|
||||
<T> ListenableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, reading the {@link ResultSet} on a per-row basis with a
|
||||
* {@link RowCallbackHandler}.
|
||||
* <p>
|
||||
* 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 rowCallbackHandler 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<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping each row to a Java object via a {@link RowMapper}.
|
||||
* <p>
|
||||
* 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[])
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 {@literal null} or empty.
|
||||
* @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<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result {@link List}, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result Map, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a result object, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 {@literal null} 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[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query given static CQL, mapping a single result row to a Java object via a {@link RowMapper}.
|
||||
* <p>
|
||||
* 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[])
|
||||
*/
|
||||
<T> ListenableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a query for a ResultSet, given static CQL.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.PreparedStatement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 preparedStatementCreator 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 executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator) 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 {@link PreparedStatement}, within
|
||||
* Spring's managed CQL environment: that is, participating in Spring-managed transactions and converting CQL
|
||||
* {@link com.datastax.oss.driver.api.core.DriverException}s into Spring's {@link DataAccessException} hierarchy.
|
||||
* <p>
|
||||
* The callback action can return a result object, for example a domain object or a collection of domain objects.
|
||||
*
|
||||
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, 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 executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> execute(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
PreparedStatementCallback<T> action) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query using a prepared statement, reading the {@link ResultSet} with a {@link AsyncResultSetExtractor}.
|
||||
*
|
||||
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, must not be {@literal null}.
|
||||
* @param resultSetExtractor object that will extract results, must not be {@literal null}.
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query using a prepared statement, reading the {@link ResultSet} on a per-row basis with a
|
||||
* {@link RowCallbackHandler}.
|
||||
*
|
||||
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, must not be {@literal null}.
|
||||
* @param rowCallbackHandler 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<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Query using a prepared statement, mapping each row to a Java object via a {@link RowMapper}.
|
||||
*
|
||||
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, 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.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator, RowMapper<T> rowMapper)
|
||||
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 AsyncResultSetExtractor}.
|
||||
*
|
||||
* @param preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, 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 resultSetExtractor object that will extract results, must not be {@literal null}.
|
||||
* @return an arbitrary result object, as returned by the {@link AsyncResultSetExtractor}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, AsyncResultSetExtractor<T> resultSetExtractor) 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 preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, 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 rowCallbackHandler 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<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) 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 preparedStatementCreator object that can create a {@link PreparedStatement} given a
|
||||
* {@link com.datastax.oss.driver.api.core.CqlSession}, 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.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException;
|
||||
}
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.DataAccessUtils;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.cassandra.SessionFactory;
|
||||
import org.springframework.data.cassandra.core.cql.CassandraAccessor;
|
||||
import org.springframework.data.cassandra.core.cql.CqlProvider;
|
||||
import org.springframework.data.cassandra.core.cql.PreparedStatementBinder;
|
||||
import org.springframework.data.cassandra.core.cql.PreparedStatementCallback;
|
||||
import org.springframework.data.cassandra.core.cql.PreparedStatementCreator;
|
||||
import org.springframework.data.cassandra.core.cql.ResultSetExtractor;
|
||||
import org.springframework.data.cassandra.core.cql.RowCallbackHandler;
|
||||
import org.springframework.data.cassandra.core.cql.RowMapper;
|
||||
import org.springframework.data.cassandra.core.cql.RowMapperResultSetExtractor;
|
||||
import org.springframework.data.cassandra.core.cql.util.CassandraFutureAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.ResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
|
||||
/**
|
||||
* <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 AsyncResultSetExtractor} 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 CqlSession} reference, or get
|
||||
* prepared in an application context and given to services as bean reference. Note: The {@link CqlSession} 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 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.data.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
|
||||
* @since 4.0
|
||||
* @see ListenableFuture
|
||||
* @see PreparedStatementCreator
|
||||
* @see PreparedStatementBinder
|
||||
* @see PreparedStatementCallback
|
||||
* @see AsyncResultSetExtractor
|
||||
* @see RowCallbackHandler
|
||||
* @see RowMapper
|
||||
* @see PersistenceExceptionTranslator
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant
|
||||
* {@link org.springframework.data.cassandra.core.cql.AsyncCqlTemplate}.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
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 CqlSession}.
|
||||
*
|
||||
* @param session the active Cassandra {@link CqlSession}, must not be {@literal null}.
|
||||
* @throws IllegalStateException if {@link CqlSession} is {@literal null}.
|
||||
*/
|
||||
public AsyncCqlTemplate(CqlSession 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.oss.driver.api.core.CqlSession
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(String cql) throws DataAccessException {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
|
||||
return new MappingListenableFutureAdapter<>(queryForResultSet(cql), AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.hasText(cql, "CQL must not be empty");
|
||||
Assert.notNull(resultSetExtractor, "AsyncResultSetExtractor must not be null");
|
||||
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Executing CQL statement [%s]", cql));
|
||||
}
|
||||
|
||||
CompletionStage<T> results = getCurrentSession().executeAsync(applyStatementSettings(newStatement(cql)))
|
||||
.thenApply(resultSetExtractor::extractData) //
|
||||
.thenCompose(ListenableFuture::completable);
|
||||
|
||||
return new CassandraFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", cql, ex));
|
||||
} catch (DriverException e) {
|
||||
throw translateException("Query", cql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(cql, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new MappingListenableFutureAdapter<>(results, o -> null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(cql, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<Map<String, Object>>> queryForList(String cql) throws DataAccessException {
|
||||
return query(cql, newAsyncResultSetExtractor(newColumnMapRowMapper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType) throws DataAccessException {
|
||||
return query(cql, newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Map<String, Object>> queryForMap(String cql) throws DataAccessException {
|
||||
return queryForObject(cql, newColumnMapRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType) throws DataAccessException {
|
||||
return queryForObject(cql, newSingleColumnRowMapper(requiredType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
|
||||
ListenableFuture<List<T>> results = query(cql, newAsyncResultSetExtractor(rowMapper));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(
|
||||
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> queryForResultSet(String cql) throws DataAccessException {
|
||||
return query(cql, AsyncCqlTemplate::toResultSet);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(Statement<?> statement) throws DataAccessException {
|
||||
|
||||
Assert.notNull(statement, "CQL Statement must not be null");
|
||||
|
||||
return new MappingListenableFutureAdapter<>(queryForResultSet(statement), AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(Statement<?> statement, AsyncResultSetExtractor<T> resultSetExtractor)
|
||||
throws DataAccessException {
|
||||
|
||||
Assert.notNull(statement, "CQL Statement must not be null");
|
||||
Assert.notNull(resultSetExtractor, "AsyncResultSetExtractor must not be null");
|
||||
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Executing statement [%s]", toCql(statement)));
|
||||
}
|
||||
|
||||
CompletionStage<T> results = getCurrentSession() //
|
||||
.executeAsync(applyStatementSettings(statement)) //
|
||||
.thenApply(resultSetExtractor::extractData) //
|
||||
.thenCompose(ListenableFuture::completable);
|
||||
|
||||
return new CassandraFutureAdapter<>(results, ex -> translateExceptionIfPossible("Query", toCql(statement), ex));
|
||||
} catch (DriverException e) {
|
||||
throw translateException("Query", toCql(statement), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(Statement<?> statement, RowCallbackHandler rowCallbackHandler)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> result = query(statement, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(result, o -> null),
|
||||
getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(Statement<?> statement, RowMapper<T> rowMapper)
|
||||
throws DataAccessException {
|
||||
return query(statement, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<Map<String, Object>>> queryForList(Statement<?> statement) throws DataAccessException {
|
||||
return query(statement, newAsyncResultSetExtractor(newColumnMapRowMapper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> queryForList(Statement<?> statement, Class<T> elementType)
|
||||
throws DataAccessException {
|
||||
return query(statement, newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Map<String, Object>> queryForMap(Statement<?> statement) throws DataAccessException {
|
||||
return queryForObject(statement, newColumnMapRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(Statement<?> statement, Class<T> requiredType)
|
||||
throws DataAccessException {
|
||||
return queryForObject(statement, newSingleColumnRowMapper(requiredType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(Statement<?> statement, RowMapper<T> rowMapper)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<List<T>> results = query(statement, newAsyncResultSetExtractor(rowMapper));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(
|
||||
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> queryForResultSet(Statement<?> statement) throws DataAccessException {
|
||||
return query(statement, AsyncCqlTemplate::toResultSet);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.driver.core.PreparedStatement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(AsyncPreparedStatementCreator preparedStatementCreator)
|
||||
throws DataAccessException {
|
||||
|
||||
return new MappingListenableFutureAdapter<>(query(preparedStatementCreator, AsyncCqlTemplate::toResultSet),
|
||||
AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(String cql, Object... args) throws DataAccessException {
|
||||
return execute(cql, newPreparedStatementBinder(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Boolean> execute(String cql, @Nullable PreparedStatementBinder psb)
|
||||
throws DataAccessException {
|
||||
|
||||
return new MappingListenableFutureAdapter<>(
|
||||
query(newAsyncPreparedStatementCreator(cql), psb, AsyncCqlTemplate::toResultSet), AsyncResultSet::wasApplied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException {
|
||||
return execute(newAsyncPreparedStatementCreator(cql), action);
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("PreparedStatementCallback",
|
||||
toCql(preparedStatementCreator), ex);
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Preparing statement [%s] using %s", toCql(preparedStatementCreator),
|
||||
preparedStatementCreator));
|
||||
}
|
||||
|
||||
CqlSession currentSession = getCurrentSession();
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(
|
||||
preparedStatementCreator.createPreparedStatement(currentSession), preparedStatement -> {
|
||||
try {
|
||||
return action.doInPreparedStatement(currentSession, preparedStatement);
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
}
|
||||
}), getExceptionTranslator());
|
||||
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
|
||||
|
||||
return query(preparedStatementCreator, null, resultSetExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(preparedStatementCreator, null, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
RowMapper<T> rowMapper) throws DataAccessException {
|
||||
|
||||
return query(preparedStatementCreator, null, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
|
||||
|
||||
Assert.notNull(preparedStatementCreator, "AsyncPreparedStatementCreator must not be null");
|
||||
Assert.notNull(resultSetExtractor, "AsyncResultSetExtractor object must not be null");
|
||||
|
||||
PersistenceExceptionTranslator exceptionTranslator = ex -> translateExceptionIfPossible("Query",
|
||||
toCql(preparedStatementCreator), ex);
|
||||
|
||||
try {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Preparing statement [%s] using %s", toCql(preparedStatementCreator),
|
||||
preparedStatementCreator));
|
||||
}
|
||||
|
||||
CqlSession session = getCurrentSession();
|
||||
|
||||
ListenableFuture<Statement<?>> statementFuture = new MappingListenableFutureAdapter<>(
|
||||
preparedStatementCreator.createPreparedStatement(session), preparedStatement -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Executing prepared statement [%s]", toCql(preparedStatement)));
|
||||
}
|
||||
|
||||
return applyStatementSettings(psb != null ? psb.bindValues(preparedStatement) : preparedStatement.bind());
|
||||
});
|
||||
|
||||
CompletableFuture<T> result = statementFuture.completable() //
|
||||
.thenCompose(session::executeAsync) //
|
||||
.thenApply(resultSetExtractor::extractData) //
|
||||
.thenCompose(ListenableFuture::completable);
|
||||
|
||||
return new CassandraFutureAdapter<>(result, exceptionTranslator);
|
||||
} catch (DriverException e) {
|
||||
throw translateException(exceptionTranslator, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(AsyncPreparedStatementCreator preparedStatementCreator,
|
||||
@Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(preparedStatementCreator, psb, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(String cql, AsyncResultSetExtractor<T> resultSetExtractor, Object... args)
|
||||
throws DataAccessException {
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args), resultSetExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(String cql, RowCallbackHandler rowCallbackHandler, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
throws DataAccessException {
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
AsyncResultSetExtractor<T> resultSetExtractor) throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), psb, resultSetExtractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Void> query(String cql, @Nullable PreparedStatementBinder psb,
|
||||
RowCallbackHandler rowCallbackHandler) throws DataAccessException {
|
||||
|
||||
ListenableFuture<?> results = query(newAsyncPreparedStatementCreator(cql), psb,
|
||||
newAsyncResultSetExtractor(rowCallbackHandler));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(new MappingListenableFutureAdapter<>(results, o -> null),
|
||||
getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> query(String cql, @Nullable PreparedStatementBinder psb, RowMapper<T> rowMapper)
|
||||
throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), psb, newAsyncResultSetExtractor(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<Map<String, Object>>> queryForList(String cql, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(newColumnMapRowMapper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<List<T>> queryForList(String cql, Class<T> elementType, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
return query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(newSingleColumnRowMapper(elementType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException {
|
||||
return queryForObject(cql, newColumnMapRowMapper(), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, Class<T> requiredType, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
return queryForObject(cql, newSingleColumnRowMapper(requiredType), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> queryForObject(String cql, RowMapper<T> rowMapper, Object... args)
|
||||
throws DataAccessException {
|
||||
|
||||
ListenableFuture<List<T>> results = query(newAsyncPreparedStatementCreator(cql), newPreparedStatementBinder(args),
|
||||
newAsyncResultSetExtractor(rowMapper));
|
||||
|
||||
return new ExceptionTranslatingListenableFutureAdapter<>(
|
||||
new MappingListenableFutureAdapter<>(results, DataAccessUtils::requiredSingleResult), getExceptionTranslator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<AsyncResultSet> queryForResultSet(String cql, Object... args) throws DataAccessException {
|
||||
return query(cql, AsyncCqlTemplate::toResultSet, 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 {@literal null})
|
||||
* @param ex the offending {@code RuntimeException}.
|
||||
* @return the exception translation {@link Function}
|
||||
* @see CqlProvider
|
||||
*/
|
||||
protected DataAccessException translateException(String task, @Nullable 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 {@literal null})
|
||||
* @param ex the offending {@code RuntimeException}.
|
||||
* @return the translated {@link DataAccessException} or {@literal null} if translation not possible.
|
||||
* @see CqlProvider
|
||||
*/
|
||||
@Nullable
|
||||
protected DataAccessException translateExceptionIfPossible(String task, @Nullable String cql, RuntimeException ex) {
|
||||
return translate(task, cql, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CQL-based {@link 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(
|
||||
(SimpleStatement) applyStatementSettings(SimpleStatement.newInstance(cql)),
|
||||
ex -> translateExceptionIfPossible("PrepareStatement", cql, ex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link ResultSetExtractor} adapting the given {@link RowCallbackHandler}.
|
||||
*
|
||||
* @param rowCallbackHandler {@link RowCallbackHandler} to adapt as a {@link ResultSetExtractor}.
|
||||
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowCallbackHandler}.
|
||||
* @see AsyncRowCallbackHandlerResultSetExtractor
|
||||
* @see ResultSetExtractor
|
||||
* @see RowCallbackHandler
|
||||
*/
|
||||
protected AsyncRowCallbackHandlerResultSetExtractor newAsyncResultSetExtractor(
|
||||
RowCallbackHandler rowCallbackHandler) {
|
||||
return new AsyncRowCallbackHandlerResultSetExtractor(rowCallbackHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the {@link ResultSetExtractor} adapting the given {@link RowMapper}.
|
||||
*
|
||||
* @param rowMapper {@link RowMapper} to adapt as a {@link ResultSetExtractor}.
|
||||
* @return a {@link ResultSetExtractor} implementation adapting an instance of the {@link RowMapper}.
|
||||
* @see ResultSetExtractor
|
||||
* @see RowMapper
|
||||
* @see RowMapperResultSetExtractor
|
||||
*/
|
||||
protected <T> AsyncRowMapperResultSetExtractor<T> newAsyncResultSetExtractor(RowMapper<T> rowMapper) {
|
||||
return new AsyncRowMapperResultSetExtractor<>(rowMapper);
|
||||
}
|
||||
|
||||
private CqlSession getCurrentSession() {
|
||||
|
||||
SessionFactory sessionFactory = getSessionFactory();
|
||||
|
||||
Assert.state(sessionFactory != null, "SessionFactory is null");
|
||||
|
||||
return sessionFactory.getSession();
|
||||
}
|
||||
|
||||
private static ListenableFuture<AsyncResultSet> toResultSet(AsyncResultSet resultSet) {
|
||||
|
||||
SettableListenableFuture<AsyncResultSet> future = new SettableListenableFuture<>();
|
||||
future.set(resultSet);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
private static RuntimeException translateException(PersistenceExceptionTranslator exceptionTranslator,
|
||||
DriverException e) {
|
||||
|
||||
DataAccessException translated = exceptionTranslator.translateExceptionIfPossible(e);
|
||||
return translated == null ? e : translated;
|
||||
}
|
||||
|
||||
private static class SimpleAsyncPreparedStatementCreator implements AsyncPreparedStatementCreator, CqlProvider {
|
||||
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
|
||||
private final SimpleStatement statement;
|
||||
|
||||
private SimpleAsyncPreparedStatementCreator(SimpleStatement statement,
|
||||
PersistenceExceptionTranslator exceptionTranslator) {
|
||||
|
||||
this.statement = statement;
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException {
|
||||
return new CassandraFutureAdapter<>(session.prepareAsync(this.statement), exceptionTranslator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCql() {
|
||||
return this.statement.getQuery();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter to enable use of a {@link RowCallbackHandler} inside a {@link ResultSetExtractor}.
|
||||
*/
|
||||
protected static class AsyncRowCallbackHandlerResultSetExtractor implements AsyncResultSetExtractor<Void> {
|
||||
|
||||
private final RowCallbackHandler rowCallbackHandler;
|
||||
|
||||
protected AsyncRowCallbackHandlerResultSetExtractor(RowCallbackHandler rowCallbackHandler) {
|
||||
this.rowCallbackHandler = rowCallbackHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ListenableFuture<Void> extractData(AsyncResultSet resultSet) {
|
||||
return AsyncResultStream.from(resultSet).forEach(rowCallbackHandler::processRow);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.AsyncCqlTemplate;
|
||||
import org.springframework.data.cassandra.core.cql.CqlProvider;
|
||||
import org.springframework.data.cassandra.core.cql.CqlTemplate;
|
||||
import org.springframework.data.cassandra.core.cql.PreparedStatementCallback;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
|
||||
/**
|
||||
* One of the two central callback interfaces used by the
|
||||
* {@link org.springframework.data.cassandra.core.cql.legacy.AsyncCqlTemplate} class. This interface prepares a CQL
|
||||
* statement returning a {@link ListenableFuture} given a {@link CqlSession}, provided by the {@link CqlTemplate} class.
|
||||
* <p>
|
||||
* 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 org.springframework.data.cassandra.core.cql.AsyncCqlTemplate} class will catch and handle
|
||||
* {@link DriverException}s appropriately.
|
||||
* <p>
|
||||
* Classes implementing this interface 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 4.0
|
||||
* @see org.springframework.data.cassandra.core.cql.legacy.AsyncCqlTemplate#execute(AsyncPreparedStatementCreator,
|
||||
* PreparedStatementCallback)
|
||||
* @see CqlProvider
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
@FunctionalInterface
|
||||
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 AsyncCqlTemplate} class will handle them.
|
||||
*/
|
||||
ListenableFuture<PreparedStatement> createPreparedStatement(CqlSession session) throws DriverException;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.cassandra.core.cql.AsyncCqlTemplate;
|
||||
import org.springframework.data.cassandra.core.cql.RowCallbackHandler;
|
||||
import org.springframework.data.cassandra.core.cql.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
|
||||
/**
|
||||
* Callback interface used by {@link org.springframework.data.cassandra.core.cql.legacy.AsyncCqlTemplate}'s query
|
||||
* methods. Implementations of this interface perform the actual work of extracting results from a
|
||||
* {@link AsyncResultSet}, but don't need to worry about exception handling. {@link DriverException}s will be caught and
|
||||
* handled by the calling {@link org.springframework.data.cassandra.core.cql.legacy.AsyncCqlTemplate}.
|
||||
* <p>
|
||||
* This interface is mainly used within the CQL framework itself. A {@link RowMapper} is usually a simpler choice for
|
||||
* {@link AsyncResultSet} processing, mapping one result object per row instead of one result object for the entire
|
||||
* {@link AsyncResultSet}.
|
||||
* <p>
|
||||
* Note: In contrast to a {@link RowCallbackHandler}, a {@link AsyncResultSetExtractor} object is typically stateless
|
||||
* and thus reusable, as long as it doesn't access stateful resources or keep result state within the object.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
* @see AsyncCqlTemplate
|
||||
* @see RowMapper
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
@FunctionalInterface
|
||||
public interface AsyncResultSetExtractor<T> {
|
||||
|
||||
/**
|
||||
* Implementations must implement this method to process the entire {@link AsyncResultSet}.
|
||||
*
|
||||
* @param resultSet {@link AsyncResultSet} to extract data from.
|
||||
* @return an arbitrary result object, or {@literal null} if none (the extractor will typically be stateful in the
|
||||
* latter case).
|
||||
* @throws DriverException if a {@link DriverException} is encountered getting column values or navigating (that is,
|
||||
* there's no need to catch {@link DriverException})
|
||||
* @throws DataAccessException in case of custom exceptions
|
||||
*/
|
||||
@Nullable
|
||||
ListenableFuture<T> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2019-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collector;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.RowMapper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
/**
|
||||
* Asynchronous supplied sequence of elements supporting sequential operations over a {@link AsyncResultSet a result
|
||||
* set}. An asynchronous stream represents a pipeline of operations to process a {@link AsyncResultSet}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class AsyncResultStream<T> {
|
||||
|
||||
private final AsyncResultSet resultSet;
|
||||
|
||||
private final RowMapper<T> mapper;
|
||||
|
||||
private AsyncResultStream(AsyncResultSet resultSet, RowMapper<T> mapper) {
|
||||
this.resultSet = resultSet;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link AsyncResultStream} given {@link AsyncResultSet}.
|
||||
*
|
||||
* @param resultSet the result set to process.
|
||||
* @return a new {@link AsyncResultStream} instance.
|
||||
*/
|
||||
static AsyncResultStream<Row> from(AsyncResultSet resultSet) {
|
||||
|
||||
Assert.notNull(resultSet, "AsyncResultSet must not be null");
|
||||
|
||||
return new AsyncResultStream<>(resultSet, (row, rowNum) -> row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream consisting of the results of applying the given function to the elements of this stream.
|
||||
* <p>
|
||||
* This is an intermediate operation.
|
||||
*
|
||||
* @param <R> The element type of the new stream
|
||||
* @param mapper a non-interfering, stateless {@link RowMapper}.
|
||||
*/
|
||||
<R> AsyncResultStream<R> map(RowMapper<R> mapper) {
|
||||
|
||||
Assert.notNull(mapper, "RowMapper must not be null");
|
||||
|
||||
return new AsyncResultStream<>(resultSet, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a mutable reduction operation on the elements of this stream using a {@link Collector} resulting in a
|
||||
* {@link ListenableFuture}.
|
||||
* <p>
|
||||
* This is a terminal operation.
|
||||
*
|
||||
* @param <R> the type of the result
|
||||
* @param <A> the intermediate accumulation type of the {@link Collector}
|
||||
* @param collector the {@link Collector} describing the reduction
|
||||
* @return the result of the reduction
|
||||
*/
|
||||
<R, A> ListenableFuture<R> collect(Collector<? super T, A, R> collector) {
|
||||
|
||||
Assert.notNull(collector, "Collector must not be null");
|
||||
|
||||
SettableListenableFuture<R> future = new SettableListenableFuture<>();
|
||||
CollectState<A, R> collectState = new CollectState<>(collector);
|
||||
|
||||
collectState.collectAsync(future, this.resultSet);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an action for each element of this stream. This method returns a {@link ListenableFuture} that completes
|
||||
* without a value ({@code null}) once all elements have been processed.
|
||||
* <p>
|
||||
* This is a terminal operation.
|
||||
* <p>
|
||||
* If the action accesses shared state, it is responsible for providing the required synchronization.
|
||||
*
|
||||
* @param action a non-interfering action to perform on the elements.
|
||||
*/
|
||||
ListenableFuture<Void> forEach(Consumer<T> action) {
|
||||
|
||||
Assert.notNull(action, "Action must not be null");
|
||||
|
||||
SettableListenableFuture<Void> future = new SettableListenableFuture<>();
|
||||
ForwardLoopState loopState = new ForwardLoopState(action);
|
||||
|
||||
loopState.forEachAsync(future, this.resultSet);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* State object for forward-looping using {@code forEach}.
|
||||
*/
|
||||
class ForwardLoopState {
|
||||
|
||||
private final AtomicInteger rowNumber = new AtomicInteger();
|
||||
private final Consumer<T> consumer;
|
||||
|
||||
ForwardLoopState(Consumer<T> consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
void peekRow(Iterable<Row> rows) {
|
||||
rows.forEach(row -> consumer.accept(mapper.mapRow(row, rowNumber.incrementAndGet())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive async iteration.
|
||||
*
|
||||
* @param target
|
||||
* @param resultSet
|
||||
*/
|
||||
void forEachAsync(SettableListenableFuture<Void> target, AsyncResultSet resultSet) {
|
||||
|
||||
if (target.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
peekRow(resultSet.currentPage());
|
||||
} catch (RuntimeException e) {
|
||||
target.setException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resultSet.hasMorePages()) {
|
||||
target.set(null);
|
||||
} else {
|
||||
|
||||
CompletionStage<AsyncResultSet> nextPage = resultSet.fetchNextPage();
|
||||
|
||||
nextPage.whenComplete((nextResultSet, throwable) -> {
|
||||
|
||||
if (throwable != null) {
|
||||
target.setException(throwable);
|
||||
} else {
|
||||
forEachAsync(target, nextResultSet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State object for collecting rows using {@code collect}.
|
||||
*/
|
||||
class CollectState<A, R> {
|
||||
|
||||
private final AtomicInteger rowNumber = new AtomicInteger();
|
||||
private volatile A intermediate;
|
||||
private final Collector<? super T, A, R> collector;
|
||||
|
||||
CollectState(Collector<? super T, A, R> collector) {
|
||||
this.collector = collector;
|
||||
this.intermediate = collector.supplier().get();
|
||||
}
|
||||
|
||||
void collectPage(Iterable<Row> rows) {
|
||||
|
||||
for (Row row : rows) {
|
||||
collector.accumulator().accept(intermediate, mapper.mapRow(row, rowNumber.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
R finish() {
|
||||
return collector.finisher().apply(intermediate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive collection.
|
||||
*
|
||||
* @param target
|
||||
* @param resultSet
|
||||
*/
|
||||
void collectAsync(SettableListenableFuture<R> target, AsyncResultSet resultSet) {
|
||||
|
||||
if (target.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
collectPage(resultSet.currentPage());
|
||||
} catch (RuntimeException e) {
|
||||
target.setException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resultSet.hasMorePages()) {
|
||||
target.set(finish());
|
||||
} else {
|
||||
|
||||
CompletionStage<AsyncResultSet> nextPage = resultSet.fetchNextPage();
|
||||
|
||||
nextPage.whenComplete((nextResultSet, throwable) -> {
|
||||
|
||||
if (throwable != null) {
|
||||
target.setException(throwable);
|
||||
} else {
|
||||
collectAsync(target, nextResultSet);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.cassandra.core.cql.AsyncCqlTemplate;
|
||||
import org.springframework.data.cassandra.core.cql.ResultSetExtractor;
|
||||
import org.springframework.data.cassandra.core.cql.RowMapper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
|
||||
/**
|
||||
* Adapter implementation of the {@link ResultSetExtractor} interface that delegates to a {@link RowMapper} which is
|
||||
* supposed to create an object for each row. Each object is added to the results List of this
|
||||
* {@link ResultSetExtractor}.
|
||||
* <p>
|
||||
* Useful for the typical case of one object per row in the database table. The number of entries in the results will
|
||||
* match the number of rows.
|
||||
* <p>
|
||||
* Note that a {@link RowMapper} object is typically stateless and thus reusable.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
* @see RowMapper
|
||||
* @see AsyncCqlTemplate
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
public class AsyncRowMapperResultSetExtractor<T> implements AsyncResultSetExtractor<List<T>> {
|
||||
|
||||
private final RowMapper<T> rowMapper;
|
||||
|
||||
/**
|
||||
* Create a new {@link AsyncRowMapperResultSetExtractor}.
|
||||
*
|
||||
* @param rowMapper the {@link RowMapper} which creates an object for each row, must not be {@literal null}.
|
||||
*/
|
||||
public AsyncRowMapperResultSetExtractor(RowMapper<T> rowMapper) {
|
||||
|
||||
Assert.notNull(rowMapper, "RowMapper is must not be null");
|
||||
|
||||
this.rowMapper = rowMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<List<T>> extractData(AsyncResultSet resultSet) throws DriverException, DataAccessException {
|
||||
return AsyncResultStream.from(resultSet).map(rowMapper).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.DriverException;
|
||||
|
||||
/**
|
||||
* Generic callback interface for code that operates asynchronously on a Cassandra {@link CqlSession}. Allows to execute
|
||||
* any number of operations on a single session, using any type and number of statements.
|
||||
* <p>
|
||||
* This is particularly useful for delegating to existing data access code that expects a {@link CqlSession} to work on
|
||||
* and throws {@link DriverException}. For newly written code, it is strongly recommended to use
|
||||
* {@link AsyncCqlTemplate}'s more specific operations, for example a {@code query} or {@code update} variant.
|
||||
*
|
||||
* @author David Webb
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
* @see AsyncCqlTemplate#execute(AsyncSessionCallback)
|
||||
* @see AsyncCqlTemplate#query
|
||||
* @see AsyncCqlTemplate#execute(String)
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
@FunctionalInterface
|
||||
public interface AsyncSessionCallback<T> {
|
||||
|
||||
/**
|
||||
* Gets called by {@link AsyncCqlTemplate#execute} with an active Cassandra {@link CqlSession}. Does not need to care
|
||||
* about activating or closing the {@link CqlSession}.
|
||||
* <p>
|
||||
* 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 AsyncCqlTemplate#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 ListenableFuture<Void>} 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<T> doInSession(CqlSession session) throws DriverException, DataAccessException;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.FailureCallback;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureCallback;
|
||||
import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
import org.springframework.util.concurrent.SuccessCallback;
|
||||
|
||||
/**
|
||||
* Adapter class to {@link ListenableFuture} {@link ExecutionException} by applying a
|
||||
* {@link PersistenceExceptionTranslator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class ExceptionTranslatingListenableFutureAdapter<T> implements ListenableFuture<T> {
|
||||
|
||||
private final ListenableFuture<T> adaptee;
|
||||
|
||||
private final ListenableFuture<T> future;
|
||||
|
||||
/**
|
||||
* Create a new {@link ExceptionTranslatingListenableFutureAdapter} given a {@link ListenableFuture} and a
|
||||
* {@link PersistenceExceptionTranslator}.
|
||||
*
|
||||
* @param adaptee must not be {@literal null}.
|
||||
* @param persistenceExceptionTranslator must not be {@literal null}.
|
||||
*/
|
||||
ExceptionTranslatingListenableFutureAdapter(ListenableFuture<T> adaptee,
|
||||
PersistenceExceptionTranslator persistenceExceptionTranslator) {
|
||||
|
||||
Assert.notNull(adaptee, "ListenableFuture must not be null");
|
||||
Assert.notNull(persistenceExceptionTranslator, "PersistenceExceptionTranslator must not be null");
|
||||
|
||||
this.adaptee = adaptee;
|
||||
this.future = adaptListenableFuture(adaptee, persistenceExceptionTranslator);
|
||||
}
|
||||
|
||||
private static <T> ListenableFuture<T> adaptListenableFuture(ListenableFuture<T> listenableFuture,
|
||||
PersistenceExceptionTranslator exceptionTranslator) {
|
||||
|
||||
SettableListenableFuture<T> settableFuture = new SettableListenableFuture<>();
|
||||
|
||||
listenableFuture.addCallback(new ListenableFutureCallback<T>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(@Nullable T result) {
|
||||
settableFuture.set(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable ex) {
|
||||
if (ex instanceof RuntimeException) {
|
||||
DataAccessException dataAccessException = exceptionTranslator
|
||||
.translateExceptionIfPossible((RuntimeException) ex);
|
||||
|
||||
if (dataAccessException != null) {
|
||||
settableFuture.setException(dataAccessException);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
settableFuture.setException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
return settableFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallback(ListenableFutureCallback<? super T> callback) {
|
||||
future.addCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
|
||||
future.addCallback(successCallback, failureCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return adaptee.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return adaptee.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
return future.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return future.get(timeout, unit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* CQL legacy core asynchronous support for easier migration.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.cassandra.core.cql.legacy;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -61,8 +61,8 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator
|
||||
|
||||
/**
|
||||
* Create a new {@link CachedPreparedStatementCreator} given {@link PreparedStatementCache} and
|
||||
* {@link SimpleStatement} to prepare. Subsequent calls require the a {@link SimpleStatement} object with the same CQL
|
||||
* test for a cache hit. Otherwise, the statement is likely to be re-prepared.
|
||||
* {@link SimpleStatement} to prepare. Subsequent calls require the given {@link SimpleStatement} object with the same
|
||||
* CQL test for a cache hit. Otherwise, the statement is likely to be re-prepared.
|
||||
*
|
||||
* @param cache must not be {@literal null}.
|
||||
* @param statement must not be {@literal null}.
|
||||
@@ -78,7 +78,7 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator
|
||||
|
||||
/**
|
||||
* Create a new {@link CachedPreparedStatementCreator} given {@link PreparedStatementCache} and {@code cql} to
|
||||
* prepare. Subsequent calls require the a CQL statement that {@link String#equals(Object) are equal} to the
|
||||
* prepare. Subsequent calls require the given CQL statement that {@link String#equals(Object) are equal} to the
|
||||
* previously used CQL string for a cache hit. Otherwise, the statement is likely to be re-prepared.
|
||||
*
|
||||
* @param cache must not be {@literal null}.
|
||||
@@ -96,8 +96,8 @@ public class CachedPreparedStatementCreator implements PreparedStatementCreator
|
||||
/**
|
||||
* Create a new {@link CachedPreparedStatementCreator} given {@link PreparedStatementCache} and {@code cql} to
|
||||
* prepare. This method applies {@link QueryOptions} to the {@link SimpleStatement} before preparing it. Subsequent
|
||||
* calls require the a CQL statement that {@link String#equals(Object) are equal} to the previously used CQL string
|
||||
* for a cache hit. Otherwise, the statement is likely to be re-prepared.
|
||||
* calls require the given CQL statement that {@link String#equals(Object) are equal} to the previously used CQL
|
||||
* string for a cache hit. Otherwise, the statement is likely to be re-prepared.
|
||||
*
|
||||
* @param cache must not be {@literal null}.
|
||||
* @param cql must not be {@literal null} or empty.
|
||||
|
||||
@@ -35,7 +35,9 @@ import org.springframework.util.concurrent.SuccessCallback;
|
||||
*
|
||||
* @since 3.0
|
||||
* @param <T> the result type returned by this Future's {@code get} method
|
||||
* @deprecated since 3.0, use {@link CompletableFuture} directly.
|
||||
*/
|
||||
@Deprecated
|
||||
public class CassandraFutureAdapter<T> implements ListenableFuture<T> {
|
||||
|
||||
private final CompletableFuture<T> completableFuture;
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.cassandra.core.DeleteOptions;
|
||||
import org.springframework.data.cassandra.core.EntityWriteResult;
|
||||
import org.springframework.data.cassandra.core.InsertOptions;
|
||||
import org.springframework.data.cassandra.core.UpdateOptions;
|
||||
import org.springframework.data.cassandra.core.WriteResult;
|
||||
import org.springframework.data.cassandra.core.convert.CassandraConverter;
|
||||
import org.springframework.data.cassandra.core.cql.QueryOptions;
|
||||
import org.springframework.data.cassandra.core.cql.WriteOptions;
|
||||
import org.springframework.data.cassandra.core.cql.legacy.AsyncCqlOperations;
|
||||
import org.springframework.data.cassandra.core.query.CassandraPageRequest;
|
||||
import org.springframework.data.cassandra.core.query.Query;
|
||||
import org.springframework.data.cassandra.core.query.Update;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
|
||||
/**
|
||||
* Interface specifying a basic set of asynchronous Cassandra operations. Implemented by {@link AsyncCassandraTemplate}.
|
||||
* Not often used directly, but a useful option to enhance testability, as it can easily be mocked or stubbed.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
* @since 4.0
|
||||
* @see AsyncCassandraTemplate
|
||||
* @see AsyncCqlOperations
|
||||
* @see Statement
|
||||
* @see InsertOptions
|
||||
* @see UpdateOptions
|
||||
* @deprecated since 4.0, use the {@link java.util.concurrent.CompletableFuture}-based variant
|
||||
* {@link org.springframework.data.cassandra.core.AsyncCassandraTemplate}.
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
public interface AsyncCassandraOperations {
|
||||
|
||||
/**
|
||||
* Expose the underlying {@link AsyncCqlOperations} to allow asynchronous CQL operations.
|
||||
*
|
||||
* @return the underlying {@link AsyncCqlOperations}.
|
||||
* @see AsyncCqlOperations
|
||||
*/
|
||||
AsyncCqlOperations getAsyncCqlOperations();
|
||||
|
||||
/**
|
||||
* Returns the underlying {@link CassandraConverter}.
|
||||
*
|
||||
* @return the underlying {@link CassandraConverter}.
|
||||
*/
|
||||
CassandraConverter getConverter();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with static CQL
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items to a {@link List} of entities.
|
||||
*
|
||||
* @param cql must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> select(String cql, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
|
||||
*
|
||||
* @param cql must not be {@literal null}.
|
||||
* @param entityConsumer object that will be notified on each entity, one object at a time, must not be
|
||||
* {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the completion handle
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<Void> select(String cql, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting item to an entity.
|
||||
*
|
||||
* @param cql must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOne(String cql, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute the given Cassandra {@link Statement}. Any errors that result from executing this command will be converted
|
||||
* into Spring's DAO exception hierarchy.
|
||||
*
|
||||
* @param statement a Cassandra {@link Statement}, must not be {@literal null}.
|
||||
* @return the {@link AsyncResultSet}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @since 3.2
|
||||
*/
|
||||
ListenableFuture<AsyncResultSet> execute(Statement<?> statement) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items to a {@link List} of entities.
|
||||
*
|
||||
* @param statement must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> select(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities. A sliced
|
||||
* query translates the effective {@link Statement#getFetchSize() fetch size} to the page size.
|
||||
*
|
||||
* @param statement the CQL statement, must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see CassandraPageRequest
|
||||
*/
|
||||
<T> ListenableFuture<Slice<T>> slice(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
|
||||
*
|
||||
* @param statement must not be {@literal null}.
|
||||
* @param entityConsumer object that will be notified on each entity, one object at a time, must not be
|
||||
* {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the completion handle
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<Void> select(Statement<?> statement, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting item to an entity.
|
||||
*
|
||||
* @param statement must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOne(Statement<?> statement, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with org.springframework.data.cassandra.core.query.Query
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items to a {@link List} of entities.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<List<T>> select(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query with paging and convert the result set to a {@link Slice} of entities.
|
||||
*
|
||||
* @param query the query object used to create a CQL statement, must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted results
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see CassandraPageRequest
|
||||
*/
|
||||
<T> ListenableFuture<Slice<T>> slice(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting items notifying {@link Consumer} for each entity.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityConsumer object that will be notified on each entity, one object at a time, must not be
|
||||
* {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the completion handle
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<Void> select(Query query, Consumer<T> entityConsumer, Class<T> entityClass)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code SELECT} query and convert the resulting item to an entity.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOne(Query query, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the queried entities and return {@literal true} if the update was applied.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param update must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> update(Query query, Update update, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Remove entities (rows)/columns from the table by {@link Query}.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return {@literal true} if the deletion was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> delete(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with entities
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the number of rows for the given entity class.
|
||||
*
|
||||
* @param entityClass {@link Class type} of the entity; must not be {@literal null}.
|
||||
* @return the number of existing entities.
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
*/
|
||||
ListenableFuture<Long> count(Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Returns the number of rows for the given entity class applying {@link Query}. This overridden method allows users
|
||||
* to further refine the selection criteria using a {@link Query} predicate to determine how many entities of the
|
||||
* given {@link Class type} match the criteria.
|
||||
*
|
||||
* @param query user-provided count {@link Query} to execute; must not be {@literal null}.
|
||||
* @param entityClass {@link Class type} of the entity; must not be {@literal null}.
|
||||
* @return the number of existing entities.
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
* @since 2.1
|
||||
*/
|
||||
ListenableFuture<Long> count(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Determine whether a row of {@code entityClass} with the given {@code id} exists.
|
||||
*
|
||||
* @param id Id value. For single primary keys it's the plain value. For composite primary keys either, it's an
|
||||
* instance of either {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or
|
||||
* {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}.
|
||||
* @param entityClass {@link Class type} of the entity; must not be {@literal null}.
|
||||
* @return {@literal true} if the object exists.
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> exists(Object id, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Determine whether the result for {@code entityClass} {@link Query} yields at least one row.
|
||||
*
|
||||
* @param query user-provided exists {@link Query} to execute; must not be {@literal null}.
|
||||
* @param entityClass {@link Class type} of the entity; must not be {@literal null}.
|
||||
* @return {@literal true} if the object exists.
|
||||
* @throws DataAccessException if any problem occurs while executing the query.
|
||||
* @since 2.1
|
||||
*/
|
||||
ListenableFuture<Boolean> exists(Query query, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute the Select by {@code id} for the given {@code entityClass}.
|
||||
*
|
||||
* @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the
|
||||
* {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or
|
||||
* {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return the converted object or {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> selectOneById(Object id, Class<T> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Insert the given entity and return the entity if the insert was applied.
|
||||
*
|
||||
* @param entity The entity to insert, must not be {@literal null}.
|
||||
* @return the inserted entity.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> insert(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Insert the given entity applying {@link WriteOptions} and return the entity if the insert was applied.
|
||||
*
|
||||
* @param entity The entity to insert, must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
* @return the {@link EntityWriteResult} for this operation.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see InsertOptions#empty()
|
||||
*/
|
||||
<T> ListenableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity and return the entity if the update was applied.
|
||||
*
|
||||
* @param entity The entity to update, must not be {@literal null}.
|
||||
* @return the updated entity.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> update(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Update the given entity applying {@link WriteOptions} and return the entity if the update was applied.
|
||||
*
|
||||
* @param entity The entity to update, must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
* @return the {@link EntityWriteResult} for this operation.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see UpdateOptions#empty()
|
||||
*/
|
||||
<T> ListenableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return the deleted entity.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
<T> ListenableFuture<T> delete(T entity) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link QueryOptions} and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
* @return the {@link WriteResult} for this operation.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see QueryOptions#empty()
|
||||
*/
|
||||
ListenableFuture<WriteResult> delete(Object entity, QueryOptions options) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Delete the given entity applying {@link DeleteOptions} and return the entity if the delete statement was applied.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param options must not be {@literal null}.
|
||||
* @return the {@link WriteResult} for this operation.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
* @see DeleteOptions#empty()
|
||||
* @since 2.2
|
||||
*/
|
||||
default ListenableFuture<WriteResult> delete(Object entity, DeleteOptions options) throws DataAccessException {
|
||||
return delete(entity, (QueryOptions) options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given object from the table by id.
|
||||
*
|
||||
* @param id the Id value. For single primary keys it's the plain value. For composite primary keys either the
|
||||
* {@link org.springframework.data.cassandra.core.mapping.PrimaryKeyClass} or
|
||||
* {@link org.springframework.data.cassandra.core.mapping.MapId}. Must not be {@literal null}.
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @return {@literal true} if the deletion was applied.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Boolean> deleteById(Object id, Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Execute a {@code TRUNCATE} query to remove all entities of a given class.
|
||||
*
|
||||
* @param entityClass The entity type must not be {@literal null}.
|
||||
* @throws DataAccessException if there is any problem executing the query.
|
||||
*/
|
||||
ListenableFuture<Void> truncate(Class<?> entityClass) throws DataAccessException;
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Delegate class to encapsulate lifecycle event configuration and publishing. Event creation is deferred within an
|
||||
* event {@link Supplier} to delay the actual event object creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
* @see ApplicationEventPublisher
|
||||
*/
|
||||
@Deprecated
|
||||
class EntityLifecycleEventDelegate {
|
||||
|
||||
private @Nullable ApplicationEventPublisher publisher;
|
||||
private boolean eventsEnabled = true;
|
||||
|
||||
public void setPublisher(@Nullable ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
public boolean isEventsEnabled() {
|
||||
return eventsEnabled;
|
||||
}
|
||||
|
||||
public void setEventsEnabled(boolean eventsEnabled) {
|
||||
this.eventsEnabled = eventsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an application event if event publishing is enabled.
|
||||
*
|
||||
* @param eventSupplier the supplier for application events.
|
||||
*/
|
||||
public void publishEvent(Supplier<?> eventSupplier) {
|
||||
|
||||
if (canPublishEvent()) {
|
||||
publisher.publishEvent(eventSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canPublishEvent() {
|
||||
return publisher != null && eventsEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright 2019-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.cassandra.core.CassandraTemplate;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraTemplate;
|
||||
import org.springframework.data.cassandra.core.convert.CassandraConverter;
|
||||
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.projection.EntityProjection;
|
||||
import org.springframework.data.projection.EntityProjectionIntrospector;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.querybuilder.condition.Condition;
|
||||
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
|
||||
import com.datastax.oss.driver.api.querybuilder.update.Update;
|
||||
|
||||
/**
|
||||
* Common data access operations performed on an entity using a {@link MappingContext} containing mapping metadata.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
* @see CassandraTemplate
|
||||
* @see AsyncCassandraTemplate
|
||||
* @see ReactiveCassandraTemplate
|
||||
* @since 4.0
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class EntityOperations {
|
||||
|
||||
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
|
||||
private final EntityProjectionIntrospector introspector;
|
||||
|
||||
EntityOperations(CassandraConverter converter) {
|
||||
this(converter.getMappingContext(), converter.getCustomConversions(), converter.getProjectionFactory());
|
||||
}
|
||||
|
||||
EntityOperations(MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> context,
|
||||
CustomConversions conversions, ProjectionFactory projectionFactory) {
|
||||
this.mappingContext = context;
|
||||
this.introspector = EntityProjectionIntrospector.create(projectionFactory,
|
||||
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
|
||||
.and(((target, underlyingType) -> !conversions.isSimpleType(target))),
|
||||
context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Entity} for the given bean.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public <T> Entity<T> forEntity(T entity) {
|
||||
|
||||
Assert.notNull(entity, "Bean must not be null");
|
||||
|
||||
return MappedEntity.of(entity, getMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AdaptibleEntity} for the given bean and {@link ConversionService}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param conversionService must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public <T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
|
||||
|
||||
Assert.notNull(entity, "Bean must not be null");
|
||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||
|
||||
return AdaptibleMappedEntity.of(entity, getMappingContext(), conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MappingContext} used by this entity data access operations class to access mapping meta-data
|
||||
* used to store (map) object to Cassandra tables.
|
||||
*
|
||||
* @return the {@link MappingContext} used by this entity data access operations class.
|
||||
* @see org.springframework.data.cassandra.core.mapping.CassandraMappingContext
|
||||
*/
|
||||
CassandraPersistentEntity<?> getRequiredPersistentEntity(Class<?> entityClass) {
|
||||
return getMappingContext().getRequiredPersistentEntity(ClassUtils.getUserClass(entityClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table name to which the entity shall be persisted.
|
||||
*
|
||||
* @param entityClass entity class, must not be {@literal null}.
|
||||
* @return the table name to which the entity shall be persisted.
|
||||
*/
|
||||
CqlIdentifier getTableName(Class<?> entityClass) {
|
||||
return getRequiredPersistentEntity(entityClass).getTableName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Introspect the given {@link Class result type} in the context of the {@link Class entity type} whether the returned
|
||||
* type is a projection and what property paths are participating in the projection.
|
||||
*
|
||||
* @param resultType the type to project on. Must not be {@literal null}.
|
||||
* @param entityType the source domain type. Must not be {@literal null}.
|
||||
* @return the introspection result.
|
||||
* @since 3.4
|
||||
* @see EntityProjectionIntrospector#introspect(Class, Class)
|
||||
*/
|
||||
public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType, Class<D> entityType) {
|
||||
return introspector.introspect(resultType, entityType);
|
||||
}
|
||||
|
||||
protected MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> getMappingContext() {
|
||||
return this.mappingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of information about an entity.
|
||||
*/
|
||||
interface Entity<T> {
|
||||
|
||||
/**
|
||||
* Returns whether the entity is versioned, i.e. if it contains a version property.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default boolean isVersionedEntity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the version if the entity has a version property, {@literal null} otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
Object getVersion();
|
||||
|
||||
/**
|
||||
* Returns the underlying bean.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
T getBean();
|
||||
|
||||
/**
|
||||
* Returns whether the entity is considered to be new.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Information and commands on an entity.
|
||||
*/
|
||||
interface AdaptibleEntity<T> extends Entity<T> {
|
||||
|
||||
/**
|
||||
* Appends a {@code IF} condition to an {@link Update} statement for optimistic locking to perform the update only
|
||||
* if the version number matches. This method accepts {@code currentVersionNumber} as the {@link Update} typically
|
||||
* requires to increment the version number upon assembly time.
|
||||
*
|
||||
* @param update the {@link Update} statement to append the condition to.
|
||||
* @param currentVersionNumber previous version number.
|
||||
* @return the altered {@link Update} containing the {@code IF} condition for optimistic locking.
|
||||
*/
|
||||
StatementBuilder<Update> appendVersionCondition(StatementBuilder<Update> update, Number currentVersionNumber);
|
||||
|
||||
/**
|
||||
* Appends a {@code IF} condition to an {@link Delete} statement for optimistic locking to perform the delete only
|
||||
* if the version number matches. The {@link #getVersion() version number} is derived from the actual state as
|
||||
* delete statements typically do not increment the version prior to statement creation.
|
||||
*
|
||||
* @param delete the {@link Delete} statement to append the condition to.
|
||||
* @return the altered {@link Delete} containing the {@code IF} condition for optimistic locking.
|
||||
* @see #getVersion()
|
||||
*/
|
||||
StatementBuilder<Delete> appendVersionCondition(StatementBuilder<Delete> delete);
|
||||
|
||||
/**
|
||||
* Initializes the version property of the of the current entity if available.
|
||||
*
|
||||
* @return the entity with the version property updated if available.
|
||||
*/
|
||||
T initializeVersionProperty();
|
||||
|
||||
/**
|
||||
* Increments the value of the version property if available.
|
||||
*
|
||||
* @return the entity with the version property incremented if available.
|
||||
*/
|
||||
T incrementVersion();
|
||||
|
||||
/**
|
||||
* Returns the current version value if the entity has a version property.
|
||||
*
|
||||
* @return the current version or {@literal null} in case it's uninitialized or the entity doesn't expose a version
|
||||
* property.
|
||||
*/
|
||||
@Nullable
|
||||
Number getVersion();
|
||||
|
||||
/**
|
||||
* Returns the {@link CassandraPersistentEntity}.
|
||||
*
|
||||
* @return the {@link CassandraPersistentEntity}.
|
||||
*/
|
||||
CassandraPersistentEntity<?> getPersistentEntity();
|
||||
|
||||
}
|
||||
|
||||
private static class MappedEntity<T> implements Entity<T> {
|
||||
|
||||
private final CassandraPersistentEntity<?> entity;
|
||||
private final PersistentPropertyAccessor<T> propertyAccessor;
|
||||
|
||||
protected MappedEntity(CassandraPersistentEntity<?> entity, PersistentPropertyAccessor<T> propertyAccessor) {
|
||||
this.entity = entity;
|
||||
this.propertyAccessor = propertyAccessor;
|
||||
}
|
||||
|
||||
private static <T> MappedEntity<T> of(T bean,
|
||||
MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> context) {
|
||||
|
||||
CassandraPersistentEntity<?> entity = context.getRequiredPersistentEntity(bean.getClass());
|
||||
PersistentPropertyAccessor<T> propertyAccessor = entity.getPropertyAccessor(bean);
|
||||
|
||||
return new MappedEntity<>(entity, propertyAccessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getBean() {
|
||||
return this.propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return this.entity.isNew(getBean());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVersionedEntity() {
|
||||
return this.entity.hasVersionProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getVersion() {
|
||||
return this.propertyAccessor.getProperty(this.entity.getRequiredVersionProperty());
|
||||
}
|
||||
}
|
||||
|
||||
private static class AdaptibleMappedEntity<T> extends MappedEntity<T> implements AdaptibleEntity<T> {
|
||||
|
||||
private final CassandraPersistentEntity<?> entity;
|
||||
private final ConvertingPropertyAccessor<T> propertyAccessor;
|
||||
|
||||
private static <T> AdaptibleEntity<T> of(T bean,
|
||||
MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext,
|
||||
ConversionService conversionService) {
|
||||
|
||||
CassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(bean.getClass());
|
||||
|
||||
PersistentPropertyAccessor<T> propertyAccessor = entity.getPropertyAccessor(bean);
|
||||
|
||||
return new AdaptibleMappedEntity<>(entity, new ConvertingPropertyAccessor<>(propertyAccessor, conversionService));
|
||||
}
|
||||
|
||||
private AdaptibleMappedEntity(CassandraPersistentEntity<?> entity, ConvertingPropertyAccessor<T> propertyAccessor) {
|
||||
|
||||
super(entity, propertyAccessor);
|
||||
|
||||
this.entity = entity;
|
||||
this.propertyAccessor = propertyAccessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatementBuilder<Update> appendVersionCondition(StatementBuilder<Update> update,
|
||||
Number currentVersionNumber) {
|
||||
|
||||
return update.bind((statement, factory) -> {
|
||||
return statement.if_(Condition.column(getVersionColumnName()).isEqualTo(factory.create(currentVersionNumber)));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatementBuilder<Delete> appendVersionCondition(StatementBuilder<Delete> delete) {
|
||||
|
||||
return delete.bind((statement, factory) -> {
|
||||
return statement.if_(Condition.column(getVersionColumnName()).isEqualTo(factory.create(getVersion())));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public T initializeVersionProperty() {
|
||||
|
||||
if (this.entity.hasVersionProperty()) {
|
||||
|
||||
CassandraPersistentProperty versionProperty = this.entity.getRequiredVersionProperty();
|
||||
|
||||
this.propertyAccessor.setProperty(versionProperty, versionProperty.getType().isPrimitive() ? 1 : 0);
|
||||
}
|
||||
|
||||
return this.propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T incrementVersion() {
|
||||
|
||||
CassandraPersistentProperty versionProperty = this.entity.getRequiredVersionProperty();
|
||||
|
||||
Number version = getVersion();
|
||||
Number nextVersion = version == null ? 0 : version.longValue() + 1;
|
||||
|
||||
this.propertyAccessor.setProperty(versionProperty, nextVersion);
|
||||
|
||||
return this.propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Number getVersion() {
|
||||
|
||||
CassandraPersistentProperty versionProperty = this.entity.getRequiredVersionProperty();
|
||||
|
||||
return this.propertyAccessor.getProperty(versionProperty, Number.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CassandraPersistentEntity<?> getPersistentEntity() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
private CqlIdentifier getVersionColumnName() {
|
||||
return this.entity.getRequiredVersionProperty().getColumnName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.RowMapper;
|
||||
import org.springframework.data.cassandra.core.query.CassandraPageRequest;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.ResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
|
||||
/**
|
||||
* Simple utility class for working with the QueryBuilder API using mapped entities.
|
||||
* <p>
|
||||
* Only intended for internal use.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class EntityQueryUtils {
|
||||
|
||||
private static final Pattern FROM_REGEX = Pattern.compile(" FROM ([\"]?[\\w]*[\\\\.]?[\\w]*[\"]?)[\\s]?",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/**
|
||||
* Read a {@link Slice} of data from the {@link ResultSet} for a {@link Pageable}.
|
||||
*
|
||||
* @param resultSet must not be {@literal null}.
|
||||
* @param mapper must not be {@literal null}.
|
||||
* @param page
|
||||
* @param pageSize
|
||||
* @return the resulting {@link Slice}.
|
||||
*/
|
||||
static <T> Slice<T> readSlice(ResultSet resultSet, RowMapper<T> mapper, int page, int pageSize) {
|
||||
|
||||
int toRead = resultSet.getAvailableWithoutFetching();
|
||||
|
||||
return readSlice(() -> limit(resultSet.iterator(), toRead), resultSet.getExecutionInfo().getPagingState(), mapper,
|
||||
page, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a {@link Slice} of data from the {@link ResultSet} for a {@link Pageable}.
|
||||
*
|
||||
* @param resultSet must not be {@literal null}.
|
||||
* @param mapper must not be {@literal null}.
|
||||
* @param page
|
||||
* @param pageSize
|
||||
* @return the resulting {@link Slice}.
|
||||
* @since 3.0
|
||||
*/
|
||||
static <T> Slice<T> readSlice(AsyncResultSet resultSet, RowMapper<T> mapper, int page, int pageSize) {
|
||||
|
||||
return readSlice(() -> limit(resultSet.currentPage().iterator(), resultSet.remaining()),
|
||||
resultSet.getExecutionInfo().getPagingState(), mapper, page, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a {@link Slice} of data from the {@link Iterable} of {@link Row}s for a {@link Pageable}.
|
||||
*
|
||||
* @param rows must not be {@literal null}.
|
||||
* @param pagingState
|
||||
* @param mapper must not be {@literal null}.
|
||||
* @param page
|
||||
* @param pageSize
|
||||
* @return the resulting {@link Slice}.
|
||||
* @since 2.1
|
||||
*/
|
||||
static <T> Slice<T> readSlice(Iterable<Row> rows, @Nullable ByteBuffer pagingState, RowMapper<T> mapper, int page,
|
||||
int pageSize) {
|
||||
|
||||
List<T> result = new ArrayList<>(pageSize);
|
||||
|
||||
Iterator<Row> iterator = rows.iterator();
|
||||
int index = 0;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T element = mapper.mapRow(iterator.next(), index++);
|
||||
result.add(element);
|
||||
}
|
||||
|
||||
CassandraPageRequest pageRequest = CassandraPageRequest.of(PageRequest.of(page, pageSize), pagingState);
|
||||
|
||||
return new SliceImpl<>(result, pageRequest, pagingState != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the table name from a {@link Statement}.
|
||||
*
|
||||
* @param statement
|
||||
* @return
|
||||
* @since 2.1
|
||||
*/
|
||||
static CqlIdentifier getTableName(Statement<?> statement) {
|
||||
|
||||
String cql = statement instanceof SimpleStatement ? ((SimpleStatement) statement).getQuery() : statement.toString();
|
||||
Matcher matcher = FROM_REGEX.matcher(cql);
|
||||
|
||||
if (matcher.find()) {
|
||||
|
||||
String cqlTableName = matcher.group(1);
|
||||
|
||||
int separator = cqlTableName.indexOf('.');
|
||||
|
||||
if (separator != -1) {
|
||||
cqlTableName = cqlTableName.substring(separator + 1);
|
||||
}
|
||||
|
||||
if (cqlTableName.startsWith("\"") || cqlTableName.endsWith("\"")) {
|
||||
return CqlIdentifier.fromCql(cqlTableName.substring(separator + 1));
|
||||
}
|
||||
|
||||
return CqlIdentifier.fromInternal(cqlTableName);
|
||||
}
|
||||
|
||||
return CqlIdentifier.fromCql("unknown");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view containing the first {@code limitSize} elements of {@code iterator}. If {@code
|
||||
* iterator} contains fewer than {@code limitSize} elements, the returned view contains all of its elements. The
|
||||
* returned iterator supports {@code remove()} if {@code iterator} does.
|
||||
*
|
||||
* @param iterator the iterator to limit
|
||||
* @param limitSize the maximum number of elements in the returned iterator
|
||||
* @throws IllegalArgumentException if {@code limitSize} is negative
|
||||
* @since 3.0
|
||||
*/
|
||||
private static <T> Iterator<T> limit(Iterator<T> iterator, int limitSize) {
|
||||
|
||||
return new Iterator<T>() {
|
||||
private int count;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return count < limitSize && iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
count++;
|
||||
return iterator.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2018-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.cassandra.core.WriteResult;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
/**
|
||||
* The result of a write operation for an entity.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @see WriteResult
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class EntityWriteResult<T> extends org.springframework.data.cassandra.core.EntityWriteResult<T> {
|
||||
|
||||
public EntityWriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows, T entity) {
|
||||
super(executionInfo, wasApplied, rows, entity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright 2020-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.data.cassandra.core.cql.QueryExtractorDelegate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.cql.BoundStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
|
||||
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
|
||||
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
import com.datastax.oss.driver.api.core.type.DataType;
|
||||
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
|
||||
|
||||
/**
|
||||
* Support class for Cassandra Template API implementation classes that want to make use of prepared statements.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class PreparedStatementDelegate {
|
||||
|
||||
/**
|
||||
* Bind values held in {@link SimpleStatement} to the {@link PreparedStatement} and apply query options that are set
|
||||
* or do not match the default value.
|
||||
*
|
||||
* @param source
|
||||
* @param ps
|
||||
* @return the bound statement.
|
||||
*/
|
||||
static BoundStatement bind(SimpleStatement source, PreparedStatement ps) {
|
||||
|
||||
BoundStatementBuilder builder = ps.boundStatementBuilder(source.getPositionalValues().toArray());
|
||||
|
||||
Mapper mapper = Mapper.INSTANCE;
|
||||
|
||||
mapper.from(source.getExecutionProfileName()).whenHasText().to(builder::setExecutionProfileName);
|
||||
mapper.from(source.getExecutionProfile()).whenNonNull().to(builder::setExecutionProfile);
|
||||
mapper.from(source.getRoutingKeyspace()).whenNonNull().to(builder::setRoutingKeyspace);
|
||||
mapper.from(source.getRoutingKey()).whenNonNull().to(builder::setRoutingKey);
|
||||
mapper.from(source.getRoutingToken()).whenNonNull().to(builder::setRoutingToken);
|
||||
mapper.from(source.isIdempotent()).whenNonNull().to(builder::setIdempotence);
|
||||
mapper.from(source.isTracing()).whenNonNull().to(builder::setTracing);
|
||||
mapper.from(source.getQueryTimestamp()).whenNot(it -> it == Statement.NO_DEFAULT_TIMESTAMP)
|
||||
.to(builder::setQueryTimestamp);
|
||||
mapper.from(source.getPagingState()).whenNonNull().to(builder::setPagingState);
|
||||
mapper.from(source.getPageSize()).whenNot(it -> it == 0L).to(builder::setPageSize);
|
||||
mapper.from(source.getConsistencyLevel()).whenNonNull().to(builder::setConsistencyLevel);
|
||||
mapper.from(source.getSerialConsistencyLevel()).whenNonNull().to(builder::setSerialConsistencyLevel);
|
||||
mapper.from(source.getTimeout()).whenNonNull().to(builder::setTimeout);
|
||||
mapper.from(source.getNode()).whenNonNull().to(builder::setNode);
|
||||
mapper.from(source.getNowInSeconds()).whenNot(it -> it == Statement.NO_NOW_IN_SECONDS).to(builder::setNowInSeconds);
|
||||
|
||||
Map<CqlIdentifier, Object> namedValues = source.getNamedValues();
|
||||
|
||||
ColumnDefinitions variableDefinitions = ps.getVariableDefinitions();
|
||||
CodecRegistry codecRegistry = builder.codecRegistry();
|
||||
for (Map.Entry<CqlIdentifier, Object> entry : namedValues.entrySet()) {
|
||||
|
||||
if (entry.getValue() == null) {
|
||||
builder = builder.setToNull(entry.getKey());
|
||||
} else {
|
||||
DataType type = variableDefinitions.get(entry.getKey()).getType();
|
||||
builder = builder.set(entry.getKey(), entry.getValue(), codecRegistry.codecFor(type));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the given {@link Statement} is a {@link SimpleStatement}. Throw a {@link IllegalArgumentException}
|
||||
* otherwise.
|
||||
*
|
||||
* @param statement
|
||||
* @return the {@link SimpleStatement}.
|
||||
*/
|
||||
static SimpleStatement getStatementForPrepare(Statement<?> statement) {
|
||||
|
||||
if (statement instanceof SimpleStatement) {
|
||||
return (SimpleStatement) statement;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(getMessage(statement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to use prepared statements. When {@code usePreparedStatements} is {@literal true}, then verifying
|
||||
* additionally that the given {@link Statement} is a {@link SimpleStatement}, otherwise log the mismatch and fallback
|
||||
* to non-prepared usage.
|
||||
*
|
||||
* @param usePreparedStatements
|
||||
* @param statement
|
||||
* @param logger
|
||||
* @return
|
||||
*/
|
||||
static boolean canPrepare(boolean usePreparedStatements, Statement<?> statement, Log logger) {
|
||||
|
||||
if (usePreparedStatements) {
|
||||
|
||||
if (statement instanceof SimpleStatement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.warn(getMessage(statement));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String getMessage(Statement<?> statement) {
|
||||
|
||||
String cql = QueryExtractorDelegate.getCql(statement);
|
||||
|
||||
if (StringUtils.hasText(cql)) {
|
||||
return String.format("Cannot prepare statement %s (%s); Statement must be a SimpleStatement", cql, statement);
|
||||
}
|
||||
|
||||
return String.format("Cannot prepare statement %s; Statement must be a SimpleStatement", statement);
|
||||
}
|
||||
|
||||
enum Mapper {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* Return a new {@link Source} from the specified value supplier that can be used to perform the mapping.
|
||||
*
|
||||
* @param <T> the source type
|
||||
* @param supplier the value supplier
|
||||
* @return a {@link Source} that can be used to complete the mapping
|
||||
* @see #from(Object)
|
||||
*/
|
||||
public <T> Source<T> from(Supplier<T> supplier) {
|
||||
|
||||
Assert.notNull(supplier, "Supplier must not be null");
|
||||
return getSource(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link Source} from the specified value that can be used to perform the mapping.
|
||||
*
|
||||
* @param <T> the source type
|
||||
* @param value the value
|
||||
* @return a {@link Source} that can be used to complete the mapping
|
||||
*/
|
||||
public <T> Source<T> from(@Nullable T value) {
|
||||
return from(() -> value);
|
||||
}
|
||||
|
||||
private <T> Source<T> getSource(Supplier<T> supplier) {
|
||||
return new Source<>(SingletonSupplier.of(supplier), t -> true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source value/supplier that is in the process of being mapped.
|
||||
*
|
||||
* @param <T> the source type
|
||||
*/
|
||||
static class Source<T> {
|
||||
|
||||
private final Supplier<T> supplier;
|
||||
|
||||
private final Predicate<T> predicate;
|
||||
|
||||
private Source(Supplier<T> supplier, Predicate<T> predicate) {
|
||||
|
||||
Assert.notNull(predicate, "Predicate must not be null");
|
||||
|
||||
this.supplier = supplier;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that won't map non-null values or suppliers that throw a
|
||||
* {@link NullPointerException}.
|
||||
*
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> whenNonNull() {
|
||||
return new Source<>(this.supplier, Objects::nonNull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that will only map values that are {@code true}.
|
||||
*
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> whenTrue() {
|
||||
return when(Boolean.TRUE::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that will only map values that are {@code false}.
|
||||
*
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> whenFalse() {
|
||||
return when(Boolean.FALSE::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that will only map values that have a {@code toString()} containing
|
||||
* actual text.
|
||||
*
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> whenHasText() {
|
||||
return when((value) -> StringUtils.hasText(Objects.toString(value, null)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that will only map values equal to the specified {@code object}.
|
||||
*
|
||||
* @param object the object to match
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> whenEqualTo(Object object) {
|
||||
return when(object::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that won't map values that match the given predicate.
|
||||
*
|
||||
* @param predicate the predicate used to filter values
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> whenNot(Predicate<T> predicate) {
|
||||
|
||||
Assert.notNull(predicate, "Predicate must not be null");
|
||||
return when(predicate.negate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a filtered version of the source that won't map values that don't match the given predicate.
|
||||
*
|
||||
* @param predicate the predicate used to filter values
|
||||
* @return a new filtered source instance
|
||||
*/
|
||||
public Source<T> when(Predicate<T> predicate) {
|
||||
|
||||
Assert.notNull(predicate, "Predicate must not be null");
|
||||
return new Source<>(this.supplier, (this.predicate != null) ? this.predicate.and(predicate) : predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the mapping by passing any non-filtered value to the specified consumer.
|
||||
*
|
||||
* @param consumer the consumer that should accept the value if it's not been filtered
|
||||
*/
|
||||
public void to(Consumer<T> consumer) {
|
||||
|
||||
Assert.notNull(consumer, "Consumer must not be null");
|
||||
|
||||
T value = this.supplier.get();
|
||||
if (this.predicate.test(value)) {
|
||||
consumer.accept(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
@Deprecated(since = "4.0", forRemoval = true)
|
||||
class WriteResult extends org.springframework.data.cassandra.core.WriteResult {
|
||||
|
||||
WriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows) {
|
||||
super(executionInfo, wasApplied, rows);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Apache Cassandra legacy asynchronous support for easier migration.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.cassandra.core.legacy;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -19,7 +19,7 @@ import com.datastax.oss.driver.api.core.cql.Statement
|
||||
import org.springframework.data.cassandra.core.query.Query
|
||||
import org.springframework.data.cassandra.core.query.Update
|
||||
import org.springframework.data.domain.Slice
|
||||
import org.springframework.util.concurrent.ListenableFuture
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@@ -36,41 +36,63 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(cql)"))
|
||||
fun <T : Any> AsyncCassandraOperations.select(cql: String, entityClass: KClass<T>): ListenableFuture<List<T>> =
|
||||
select(cql, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(cql)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.select(
|
||||
cql: String,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<List<T>> =
|
||||
select(cql, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(cql: String): ListenableFuture<List<T>> =
|
||||
select(cql, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(cql: String): CompletableFuture<List<T>> =
|
||||
select(cql, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a Consumer-like function.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(cql, consumer)"))
|
||||
fun <T : Any> AsyncCassandraOperations.select(cql: String, entityClass: KClass<T>, consumer: (T) -> Unit): ListenableFuture<Void> =
|
||||
select(cql, consumer, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(cql, consumer)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.select(
|
||||
cql: String,
|
||||
entityClass: KClass<T>,
|
||||
consumer: (T) -> Unit
|
||||
): CompletableFuture<Void> =
|
||||
select(cql, consumer, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a Consumer-like function leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(cql: String, crossinline consumer: (T) -> Unit): ListenableFuture<Void> =
|
||||
select(cql, { consumer(it) }, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(
|
||||
cql: String,
|
||||
crossinline consumer: (T) -> Unit
|
||||
): CompletableFuture<Void> =
|
||||
select(cql, { consumer(it) }, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOne] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("selectOne<T>(cql)"))
|
||||
fun <T : Any> AsyncCassandraOperations.selectOne(cql: String, entityClass: KClass<T>): ListenableFuture<T?> =
|
||||
selectOne(cql, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("selectOne<T>(cql)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.selectOne(
|
||||
cql: String,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<T?> =
|
||||
selectOne(cql, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOne] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOne(cql: String): ListenableFuture<T?> =
|
||||
selectOne(cql, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOne(cql: String): CompletableFuture<T?> =
|
||||
selectOne(cql, T::class.java)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with com.datastax.oss.driver.api.core.cql.Statement
|
||||
@@ -79,54 +101,82 @@ inline fun <reified T : Any> AsyncCassandraOperations.selectOne(cql: String): Li
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(statement)"))
|
||||
fun <T : Any> AsyncCassandraOperations.select(statement: Statement<*>, entityClass: KClass<T>): ListenableFuture<List<T>> =
|
||||
select(statement, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(statement)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.select(
|
||||
statement: Statement<*>,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<List<T>> =
|
||||
select(statement, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(statement: Statement<*>): ListenableFuture<List<T>> =
|
||||
select(statement, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(statement: Statement<*>): CompletableFuture<List<T>> =
|
||||
select(statement, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a Consumer-like function.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(statement, consumer)"))
|
||||
fun <T : Any> AsyncCassandraOperations.select(statement: Statement<*>, entityClass: KClass<T>, consumer: (T) -> Unit): ListenableFuture<Void> =
|
||||
select(statement, consumer, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(statement, consumer)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.select(
|
||||
statement: Statement<*>,
|
||||
entityClass: KClass<T>,
|
||||
consumer: (T) -> Unit
|
||||
): CompletableFuture<Void> =
|
||||
select(statement, consumer, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a Consumer-like function leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(statement: Statement<*>, crossinline consumer: (T) -> Unit): ListenableFuture<Void> =
|
||||
select(statement, { consumer(it) }, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(
|
||||
statement: Statement<*>,
|
||||
crossinline consumer: (T) -> Unit
|
||||
): CompletableFuture<Void> =
|
||||
select(statement, { consumer(it) }, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.slice] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("slice<T>(statement)"))
|
||||
fun <T : Any> AsyncCassandraOperations.slice(statement: Statement<*>, entityClass: KClass<T>): ListenableFuture<Slice<T>> =
|
||||
slice(statement, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("slice<T>(statement)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.slice(
|
||||
statement: Statement<*>,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Slice<T>> =
|
||||
slice(statement, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.slice] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.slice(statement: Statement<*>): ListenableFuture<Slice<T>> =
|
||||
slice(statement, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.slice(statement: Statement<*>): CompletableFuture<Slice<T>> =
|
||||
slice(statement, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOne] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("selectOne<T>(statement)"))
|
||||
fun <T : Any> AsyncCassandraOperations.selectOne(statement: Statement<*>, entityClass: KClass<T>): ListenableFuture<T?> =
|
||||
selectOne(statement, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("selectOne<T>(statement)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.selectOne(
|
||||
statement: Statement<*>,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<T?> =
|
||||
selectOne(statement, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOne] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOne(statement: Statement<*>): ListenableFuture<T?> =
|
||||
selectOne(statement, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOne(statement: Statement<*>): CompletableFuture<T?> =
|
||||
selectOne(statement, T::class.java)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with org.springframework.data.cassandra.core.query.Query
|
||||
@@ -135,82 +185,129 @@ inline fun <reified T : Any> AsyncCassandraOperations.selectOne(statement: State
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(query)"))
|
||||
fun <T : Any> AsyncCassandraOperations.select(query: Query, entityClass: KClass<T>): ListenableFuture<List<T>> =
|
||||
select(query, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(query)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.select(
|
||||
query: Query,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<List<T>> =
|
||||
select(query, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(query: Query): ListenableFuture<List<T>> =
|
||||
select(query, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(query: Query): CompletableFuture<List<T>> =
|
||||
select(query, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a Consumer-like function.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(query, consumer)"))
|
||||
fun <T : Any> AsyncCassandraOperations.select(query: Query, entityClass: KClass<T>, consumer: (T) -> Unit): ListenableFuture<Void> =
|
||||
select(query, consumer, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(query, consumer)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.select(
|
||||
query: Query,
|
||||
entityClass: KClass<T>,
|
||||
consumer: (T) -> Unit
|
||||
): CompletableFuture<Void> =
|
||||
select(query, consumer, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.select] providing a Consumer-like function leveraging reified type parameters.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("select<T>(query, consumer)"))
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(query: Query, crossinline consumer: (T) -> Unit): ListenableFuture<Void> =
|
||||
select(query, { consumer(it) }, T::class.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("select<T>(query, consumer)")
|
||||
)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.select(
|
||||
query: Query,
|
||||
crossinline consumer: (T) -> Unit
|
||||
): CompletableFuture<Void> =
|
||||
select(query, { consumer(it) }, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.slice] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("slice<T>(query)"))
|
||||
fun <T : Any> AsyncCassandraOperations.slice(query: Query, entityClass: KClass<T>): ListenableFuture<Slice<T>> =
|
||||
slice(query, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("slice<T>(query)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.slice(
|
||||
query: Query,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Slice<T>> =
|
||||
slice(query, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.slice] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.slice(query: Query): ListenableFuture<Slice<T>> =
|
||||
slice(query, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.slice(query: Query): CompletableFuture<Slice<T>> =
|
||||
slice(query, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOne] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("selectOne<T>(query)"))
|
||||
fun <T : Any> AsyncCassandraOperations.selectOne(query: Query, entityClass: KClass<T>): ListenableFuture<T?> =
|
||||
selectOne(query, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("selectOne<T>(query)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.selectOne(
|
||||
query: Query,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<T?> =
|
||||
selectOne(query, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOne] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOne(query: Query): ListenableFuture<T?> =
|
||||
selectOne(query, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOne(query: Query): CompletableFuture<T?> =
|
||||
selectOne(query, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.update] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("update<T>(query, update)"))
|
||||
fun <T : Any> AsyncCassandraOperations.update(query: Query, update: Update, entityClass: KClass<T>): ListenableFuture<Boolean> =
|
||||
update(query, update, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("update<T>(query, update)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.update(
|
||||
query: Query,
|
||||
update: Update,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Boolean> =
|
||||
update(query, update, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.update] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.update(query: Query, update: Update): ListenableFuture<Boolean> =
|
||||
update(query, update, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.update(
|
||||
query: Query,
|
||||
update: Update
|
||||
): CompletableFuture<Boolean> =
|
||||
update(query, update, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.delete] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("delete<T>(query)"))
|
||||
fun <T : Any> AsyncCassandraOperations.delete(query: Query, entityClass: KClass<T>): ListenableFuture<Boolean> =
|
||||
delete(query, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("delete<T>(query)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.delete(
|
||||
query: Query,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Boolean> =
|
||||
delete(query, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.delete] leveraging reified type parameters.
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.delete(query: Query): ListenableFuture<Boolean> =
|
||||
delete(query, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.delete(query: Query): CompletableFuture<Boolean> =
|
||||
delete(query, T::class.java)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with entities
|
||||
@@ -220,89 +317,122 @@ inline fun <reified T : Any> AsyncCassandraOperations.delete(query: Query): List
|
||||
* Extension for [AsyncCassandraOperations.count] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("count<T>()"))
|
||||
fun <T : Any> AsyncCassandraOperations.count(entityClass: KClass<T>): ListenableFuture<Long> =
|
||||
count(entityClass.java)
|
||||
fun <T : Any> AsyncCassandraOperations.count(entityClass: KClass<T>): CompletableFuture<Long> =
|
||||
count(entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.count] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.count(): ListenableFuture<Long> =
|
||||
count(T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.count(): CompletableFuture<Long> =
|
||||
count(T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.count] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("count<T>(query)"))
|
||||
fun <T : Any> AsyncCassandraOperations.count(query: Query, entityClass: KClass<T>): ListenableFuture<Long> =
|
||||
count(query, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("count<T>(query)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.count(
|
||||
query: Query,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Long> =
|
||||
count(query, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.count] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.count(query: Query): ListenableFuture<Long> =
|
||||
count(query, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.count(query: Query): CompletableFuture<Long> =
|
||||
count(query, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.exists] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("exists<T>(id)"))
|
||||
fun <T : Any> AsyncCassandraOperations.exists(id: Any, entityClass: KClass<T>): ListenableFuture<Boolean> =
|
||||
exists(id, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("exists<T>(id)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.exists(
|
||||
id: Any,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Boolean> =
|
||||
exists(id, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.exists] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.exists(id: Any): ListenableFuture<Boolean> =
|
||||
exists(id, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.exists(id: Any): CompletableFuture<Boolean> =
|
||||
exists(id, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.count] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("exists<T>(query)"))
|
||||
fun <T : Any> AsyncCassandraOperations.exists(query: Query, entityClass: KClass<T>): ListenableFuture<Boolean> =
|
||||
exists(query, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("exists<T>(query)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.exists(
|
||||
query: Query,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Boolean> =
|
||||
exists(query, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.count] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.exists(query: Query): ListenableFuture<Boolean> =
|
||||
exists(query, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.exists(query: Query): CompletableFuture<Boolean> =
|
||||
exists(query, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOneById] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("selectOneById<T>(id)"))
|
||||
fun <T : Any> AsyncCassandraOperations.selectOneById(id: Any, entityClass: KClass<T>): ListenableFuture<T?> =
|
||||
selectOneById(id, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("selectOneById<T>(id)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.selectOneById(
|
||||
id: Any,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<T?> =
|
||||
selectOneById(id, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.selectOneById] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOneById(id: Any): ListenableFuture<T?> =
|
||||
selectOneById(id, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.selectOneById(id: Any): CompletableFuture<T?> =
|
||||
selectOneById(id, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.deleteById] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("deleteById<T>(id)"))
|
||||
fun <T : Any> AsyncCassandraOperations.deleteById(id: Any, entityClass: KClass<T>): ListenableFuture<Boolean> =
|
||||
deleteById(id, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("deleteById<T>(id)")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.deleteById(
|
||||
id: Any,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<Boolean> =
|
||||
deleteById(id, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.deleteById] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.deleteById(id: Any): ListenableFuture<Boolean> =
|
||||
deleteById(id, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.deleteById(id: Any): CompletableFuture<Boolean> =
|
||||
deleteById(id, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.truncate] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("truncate<T>()"))
|
||||
fun <T : Any> AsyncCassandraOperations.truncate(entityClass: KClass<T>): ListenableFuture<Void> =
|
||||
truncate(entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("truncate<T>()")
|
||||
)
|
||||
fun <T : Any> AsyncCassandraOperations.truncate(entityClass: KClass<T>): CompletableFuture<Void> =
|
||||
truncate(entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCassandraOperations.truncate] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.truncate(): ListenableFuture<Void> =
|
||||
truncate(T::class.java)
|
||||
inline fun <reified T : Any> AsyncCassandraOperations.truncate(): CompletableFuture<Void> =
|
||||
truncate(T::class.java)
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.cassandra.core.cql
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet
|
||||
import com.datastax.oss.driver.api.core.cql.Row
|
||||
import com.datastax.oss.driver.api.core.cql.Statement
|
||||
import org.springframework.util.concurrent.ListenableFuture
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@@ -31,86 +31,129 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("queryForObject<T>(cql)"))
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(cql: String, entityClass: KClass<T>): ListenableFuture<T?> =
|
||||
queryForObject(cql, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("queryForObject<T>(cql)")
|
||||
)
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(
|
||||
cql: String,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<T?> =
|
||||
queryForObject(cql, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForObject(cql: String): ListenableFuture<T?> =
|
||||
queryForObject(cql, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForObject(cql: String): CompletableFuture<T?> =
|
||||
queryForObject(cql, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("queryForObject<T>(cql, args)"))
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(cql: String, entityClass: KClass<T>, vararg args: Any): ListenableFuture<T?> =
|
||||
queryForObject(cql, entityClass.java, args)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("queryForObject<T>(cql, args)")
|
||||
)
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(
|
||||
cql: String,
|
||||
entityClass: KClass<T>,
|
||||
vararg args: Any
|
||||
): CompletableFuture<T?> =
|
||||
queryForObject(cql, entityClass.java, args)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForObject(cql: String, vararg args: Any): ListenableFuture<T?> =
|
||||
queryForObject(cql, T::class.java, args)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForObject(
|
||||
cql: String,
|
||||
vararg args: Any
|
||||
): CompletableFuture<T?> =
|
||||
queryForObject(cql, T::class.java, args)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] leveraging reified type parameters.
|
||||
*/
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(cql: String, vararg args: Any, function: (Row, Int) -> T): ListenableFuture<T?> =
|
||||
queryForObject(cql, RowMapper { row, i -> function(row, i) }, *args)
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(
|
||||
cql: String,
|
||||
vararg args: Any,
|
||||
function: (Row, Int) -> T
|
||||
): CompletableFuture<T?> =
|
||||
queryForObject(cql, RowMapper { row, i -> function(row, i) }, *args)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("queryForObject<T>(statement)"))
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(statement: Statement<*>, entityClass: KClass<T>): ListenableFuture<T?> =
|
||||
queryForObject(statement, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("queryForObject<T>(statement)")
|
||||
)
|
||||
fun <T : Any> AsyncCqlOperations.queryForObject(
|
||||
statement: Statement<*>,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<T?> =
|
||||
queryForObject(statement, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForObject] leveraging reified type parameters.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForObject(statement: Statement<*>): ListenableFuture<T?> =
|
||||
queryForObject(statement, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForObject(statement: Statement<*>): CompletableFuture<T?> =
|
||||
queryForObject(statement, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForList] leveraging reified type parameters.
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForList(cql: String): ListenableFuture<List<T>> =
|
||||
queryForList(cql, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForList(cql: String): CompletableFuture<List<T>> =
|
||||
queryForList(cql, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForList] leveraging reified type parameters.
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForList(cql: String, vararg args: Any): ListenableFuture<List<T>> =
|
||||
queryForList(cql, T::class.java, args)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForList(
|
||||
cql: String,
|
||||
vararg args: Any
|
||||
): CompletableFuture<List<T>> =
|
||||
queryForList(cql, T::class.java, args)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForList] providing a [KClass] based variant.
|
||||
*/
|
||||
@Deprecated("Since 2.2, use the reified variant", replaceWith = ReplaceWith("queryForList<T>(statement)"))
|
||||
fun <T : Any> AsyncCqlOperations.queryForList(statement: Statement<*>, entityClass: KClass<T>): ListenableFuture<List<T>> =
|
||||
queryForList(statement, entityClass.java)
|
||||
@Deprecated(
|
||||
"Since 2.2, use the reified variant",
|
||||
replaceWith = ReplaceWith("queryForList<T>(statement)")
|
||||
)
|
||||
fun <T : Any> AsyncCqlOperations.queryForList(
|
||||
statement: Statement<*>,
|
||||
entityClass: KClass<T>
|
||||
): CompletableFuture<List<T>> =
|
||||
queryForList(statement, entityClass.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.queryForList] leveraging reified type parameters.
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForList(statement: Statement<*>): ListenableFuture<List<T>> =
|
||||
queryForList(statement, T::class.java)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.queryForList(statement: Statement<*>): CompletableFuture<List<T>> =
|
||||
queryForList(statement, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.query] providing a ResultSetExtractor-like function
|
||||
* variant: `query("...", arg1, argN){ rs -> }`.
|
||||
*/
|
||||
inline fun <reified T : Any> AsyncCqlOperations.query(cql: String, vararg args: Any, crossinline function: (AsyncResultSet) -> ListenableFuture<T>): ListenableFuture<T?> =
|
||||
query(cql, AsyncResultSetExtractor { function(it) }, *args)
|
||||
inline fun <reified T : Any> AsyncCqlOperations.query(
|
||||
cql: String,
|
||||
vararg args: Any,
|
||||
crossinline function: (AsyncResultSet) -> CompletableFuture<T>
|
||||
): CompletableFuture<T?> =
|
||||
query(cql, AsyncResultSetExtractor { function(it) }, *args)
|
||||
|
||||
/**
|
||||
* Extension for [AsyncCqlOperations.query] providing a RowMapper-like function
|
||||
* variant: `query("...", arg1, argN){ row, i -> }`.
|
||||
*/
|
||||
fun <T : Any> AsyncCqlOperations.query(cql: String, vararg args: Any, function: (Row, Int) -> T): ListenableFuture<List<T>> =
|
||||
query(cql, RowMapper { row, i -> function(row, i) }, *args)
|
||||
fun <T : Any> AsyncCqlOperations.query(
|
||||
cql: String,
|
||||
vararg args: Any,
|
||||
function: (Row, Int) -> T
|
||||
): CompletableFuture<List<T>> =
|
||||
query(cql, RowMapper { row, i -> function(row, i) }, *args)
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.springframework.data.cassandra.repository.support.SchemaTestUtils;
|
||||
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTests;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
||||
|
||||
@@ -117,7 +116,7 @@ class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
|
||||
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
|
||||
ListenableFuture<User> insert = template.insert(user);
|
||||
Future<User> insert = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(insert)).isEqualTo(user);
|
||||
assertThat(getUser(user.getId())).isEqualTo(user);
|
||||
@@ -130,7 +129,7 @@ class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<EntityWriteResult<User>> inserted = template.insert(user, lwtOptions);
|
||||
Future<EntityWriteResult<User>> inserted = template.insert(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(inserted).wasApplied()).isTrue();
|
||||
}
|
||||
@@ -146,7 +145,7 @@ class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
ListenableFuture<EntityWriteResult<User>> lwt = template.insert(user, lwtOptions);
|
||||
Future<EntityWriteResult<User>> lwt = template.insert(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(lwt).wasApplied()).isFalse();
|
||||
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter");
|
||||
@@ -159,7 +158,7 @@ class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
|
||||
|
||||
User result = getUninterruptibly(template.insert(user));
|
||||
|
||||
ListenableFuture<Long> count = template.count(User.class);
|
||||
Future<Long> count = template.count(User.class);
|
||||
assertThat(result).isSameAs(user);
|
||||
assertThat(getUninterruptibly(count)).isEqualTo(1L);
|
||||
}
|
||||
@@ -207,7 +206,7 @@ class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<EntityWriteResult<User>> lwt = template.update(user, lwtOptions);
|
||||
Future<EntityWriteResult<User>> lwt = template.update(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(lwt).wasApplied()).isFalse();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
@@ -223,7 +222,7 @@ class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingInt
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
ListenableFuture<EntityWriteResult<User>> updated = template.update(user, lwtOptions);
|
||||
Future<EntityWriteResult<User>> updated = template.update(user, lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(updated).wasApplied()).isTrue();
|
||||
assertThat(getUninterruptibly(updated).getEntity()).isSameAs(user);
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.springframework.data.cassandra.core.query.Update;
|
||||
import org.springframework.data.cassandra.domain.User;
|
||||
import org.springframework.data.cassandra.domain.VersionedUser;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
@@ -140,7 +139,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
ListenableFuture<List<User>> list = template.select("SELECT * FROM users", User.class);
|
||||
CompletableFuture<List<User>> list = template.select("SELECT * FROM users", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(list)).hasSize(1).contains(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -165,7 +164,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
List<User> list = new ArrayList<>();
|
||||
|
||||
ListenableFuture<Void> result = template.select("SELECT * FROM users", list::add, User.class);
|
||||
CompletableFuture<Void> result = template.select("SELECT * FROM users", list::add, User.class);
|
||||
|
||||
assertThat(getUninterruptibly(result)).isNull();
|
||||
assertThat(list).hasSize(1).contains(new User("myid", "Walter", "White"));
|
||||
@@ -178,7 +177,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
when(resultSet.currentPage()).thenThrow(new NoNodeAvailableException());
|
||||
|
||||
ListenableFuture<List<User>> list = template.select("SELECT * FROM users", User.class);
|
||||
CompletableFuture<List<User>> list = template.select("SELECT * FROM users", User.class);
|
||||
|
||||
try {
|
||||
list.get();
|
||||
@@ -207,7 +206,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
ListenableFuture<User> future = template.selectOne("SELECT * FROM users WHERE id='myid'", User.class);
|
||||
CompletableFuture<User> future = template.selectOne("SELECT * FROM users WHERE id='myid'", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -230,7 +229,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
ListenableFuture<User> future = template.selectOneById("myid", User.class);
|
||||
CompletableFuture<User> future = template.selectOneById("myid", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -242,7 +241,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.selectOne("SELECT id FROM users WHERE id='myid'", String.class);
|
||||
CompletableFuture<String> future = template.selectOne("SELECT id FROM users WHERE id='myid'", String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isNull();
|
||||
}
|
||||
@@ -252,7 +251,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
when(resultSet.one()).thenReturn(row);
|
||||
|
||||
ListenableFuture<Boolean> future = template.exists("myid", User.class);
|
||||
CompletableFuture<Boolean> future = template.exists("myid", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -262,7 +261,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
@Test // DATACASS-292
|
||||
void existsShouldReturnNonExistingElement() {
|
||||
|
||||
ListenableFuture<Boolean> future = template.exists("myid", User.class);
|
||||
CompletableFuture<Boolean> future = template.exists("myid", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isFalse();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -274,7 +273,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
when(resultSet.one()).thenReturn(row);
|
||||
|
||||
ListenableFuture<Boolean> future = template.exists(Query.empty(), User.class);
|
||||
CompletableFuture<Boolean> future = template.exists(Query.empty(), User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -288,7 +287,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(row.getLong(0)).thenReturn(42L);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
|
||||
ListenableFuture<Long> future = template.count(User.class);
|
||||
CompletableFuture<Long> future = template.count(User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(42L);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -302,7 +301,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(row.getLong(0)).thenReturn(42L);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
|
||||
ListenableFuture<Long> future = template.count(Query.empty(), User.class);
|
||||
CompletableFuture<Long> future = template.count(Query.empty(), User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(42L);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -316,7 +315,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> future = template.insert(user);
|
||||
CompletableFuture<User> future = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -333,7 +332,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
VersionedUser user = new VersionedUser("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<VersionedUser> future = template.insert(user);
|
||||
CompletableFuture<VersionedUser> future = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -350,7 +349,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class)))
|
||||
.thenReturn(TestResultSetFuture.failed(new NoNodeAvailableException()));
|
||||
|
||||
ListenableFuture<User> future = template.insert(new User("heisenberg", "Walter", "White"));
|
||||
CompletableFuture<User> future = template.insert(new User("heisenberg", "Walter", "White"));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -369,7 +368,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> future = template.update(user);
|
||||
CompletableFuture<User> future = template.update(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -387,7 +386,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
VersionedUser user = new VersionedUser("heisenberg", "Walter", "White");
|
||||
user.setVersion(0L);
|
||||
|
||||
ListenableFuture<VersionedUser> future = template.update(user);
|
||||
CompletableFuture<VersionedUser> future = template.update(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -461,7 +460,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class)))
|
||||
.thenReturn(TestResultSetFuture.failed(new NoNodeAvailableException()));
|
||||
|
||||
ListenableFuture<User> future = template.update(new User("heisenberg", "Walter", "White"));
|
||||
CompletableFuture<User> future = template.update(new User("heisenberg", "Walter", "White"));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -480,7 +479,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<Boolean> future = template.deleteById(user.getId(), User.class);
|
||||
CompletableFuture<Boolean> future = template.deleteById(user.getId(), User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -494,7 +493,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> future = template.delete(user);
|
||||
CompletableFuture<User> future = template.delete(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
@@ -534,7 +533,7 @@ class AsyncCassandraTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class)))
|
||||
.thenReturn(TestResultSetFuture.failed(new NoNodeAvailableException()));
|
||||
|
||||
ListenableFuture<User> future = template.delete(new User("heisenberg", "Walter", "White"));
|
||||
CompletableFuture<User> future = template.delete(new User("heisenberg", "Walter", "White"));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTests;
|
||||
import org.springframework.util.concurrent.CompletableToListenableFutureAdapter;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
|
||||
@@ -150,8 +149,8 @@ class AsyncCqlTemplateIntegrationTests extends AbstractKeyspaceCreatingIntegrati
|
||||
void queryPreparedStatementCreatorShouldInvokeCallback() {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
getUninterruptibly(template.query(session -> new CompletableToListenableFutureAdapter<>(
|
||||
session.prepareAsync("SELECT id FROM user WHERE id = ?;")), ps -> ps.bind("WHITE"), row -> {
|
||||
getUninterruptibly(template.query(session -> session.prepareAsync("SELECT id FROM user WHERE id = ?;"),
|
||||
ps -> ps.bind("WHITE"), row -> {
|
||||
result.add(row.getString(0));
|
||||
}));
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.cassandra.CassandraConnectionFailureException;
|
||||
import org.springframework.data.cassandra.CassandraInvalidQueryException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.ConsistencyLevel;
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
@@ -107,7 +105,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';");
|
||||
CompletableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';");
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -203,8 +201,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<Boolean> future = template.query("UPDATE user SET a = 'b';",
|
||||
(AsyncResultSetExtractor<Boolean>) it -> new AsyncResult<>(it.wasApplied()));
|
||||
CompletableFuture<Boolean> future = template.query("UPDATE user SET a = 'b';",
|
||||
(AsyncResultSetExtractor<Boolean>) it -> CompletableFuture.completedFuture(it.wasApplied()));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -222,7 +220,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.emptyList());
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -240,7 +238,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@@ -250,7 +248,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> null);
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> null);
|
||||
assertThat(getUninterruptibly(future)).isNull();
|
||||
}
|
||||
|
||||
@@ -260,7 +258,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
try {
|
||||
future.get();
|
||||
|
||||
@@ -280,7 +278,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK");
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", String.class);
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user", String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
@@ -294,7 +292,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK", "NOT OK");
|
||||
|
||||
ListenableFuture<List<String>> future = template.queryForList("SELECT * FROM user", String.class);
|
||||
CompletableFuture<List<String>> future = template.queryForList("SELECT * FROM user", String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains("OK", "NOT OK");
|
||||
}
|
||||
@@ -305,7 +303,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';");
|
||||
CompletableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';");
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
}
|
||||
@@ -341,7 +339,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = asyncCqlTemplate
|
||||
CompletableFuture<AsyncResultSet> future = asyncCqlTemplate
|
||||
.queryForResultSet(SimpleStatement.newInstance("SELECT * from USERS"));
|
||||
|
||||
assertThat(getUninterruptibly(future).currentPage()).hasSize(3);
|
||||
@@ -354,8 +352,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<List<String>> future = asyncCqlTemplate.query(SimpleStatement.newInstance("SELECT * from USERS"),
|
||||
(row, index) -> row.getString(0));
|
||||
CompletableFuture<List<String>> future = asyncCqlTemplate
|
||||
.query(SimpleStatement.newInstance("SELECT * from USERS"), (row, index) -> row.getString(0));
|
||||
|
||||
assertThat(getUninterruptibly(future)).hasSize(3).contains("Walter", "Hank", "Jesse");
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
@@ -367,8 +365,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
|
||||
doTestStrings(5, ConsistencyLevel.ONE, null, asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<List<String>> future = asyncCqlTemplate.query(SimpleStatement.newInstance("SELECT * from USERS"),
|
||||
(row, index) -> row.getString(0));
|
||||
CompletableFuture<List<String>> future = asyncCqlTemplate
|
||||
.query(SimpleStatement.newInstance("SELECT * from USERS"), (row, index) -> row.getString(0));
|
||||
|
||||
assertThat(getUninterruptibly(future)).hasSize(3).contains("Walter", "Hank", "Jesse");
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
@@ -381,8 +379,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<Boolean> future = template.query(SimpleStatement.newInstance("UPDATE user SET a = 'b';"),
|
||||
(AsyncResultSetExtractor<Boolean>) rs -> new AsyncResult<>(rs.wasApplied()));
|
||||
CompletableFuture<Boolean> future = template.query(SimpleStatement.newInstance("UPDATE user SET a = 'b';"),
|
||||
(AsyncResultSetExtractor<Boolean>) rs -> CompletableFuture.completedFuture(rs.wasApplied()));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -400,7 +398,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.emptyList());
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
CompletableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> "OK");
|
||||
|
||||
try {
|
||||
@@ -419,7 +417,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
CompletableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> "OK");
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
@@ -430,7 +428,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
CompletableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> null);
|
||||
assertThat(getUninterruptibly(future)).isNull();
|
||||
}
|
||||
@@ -441,7 +439,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
CompletableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> "OK");
|
||||
try {
|
||||
future.get();
|
||||
@@ -462,7 +460,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK");
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
CompletableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
@@ -477,7 +475,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK", "NOT OK");
|
||||
|
||||
ListenableFuture<List<String>> future = template.queryForList(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
CompletableFuture<List<String>> future = template.queryForList(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains("OK", "NOT OK");
|
||||
@@ -489,7 +487,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute(SimpleStatement.newInstance("UPDATE user SET a = 'b';"));
|
||||
CompletableFuture<Boolean> future = template.execute(SimpleStatement.newInstance("UPDATE user SET a = 'b';"));
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
}
|
||||
@@ -503,8 +501,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<CompletionStage<AsyncResultSet>> futureOfFuture = asyncCqlTemplate.execute("SELECT * from USERS",
|
||||
(session, ps) -> session.executeAsync(ps.bind("A")));
|
||||
CompletableFuture<CompletionStage<AsyncResultSet>> futureOfFuture = asyncCqlTemplate
|
||||
.execute("SELECT * from USERS", (session, ps) -> session.executeAsync(ps.bind("A")));
|
||||
|
||||
try {
|
||||
assertThat(getUninterruptibly(futureOfFuture).toCompletableFuture().get().currentPage()).hasSize(3);
|
||||
@@ -522,7 +520,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(this.preparedStatement.bind("White")).thenReturn(this.boundStatement);
|
||||
when(this.resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> applied = asyncCqlTemplate.execute("UPDATE users SET name = ?", "White");
|
||||
CompletableFuture<Boolean> applied = asyncCqlTemplate.execute("UPDATE users SET name = ?", "White");
|
||||
|
||||
assertThat(getUninterruptibly(applied)).isTrue();
|
||||
});
|
||||
@@ -540,8 +538,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
assertThat(e).hasMessageContaining("No node was available");
|
||||
}
|
||||
|
||||
ListenableFuture<CompletionStage<AsyncResultSet>> future = template.execute(
|
||||
session -> AsyncResult.forExecutionException(new NoNodeAvailableException()),
|
||||
CompletableFuture<CompletionStage<AsyncResultSet>> future = template.execute(
|
||||
session -> CompletableFuture.failedFuture(new NoNodeAvailableException()),
|
||||
(session, ps) -> session.executeAsync(boundStatement));
|
||||
|
||||
try {
|
||||
@@ -557,9 +555,10 @@ class AsyncCqlTemplateUnitTests {
|
||||
@Test // DATACASS-292
|
||||
void executePreparedStatementCreatorShouldTranslateStatementCallbackExceptions() throws Exception {
|
||||
|
||||
ListenableFuture<?> future = template.execute(session -> new AsyncResult<>(preparedStatement), (session, ps) -> {
|
||||
throw new NoNodeAvailableException();
|
||||
});
|
||||
CompletableFuture<?> future = template.execute(session -> CompletableFuture.completedFuture(preparedStatement),
|
||||
(session, ps) -> {
|
||||
throw new NoNodeAvailableException();
|
||||
});
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -578,8 +577,9 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<Iterable<Row>> future = template.query(session -> new AsyncResult<>(preparedStatement),
|
||||
(AsyncResultSetExtractor<Iterable<Row>>) rs -> new AsyncResult<>(rs.currentPage()));
|
||||
CompletableFuture<Iterable<Row>> future = template.query(
|
||||
session -> CompletableFuture.completedFuture(preparedStatement),
|
||||
(AsyncResultSetExtractor<Iterable<Row>>) rs -> CompletableFuture.completedFuture(rs.currentPage()));
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains(row);
|
||||
verify(preparedStatement).bind();
|
||||
@@ -591,11 +591,11 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template
|
||||
.query(session -> new AsyncResult<PreparedStatement>(preparedStatement), ps -> {
|
||||
CompletableFuture<AsyncResultSet> future = template
|
||||
.query(session -> CompletableFuture.completedFuture(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) CompletableFuture::completedFuture);
|
||||
|
||||
assertThat(getUninterruptibly(future).currentPage()).contains(row);
|
||||
verify(preparedStatement).bind("a", "b");
|
||||
@@ -604,11 +604,11 @@ class AsyncCqlTemplateUnitTests {
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderShouldTranslatePrepareStatementExceptions() throws Exception {
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template
|
||||
.query(session -> AsyncResult.forExecutionException(new NoNodeAvailableException()), ps -> {
|
||||
CompletableFuture<AsyncResultSet> future = template
|
||||
.query(session -> CompletableFuture.failedFuture(new NoNodeAvailableException()), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) CompletableFuture::completedFuture);
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -622,9 +622,10 @@ class AsyncCqlTemplateUnitTests {
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderShouldTranslateBindExceptions() throws Exception {
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> {
|
||||
throw new NoNodeAvailableException();
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
CompletableFuture<AsyncResultSet> future = template
|
||||
.query(session -> CompletableFuture.completedFuture(preparedStatement), ps -> {
|
||||
throw new NoNodeAvailableException();
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) CompletableFuture::completedFuture);
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -641,10 +642,11 @@ class AsyncCqlTemplateUnitTests {
|
||||
|
||||
when(session.executeAsync(boundStatement)).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
CompletableFuture<AsyncResultSet> future = template
|
||||
.query(session -> CompletableFuture.completedFuture(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) CompletableFuture::completedFuture);
|
||||
|
||||
try {
|
||||
future.get();
|
||||
@@ -660,10 +662,11 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<List<Row>> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (row, rowNum) -> row);
|
||||
CompletableFuture<List<Row>> future = template
|
||||
.query(session -> CompletableFuture.completedFuture(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (row, rowNum) -> row);
|
||||
|
||||
assertThat(getUninterruptibly(future)).hasSize(1).contains(row);
|
||||
verify(preparedStatement).bind("a", "b");
|
||||
@@ -678,7 +681,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.emptyList());
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
(row, rowNum) -> "OK", "Walter");
|
||||
|
||||
try {
|
||||
@@ -700,7 +703,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
(row, rowNum) -> "OK", "Walter");
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
@@ -714,7 +717,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
CompletableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
(row, rowNum) -> "OK", "Walter");
|
||||
try {
|
||||
future.get();
|
||||
@@ -755,8 +758,8 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK", "NOT OK");
|
||||
|
||||
ListenableFuture<List<String>> future = template.queryForList("SELECT * FROM user WHERE username = ?", String.class,
|
||||
"Walter");
|
||||
CompletableFuture<List<String>> future = template.queryForList("SELECT * FROM user WHERE username = ?",
|
||||
String.class, "Walter");
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains("OK", "NOT OK");
|
||||
}
|
||||
@@ -770,7 +773,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute("UPDATE user SET username = ?", "Walter");
|
||||
CompletableFuture<Boolean> future = template.execute("UPDATE user SET username = ?", "Walter");
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
}
|
||||
@@ -780,8 +783,7 @@ class AsyncCqlTemplateUnitTests {
|
||||
}
|
||||
|
||||
private void doTestStrings(@Nullable Integer fetchSize, @Nullable ConsistencyLevel consistencyLevel,
|
||||
@Nullable CqlIdentifier keyspace,
|
||||
Consumer<AsyncCqlTemplate> cqlTemplateConsumer) {
|
||||
@Nullable CqlIdentifier keyspace, Consumer<AsyncCqlTemplate> cqlTemplateConsumer) {
|
||||
|
||||
String[] results = { "Walter", "Hank", "Jesse" };
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
@@ -95,7 +93,7 @@ class AsyncResultStreamUnitTests {
|
||||
|
||||
List<String> rows = new ArrayList<>();
|
||||
|
||||
ListenableFuture<Void> completion = AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum)
|
||||
CompletableFuture<Void> completion = AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum)
|
||||
.forEach(rows::add);
|
||||
|
||||
assertThatThrownBy(completion::get).hasRootCauseInstanceOf(RuntimeException.class);
|
||||
@@ -106,7 +104,7 @@ class AsyncResultStreamUnitTests {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
|
||||
ListenableFuture<List<Row>> collect = AsyncResultStream.from(first).collect(Collectors.toList());
|
||||
CompletableFuture<List<Row>> collect = AsyncResultStream.from(first).collect(Collectors.toList());
|
||||
|
||||
assertThat(collect.get()).containsOnly(row1);
|
||||
}
|
||||
@@ -119,7 +117,7 @@ class AsyncResultStreamUnitTests {
|
||||
when(first.fetchNextPage()).thenReturn(CompletableFuture.completedFuture(last));
|
||||
when(first.hasMorePages()).thenReturn(true);
|
||||
|
||||
ListenableFuture<List<String>> rows = AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum)
|
||||
CompletableFuture<List<String>> rows = AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(rows.get()).containsOnly("row-1", "row-2");
|
||||
@@ -135,7 +133,7 @@ class AsyncResultStreamUnitTests {
|
||||
when(first.fetchNextPage()).thenReturn(failed);
|
||||
when(first.hasMorePages()).thenReturn(true);
|
||||
|
||||
ListenableFuture<List<Row>> collect = AsyncResultStream.from(first).collect(Collectors.toList());
|
||||
CompletableFuture<List<Row>> collect = AsyncResultStream.from(first).collect(Collectors.toList());
|
||||
|
||||
assertThatThrownBy(collect::get).hasRootCauseInstanceOf(RuntimeException.class);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTests;
|
||||
import org.springframework.util.concurrent.CompletableToListenableFutureAdapter;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link AsyncCqlTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class AsyncCqlTemplateIntegrationTests extends AbstractKeyspaceCreatingIntegrationTests {
|
||||
|
||||
private static final AtomicBoolean initialized = new AtomicBoolean();
|
||||
private AsyncCqlTemplate template;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
|
||||
if (initialized.compareAndSet(false, true)) {
|
||||
session.execute("CREATE TABLE IF NOT EXISTS user (id text PRIMARY KEY, username text);");
|
||||
}
|
||||
|
||||
session.execute("TRUNCATE user;");
|
||||
session.execute("INSERT INTO user (id, username) VALUES ('WHITE', 'Walter');");
|
||||
|
||||
template = new AsyncCqlTemplate();
|
||||
template.setSession(getSession());
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeShouldRemoveRecords() {
|
||||
|
||||
getUninterruptibly(template.execute("DELETE FROM user WHERE id = 'WHITE'"));
|
||||
|
||||
assertThat(session.execute("SELECT * FROM user").one()).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryShouldInvokeCallback() {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
getUninterruptibly(template.query("SELECT id FROM user;", row -> {
|
||||
result.add(row.getString(0));
|
||||
}));
|
||||
|
||||
assertThat(result).contains("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectShouldReturnFirstColumn() {
|
||||
|
||||
String id = getUninterruptibly(template.queryForObject("SELECT id FROM user;", String.class));
|
||||
|
||||
assertThat(id).isEqualTo("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectShouldReturnMap() {
|
||||
|
||||
Map<String, Object> map = getUninterruptibly(template.queryForMap("SELECT * FROM user;"));
|
||||
|
||||
assertThat(map).containsEntry("id", "WHITE").containsEntry("username", "Walter");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeStatementShouldRemoveRecords() {
|
||||
|
||||
getUninterruptibly(template.execute(SimpleStatement.newInstance("DELETE FROM user WHERE id = 'WHITE'")));
|
||||
|
||||
assertThat(session.execute("SELECT * FROM user").one()).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryStatementShouldInvokeCallback() {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
getUninterruptibly(template.query(SimpleStatement.newInstance("SELECT id FROM user"), row -> {
|
||||
result.add(row.getString(0));
|
||||
}));
|
||||
|
||||
assertThat(result).contains("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementShouldReturnFirstColumn() {
|
||||
|
||||
String id = getUninterruptibly(
|
||||
template.queryForObject(SimpleStatement.newInstance("SELECT id FROM user"), String.class));
|
||||
|
||||
assertThat(id).isEqualTo("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementShouldReturnMap() {
|
||||
|
||||
Map<String, Object> map = getUninterruptibly(
|
||||
template.queryForMap(SimpleStatement.newInstance("SELECT * FROM user")));
|
||||
|
||||
assertThat(map).containsEntry("id", "WHITE").containsEntry("username", "Walter");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeWithArgsShouldRemoveRecords() {
|
||||
|
||||
getUninterruptibly(template.execute("DELETE FROM user WHERE id = ?", "WHITE"));
|
||||
|
||||
assertThat(session.execute("SELECT * FROM user").one()).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementShouldInvokeCallback() {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
getUninterruptibly(template.query("SELECT id FROM user WHERE id = ?;", row -> {
|
||||
result.add(row.getString(0));
|
||||
}, "WHITE"));
|
||||
|
||||
assertThat(result).contains("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorShouldInvokeCallback() {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
getUninterruptibly(template.query(session -> new CompletableToListenableFutureAdapter<>(
|
||||
session.prepareAsync("SELECT id FROM user WHERE id = ?;")), ps -> ps.bind("WHITE"), row -> {
|
||||
result.add(row.getString(0));
|
||||
}));
|
||||
|
||||
assertThat(result).contains("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectWithArgsShouldReturnFirstColumn() {
|
||||
|
||||
String id = getUninterruptibly(template.queryForObject("SELECT id FROM user WHERE id = ?;", String.class, "WHITE"));
|
||||
|
||||
assertThat(id).isEqualTo("WHITE");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectWithArgsShouldReturnMap() {
|
||||
|
||||
Map<String, Object> map = getUninterruptibly(template.queryForMap("SELECT * FROM user WHERE id = ?;", "WHITE"));
|
||||
|
||||
assertThat(map).containsEntry("id", "WHITE").containsEntry("username", "Walter");
|
||||
}
|
||||
|
||||
private static <T> T getUninterruptibly(Future<T> future) {
|
||||
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,865 @@
|
||||
/*
|
||||
* Copyright 2016-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.cassandra.CassandraConnectionFailureException;
|
||||
import org.springframework.data.cassandra.CassandraInvalidQueryException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.ConsistencyLevel;
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.NoNodeAvailableException;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.BoundStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
|
||||
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AsyncCqlTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class AsyncCqlTemplateUnitTests {
|
||||
|
||||
@Mock CqlSession session;
|
||||
@Mock AsyncResultSet resultSet;
|
||||
@Mock Row row;
|
||||
@Mock PreparedStatement preparedStatement;
|
||||
@Mock BoundStatement boundStatement;
|
||||
@Mock ColumnDefinitions columnDefinitions;
|
||||
|
||||
private AsyncCqlTemplate template;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
|
||||
this.template = new AsyncCqlTemplate();
|
||||
this.template.setSession(session);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests dealing with a plain com.datastax.oss.driver.api.core.CqlSession
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeCallbackShouldTranslateExceptions() {
|
||||
|
||||
try {
|
||||
template.execute((AsyncSessionCallback<String>) session -> {
|
||||
throw new InvalidQueryException(null, "wrong query");
|
||||
});
|
||||
|
||||
fail("Missing CassandraInvalidQueryException");
|
||||
} catch (CassandraInvalidQueryException e) {
|
||||
assertThat(e).hasMessageContaining("wrong query");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeCqlShouldTranslateExceptions() throws Exception {
|
||||
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';");
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasMessageContaining("No node was available");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests dealing with static CQL
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeCqlShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
asyncCqlTemplate.execute("SELECT * from USERS");
|
||||
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-767
|
||||
void executePreparedStatementShouldApplyKeyspace() {
|
||||
|
||||
doTestStrings(null, null, CqlIdentifier.fromCql("ks1"), cqlTemplate -> {
|
||||
cqlTemplate.execute("SELECT * from USERS", (session, ps) -> session.executeAsync(ps.bind("A")));
|
||||
});
|
||||
|
||||
ArgumentCaptor<SimpleStatement> captor = ArgumentCaptor.forClass(SimpleStatement.class);
|
||||
verify(session).prepareAsync(captor.capture());
|
||||
|
||||
SimpleStatement statement = captor.getValue();
|
||||
assertThat(statement.getKeyspace()).isEqualTo(CqlIdentifier.fromCql("ks1"));
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeCqlWithArgumentsShouldCallExecution() {
|
||||
|
||||
doTestStrings(5, ConsistencyLevel.ONE, null, asyncCqlTemplate -> {
|
||||
|
||||
asyncCqlTemplate.execute("SELECT * from USERS");
|
||||
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForResultSetShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
AsyncResultSet resultSet = getUninterruptibly(asyncCqlTemplate.queryForResultSet("SELECT * from USERS"));
|
||||
|
||||
assertThat(resultSet.currentPage()).hasSize(3);
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryWithResultSetExtractorShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
List<String> rows = getUninterruptibly(
|
||||
asyncCqlTemplate.query("SELECT * from USERS", (row, index) -> row.getString(0)));
|
||||
|
||||
assertThat(rows).hasSize(3).contains("Walter", "Hank", "Jesse");
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryWithResultSetExtractorWithArgumentsShouldCallExecution() {
|
||||
|
||||
doTestStrings(5, ConsistencyLevel.ONE, null, asyncCqlTemplate -> {
|
||||
|
||||
List<String> rows = getUninterruptibly(
|
||||
asyncCqlTemplate.query("SELECT * from USERS", (row, index) -> row.getString(0)));
|
||||
|
||||
assertThat(rows).hasSize(3).contains("Walter", "Hank", "Jesse");
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryCqlShouldTranslateExceptions() throws Exception {
|
||||
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<Boolean> future = template.query("UPDATE user SET a = 'b';",
|
||||
(AsyncResultSetExtractor<Boolean>) it -> new AsyncResult<>(it.wasApplied()));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasMessageContaining("No node was available");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectCqlShouldBeEmpty() throws Exception {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.emptyList());
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing IncorrectResultSizeDataAccessException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(EmptyResultDataAccessException.class)
|
||||
.hasMessageContaining("expected 1, actual 0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectCqlShouldReturnRecord() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectCqlShouldReturnNullValue() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> null);
|
||||
assertThat(getUninterruptibly(future)).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectCqlShouldFailReturningManyRecords() throws Exception {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", (row, rowNum) -> "OK");
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing IncorrectResultSizeDataAccessException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(IncorrectResultSizeDataAccessException.class)
|
||||
.hasMessageContaining("expected 1, actual 2");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectCqlWithTypeShouldReturnRecord() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK");
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user", String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForListCqlWithTypeShouldReturnRecord() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK", "NOT OK");
|
||||
|
||||
ListenableFuture<List<String>> future = template.queryForList("SELECT * FROM user", String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains("OK", "NOT OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeCqlShouldReturnWasApplied() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute("UPDATE user SET a = 'b';");
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests dealing with com.datastax.oss.driver.api.core.cql.Statement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeStatementShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
asyncCqlTemplate.execute(SimpleStatement.newInstance("SELECT * from USERS"));
|
||||
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeStatementWithArgumentsShouldCallExecution() {
|
||||
|
||||
doTestStrings(5, ConsistencyLevel.ONE, null, asyncCqlTemplate -> {
|
||||
|
||||
asyncCqlTemplate.execute(SimpleStatement.newInstance("SELECT * from USERS"));
|
||||
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForResultStatementSetShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = asyncCqlTemplate
|
||||
.queryForResultSet(SimpleStatement.newInstance("SELECT * from USERS"));
|
||||
|
||||
assertThat(getUninterruptibly(future).currentPage()).hasSize(3);
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryWithResultSetStatementExtractorShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<List<String>> future = asyncCqlTemplate.query(SimpleStatement.newInstance("SELECT * from USERS"),
|
||||
(row, index) -> row.getString(0));
|
||||
|
||||
assertThat(getUninterruptibly(future)).hasSize(3).contains("Walter", "Hank", "Jesse");
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryWithResultSetStatementExtractorWithArgumentsShouldCallExecution() {
|
||||
|
||||
doTestStrings(5, ConsistencyLevel.ONE, null, asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<List<String>> future = asyncCqlTemplate.query(SimpleStatement.newInstance("SELECT * from USERS"),
|
||||
(row, index) -> row.getString(0));
|
||||
|
||||
assertThat(getUninterruptibly(future)).hasSize(3).contains("Walter", "Hank", "Jesse");
|
||||
verify(session).executeAsync(any(Statement.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryStatementShouldTranslateExceptions() throws Exception {
|
||||
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<Boolean> future = template.query(SimpleStatement.newInstance("UPDATE user SET a = 'b';"),
|
||||
(AsyncResultSetExtractor<Boolean>) rs -> new AsyncResult<>(rs.wasApplied()));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasMessageContaining("No node was available");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementShouldBeEmpty() throws Exception {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.emptyList());
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> "OK");
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing IncorrectResultSizeDataAccessException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(EmptyResultDataAccessException.class)
|
||||
.hasMessageContaining("expected 1, actual 0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementShouldReturnRecord() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> "OK");
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementShouldReturnNullValue() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> null);
|
||||
assertThat(getUninterruptibly(future)).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementShouldFailReturningManyRecords() throws Exception {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
(row, rowNum) -> "OK");
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing IncorrectResultSizeDataAccessException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(IncorrectResultSizeDataAccessException.class)
|
||||
.hasMessageContaining("expected 1, actual 2");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectStatementWithTypeShouldReturnRecord() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK");
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForListStatementWithTypeShouldReturnRecord() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK", "NOT OK");
|
||||
|
||||
ListenableFuture<List<String>> future = template.queryForList(SimpleStatement.newInstance("SELECT * FROM user"),
|
||||
String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains("OK", "NOT OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executeStatementShouldReturnWasApplied() {
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute(SimpleStatement.newInstance("UPDATE user SET a = 'b';"));
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Methods dealing with prepared statements
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementWithCallbackShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
ListenableFuture<CompletionStage<AsyncResultSet>> futureOfFuture = asyncCqlTemplate.execute("SELECT * from USERS",
|
||||
(session, ps) -> session.executeAsync(ps.bind("A")));
|
||||
|
||||
try {
|
||||
assertThat(getUninterruptibly(futureOfFuture).toCompletableFuture().get().currentPage()).hasSize(3);
|
||||
} catch (Exception e) {
|
||||
fail(e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executePreparedStatementWithCallbackShouldCallExecution() {
|
||||
|
||||
doTestStrings(asyncCqlTemplate -> {
|
||||
|
||||
when(this.preparedStatement.bind("White")).thenReturn(this.boundStatement);
|
||||
when(this.resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> applied = asyncCqlTemplate.execute("UPDATE users SET name = ?", "White");
|
||||
|
||||
assertThat(getUninterruptibly(applied)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executePreparedStatementCreatorShouldTranslateStatementCreationExceptions() throws Exception {
|
||||
|
||||
try {
|
||||
template.execute(session -> {
|
||||
throw new NoNodeAvailableException();
|
||||
}, (session, ps) -> session.executeAsync(boundStatement));
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (CassandraConnectionFailureException e) {
|
||||
assertThat(e).hasMessageContaining("No node was available");
|
||||
}
|
||||
|
||||
ListenableFuture<CompletionStage<AsyncResultSet>> future = template.execute(
|
||||
session -> AsyncResult.forExecutionException(new NoNodeAvailableException()),
|
||||
(session, ps) -> session.executeAsync(boundStatement));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasMessageContaining("No node was available");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void executePreparedStatementCreatorShouldTranslateStatementCallbackExceptions() throws Exception {
|
||||
|
||||
ListenableFuture<?> future = template.execute(session -> new AsyncResult<>(preparedStatement), (session, ps) -> {
|
||||
throw new NoNodeAvailableException();
|
||||
});
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasMessageContaining("No node was available");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorShouldReturnResult() {
|
||||
|
||||
when(preparedStatement.bind()).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<Iterable<Row>> future = template.query(session -> new AsyncResult<>(preparedStatement),
|
||||
(AsyncResultSetExtractor<Iterable<Row>>) rs -> new AsyncResult<>(rs.currentPage()));
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains(row);
|
||||
verify(preparedStatement).bind();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderShouldReturnResult() {
|
||||
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template
|
||||
.query(session -> new AsyncResult<PreparedStatement>(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
|
||||
assertThat(getUninterruptibly(future).currentPage()).contains(row);
|
||||
verify(preparedStatement).bind("a", "b");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderShouldTranslatePrepareStatementExceptions() throws Exception {
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template
|
||||
.query(session -> AsyncResult.forExecutionException(new NoNodeAvailableException()), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderShouldTranslateBindExceptions() throws Exception {
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> {
|
||||
throw new NoNodeAvailableException();
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
|
||||
try {
|
||||
future.get();
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderShouldTranslateExecutionExceptions() throws Exception {
|
||||
|
||||
TestResultSetFuture resultSetFuture = TestResultSetFuture.failed(new NoNodeAvailableException());
|
||||
|
||||
when(session.executeAsync(boundStatement)).thenReturn(resultSetFuture);
|
||||
|
||||
ListenableFuture<AsyncResultSet> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (AsyncResultSetExtractor<AsyncResultSet>) AsyncResult::new);
|
||||
|
||||
try {
|
||||
future.get();
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryPreparedStatementCreatorAndBinderAndMapperShouldReturnResult() {
|
||||
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<List<Row>> future = template.query(session -> new AsyncResult<>(preparedStatement), ps -> {
|
||||
ps.bind("a", "b");
|
||||
return boundStatement;
|
||||
}, (row, rowNum) -> row);
|
||||
|
||||
assertThat(getUninterruptibly(future)).hasSize(1).contains(row);
|
||||
verify(preparedStatement).bind("a", "b");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectPreparedStatementShouldBeEmpty() throws Exception {
|
||||
|
||||
when(session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(preparedStatement));
|
||||
when(preparedStatement.bind("Walter")).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.emptyList());
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
(row, rowNum) -> "OK", "Walter");
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing IncorrectResultSizeDataAccessException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(EmptyResultDataAccessException.class)
|
||||
.hasMessageContaining("expected 1, actual 0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectPreparedStatementShouldReturnRecord() {
|
||||
|
||||
when(session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(preparedStatement));
|
||||
when(preparedStatement.bind("Walter")).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
(row, rowNum) -> "OK", "Walter");
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectPreparedStatementShouldFailReturningManyRecords() throws Exception {
|
||||
|
||||
when(session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(preparedStatement));
|
||||
when(preparedStatement.bind("Walter")).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
|
||||
ListenableFuture<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?",
|
||||
(row, rowNum) -> "OK", "Walter");
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing IncorrectResultSizeDataAccessException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(IncorrectResultSizeDataAccessException.class)
|
||||
.hasMessageContaining("expected 1, actual 2");
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForObjectPreparedStatementWithTypeShouldReturnRecord() {
|
||||
|
||||
when(session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(preparedStatement));
|
||||
when(preparedStatement.bind("Walter")).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK");
|
||||
|
||||
Future<String> future = template.queryForObject("SELECT * FROM user WHERE username = ?", String.class, "Walter");
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo("OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void queryForListPreparedStatementWithTypeShouldReturnRecord() {
|
||||
|
||||
when(session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(preparedStatement));
|
||||
when(preparedStatement.bind("Walter")).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.currentPage()).thenReturn(Arrays.asList(row, row));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
when(row.getString(0)).thenReturn("OK", "NOT OK");
|
||||
|
||||
ListenableFuture<List<String>> future = template.queryForList("SELECT * FROM user WHERE username = ?", String.class,
|
||||
"Walter");
|
||||
|
||||
assertThat(getUninterruptibly(future)).contains("OK", "NOT OK");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void updatePreparedStatementShouldReturnApplied() {
|
||||
|
||||
when(session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(preparedStatement));
|
||||
when(preparedStatement.bind("Walter")).thenReturn(boundStatement);
|
||||
when(session.executeAsync(boundStatement)).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
ListenableFuture<Boolean> future = template.execute("UPDATE user SET username = ?", "Walter");
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
}
|
||||
|
||||
private void doTestStrings(Consumer<AsyncCqlTemplate> cqlTemplateConsumer) {
|
||||
doTestStrings(null, null, null, cqlTemplateConsumer);
|
||||
}
|
||||
|
||||
private void doTestStrings(@Nullable Integer fetchSize, @Nullable ConsistencyLevel consistencyLevel,
|
||||
@Nullable CqlIdentifier keyspace, Consumer<AsyncCqlTemplate> cqlTemplateConsumer) {
|
||||
|
||||
String[] results = { "Walter", "Hank", "Jesse" };
|
||||
|
||||
when(this.session.executeAsync((Statement) any())).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(this.resultSet.currentPage()).thenReturn(Arrays.asList(row, row, row));
|
||||
when(this.row.getString(0)).thenReturn(results[0], results[1], results[2]);
|
||||
when(this.session.prepareAsync(anyString())).thenReturn(new TestPreparedStatementFuture(this.preparedStatement));
|
||||
when(this.session.prepareAsync(any(SimpleStatement.class)))
|
||||
.thenReturn(new TestPreparedStatementFuture(this.preparedStatement));
|
||||
|
||||
AsyncCqlTemplate template = new AsyncCqlTemplate();
|
||||
template.setSession(this.session);
|
||||
|
||||
if (fetchSize != null) {
|
||||
template.setFetchSize(fetchSize);
|
||||
}
|
||||
|
||||
if (consistencyLevel != null) {
|
||||
template.setConsistencyLevel(consistencyLevel);
|
||||
}
|
||||
|
||||
if (keyspace != null) {
|
||||
template.setKeyspace(keyspace);
|
||||
}
|
||||
|
||||
cqlTemplateConsumer.accept(template);
|
||||
|
||||
ArgumentCaptor<Statement> statementArgumentCaptor = ArgumentCaptor.forClass(Statement.class);
|
||||
verify(this.session).executeAsync(statementArgumentCaptor.capture());
|
||||
|
||||
Statement statement = statementArgumentCaptor.getValue();
|
||||
|
||||
if (fetchSize != null) {
|
||||
assertThat(statement.getPageSize()).isEqualTo(fetchSize.intValue());
|
||||
}
|
||||
|
||||
if (consistencyLevel != null) {
|
||||
assertThat(statement.getConsistencyLevel()).isEqualTo(consistencyLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T getUninterruptibly(Future<T> future) {
|
||||
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestResultSetFuture extends CompletableFuture<AsyncResultSet> {
|
||||
|
||||
private TestResultSetFuture() {}
|
||||
|
||||
private TestResultSetFuture(AsyncResultSet result) {
|
||||
complete(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a completed future that reports a failure given {@link Throwable}.
|
||||
*
|
||||
* @param throwable must not be {@literal null}.
|
||||
* @return the completed/failed {@link TestResultSetFuture}.
|
||||
*/
|
||||
private static TestResultSetFuture failed(Throwable throwable) {
|
||||
|
||||
TestResultSetFuture future = new TestResultSetFuture();
|
||||
future.completeExceptionally(throwable);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestPreparedStatementFuture extends CompletableFuture<PreparedStatement> {
|
||||
|
||||
public TestPreparedStatementFuture() {}
|
||||
|
||||
private TestPreparedStatementFuture(PreparedStatement ps) {
|
||||
complete(ps);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2019-2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.cql.legacy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AsyncResultStream}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class AsyncResultStreamUnitTests {
|
||||
|
||||
private AsyncResultSet first = mock(AsyncResultSet.class);
|
||||
private AsyncResultSet last = mock(AsyncResultSet.class);
|
||||
private Row row1 = mock(Row.class);
|
||||
private Row row2 = mock(Row.class);
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldIterateFirstPage() {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
|
||||
List<Row> rows = new ArrayList<>();
|
||||
|
||||
AsyncResultStream.from(first).forEach(rows::add);
|
||||
|
||||
assertThat(rows).containsOnly(row1);
|
||||
}
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldIterateMappedFirstPage() {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
|
||||
List<String> rows = new ArrayList<>();
|
||||
|
||||
AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum).forEach(rows::add);
|
||||
|
||||
assertThat(rows).containsOnly("row-1");
|
||||
}
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldIterateMappedPages() {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
when(last.currentPage()).thenReturn(Collections.singletonList(row2));
|
||||
when(first.fetchNextPage()).thenReturn(CompletableFuture.completedFuture(last));
|
||||
when(first.hasMorePages()).thenReturn(true);
|
||||
|
||||
List<String> rows = new ArrayList<>();
|
||||
|
||||
AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum).forEach(rows::add);
|
||||
|
||||
assertThat(rows).containsOnly("row-1", "row-2");
|
||||
}
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldPropagateExceptionOnIterate() {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
|
||||
CompletableFuture<AsyncResultSet> failed = new CompletableFuture<>();
|
||||
failed.completeExceptionally(new RuntimeException("boo"));
|
||||
when(first.fetchNextPage()).thenReturn(failed);
|
||||
when(first.hasMorePages()).thenReturn(true);
|
||||
|
||||
List<String> rows = new ArrayList<>();
|
||||
|
||||
ListenableFuture<Void> completion = AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum)
|
||||
.forEach(rows::add);
|
||||
|
||||
assertThatThrownBy(completion::get).hasRootCauseInstanceOf(RuntimeException.class);
|
||||
}
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldCollectFirstPage() throws ExecutionException, InterruptedException {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
|
||||
ListenableFuture<List<Row>> collect = AsyncResultStream.from(first).collect(Collectors.toList());
|
||||
|
||||
assertThat(collect.get()).containsOnly(row1);
|
||||
}
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldCollectMappedPages() throws ExecutionException, InterruptedException {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
when(last.currentPage()).thenReturn(Collections.singletonList(row2));
|
||||
when(first.fetchNextPage()).thenReturn(CompletableFuture.completedFuture(last));
|
||||
when(first.hasMorePages()).thenReturn(true);
|
||||
|
||||
ListenableFuture<List<String>> rows = AsyncResultStream.from(first).map((row, rowNum) -> "row-" + rowNum)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(rows.get()).containsOnly("row-1", "row-2");
|
||||
}
|
||||
|
||||
@Test // DATACASS-656
|
||||
void shouldPropagateExceptionOnCollect() {
|
||||
|
||||
when(first.currentPage()).thenReturn(Collections.singletonList(row1));
|
||||
|
||||
CompletableFuture<AsyncResultSet> failed = new CompletableFuture<>();
|
||||
failed.completeExceptionally(new RuntimeException("boo"));
|
||||
when(first.fetchNextPage()).thenReturn(failed);
|
||||
when(first.hasMorePages()).thenReturn(true);
|
||||
|
||||
ListenableFuture<List<Row>> collect = AsyncResultStream.from(first).collect(Collectors.toList());
|
||||
|
||||
assertThatThrownBy(collect::get).hasRootCauseInstanceOf(RuntimeException.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.cassandra.core.query.Criteria.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.cassandra.core.CassandraTemplate;
|
||||
import org.springframework.data.cassandra.core.DeleteOptions;
|
||||
import org.springframework.data.cassandra.core.InsertOptions;
|
||||
import org.springframework.data.cassandra.core.UpdateOptions;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.cql.legacy.AsyncCqlTemplate;
|
||||
import org.springframework.data.cassandra.core.query.CassandraPageRequest;
|
||||
import org.springframework.data.cassandra.core.query.Columns;
|
||||
import org.springframework.data.cassandra.core.query.Query;
|
||||
import org.springframework.data.cassandra.core.query.Update;
|
||||
import org.springframework.data.cassandra.domain.User;
|
||||
import org.springframework.data.cassandra.domain.UserToken;
|
||||
import org.springframework.data.cassandra.repository.support.SchemaTestUtils;
|
||||
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTests;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link AsyncCassandraTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingIntegrationTests {
|
||||
|
||||
private AsyncCassandraTemplate template;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
MappingCassandraConverter converter = new MappingCassandraConverter();
|
||||
CassandraTemplate cassandraTemplate = new CassandraTemplate(session, converter);
|
||||
template = new AsyncCassandraTemplate(new AsyncCqlTemplate(session), converter);
|
||||
prepareTemplate(template);
|
||||
|
||||
SchemaTestUtils.potentiallyCreateTableFor(User.class, cassandraTemplate);
|
||||
SchemaTestUtils.potentiallyCreateTableFor(UserToken.class, cassandraTemplate);
|
||||
SchemaTestUtils.truncate(User.class, cassandraTemplate);
|
||||
SchemaTestUtils.truncate(UserToken.class, cassandraTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the {@link AsyncCassandraTemplate} before running the tests.
|
||||
*
|
||||
* @param template
|
||||
*/
|
||||
void prepareTemplate(AsyncCassandraTemplate template) {
|
||||
template.setUsePreparedStatements(false);
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
void shouldSelectByQueryWithSorting() {
|
||||
|
||||
UserToken token1 = new UserToken();
|
||||
token1.setUserId(Uuids.endOf(System.currentTimeMillis()));
|
||||
token1.setToken(Uuids.startOf(System.currentTimeMillis()));
|
||||
token1.setUserComment("foo");
|
||||
|
||||
UserToken token2 = new UserToken();
|
||||
token2.setUserId(token1.getUserId());
|
||||
token2.setToken(Uuids.endOf(System.currentTimeMillis() + 100));
|
||||
token2.setUserComment("bar");
|
||||
|
||||
getUninterruptibly(template.insert(token1));
|
||||
getUninterruptibly(template.insert(token2));
|
||||
|
||||
Query query = Query.query(where("userId").is(token1.getUserId())).sort(Sort.by("token"));
|
||||
|
||||
assertThat(getUninterruptibly(template.select(query, UserToken.class))).containsSequence(token1, token2);
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
void shouldSelectOneByQuery() {
|
||||
|
||||
UserToken token1 = new UserToken();
|
||||
token1.setUserId(Uuids.endOf(System.currentTimeMillis()));
|
||||
token1.setToken(Uuids.startOf(System.currentTimeMillis()));
|
||||
token1.setUserComment("foo");
|
||||
|
||||
getUninterruptibly(template.insert(token1));
|
||||
|
||||
Query query = Query.query(where("userId").is(token1.getUserId()));
|
||||
|
||||
assertThat(getUninterruptibly(template.selectOne(query, UserToken.class))).isEqualTo(token1);
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void insertShouldInsertEntity() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
|
||||
ListenableFuture<User> insert = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(insert)).isEqualTo(user);
|
||||
assertThat(getUser(user.getId())).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
void insertShouldCreateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<org.springframework.data.cassandra.core.EntityWriteResult<User>> inserted = template.insert(user,
|
||||
lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(inserted).wasApplied()).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACASS-250
|
||||
void insertShouldNotUpdateEntityWithLwt() {
|
||||
|
||||
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
getUninterruptibly(template.insert(user, lwtOptions));
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
ListenableFuture<org.springframework.data.cassandra.core.EntityWriteResult<User>> lwt = template.insert(user,
|
||||
lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(lwt).wasApplied()).isFalse();
|
||||
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void shouldInsertAndCountEntities() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
User result = getUninterruptibly(template.insert(user));
|
||||
|
||||
ListenableFuture<Long> count = template.count(User.class);
|
||||
assertThat(result).isSameAs(user);
|
||||
assertThat(getUninterruptibly(count)).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test // DATACASS-512
|
||||
void shouldInsertEntityAndCountByQuery() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
assertThat(getUninterruptibly(template.count(Query.query(where("id").is("heisenberg")), User.class))).isOne();
|
||||
assertThat(getUninterruptibly(template.count(Query.query(where("id").is("foo")), User.class))).isZero();
|
||||
}
|
||||
|
||||
@Test // DATACASS-512
|
||||
void shouldInsertEntityAndExistsByQuery() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
assertThat(getUninterruptibly(template.exists(Query.query(where("id").is("heisenberg")), User.class))).isTrue();
|
||||
assertThat(getUninterruptibly(template.exists(Query.query(where("id").is("foo")), User.class))).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void updateShouldUpdateEntity() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
User updated = getUninterruptibly(template.update(user));
|
||||
|
||||
assertThat(updated).isNotNull();
|
||||
assertThat(getUser(user.getId())).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void updateShouldNotCreateEntityWithLwt() {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<org.springframework.data.cassandra.core.EntityWriteResult<User>> lwt = template.update(user,
|
||||
lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(lwt).wasApplied()).isFalse();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void updateShouldUpdateEntityWithLwt() throws InterruptedException {
|
||||
|
||||
UpdateOptions lwtOptions = UpdateOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
user.setFirstname("Walter Hartwell");
|
||||
|
||||
ListenableFuture<org.springframework.data.cassandra.core.EntityWriteResult<User>> updated = template.update(user,
|
||||
lwtOptions);
|
||||
|
||||
assertThat(getUninterruptibly(updated).wasApplied()).isTrue();
|
||||
assertThat(getUninterruptibly(updated).getEntity()).isSameAs(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
void updateShouldUpdateEntityByQuery() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
Query query = Query.query(where("id").is("heisenberg"));
|
||||
boolean result = getUninterruptibly(
|
||||
template.update(query, Update.empty().set("firstname", "Walter Hartwell"), User.class));
|
||||
assertThat(result).isTrue();
|
||||
|
||||
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter Hartwell");
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
void deleteByQueryShouldRemoveEntity() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
Query query = Query.query(where("id").is("heisenberg"));
|
||||
assertThat(getUninterruptibly(template.delete(query, User.class))).isTrue();
|
||||
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-343
|
||||
void deleteColumnsByQueryShouldRemoveColumn() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
Query query = Query.query(where("id").is("heisenberg")).columns(Columns.from("lastname"));
|
||||
|
||||
assertThat(getUninterruptibly(template.delete(query, User.class))).isTrue();
|
||||
|
||||
User loaded = getUser(user.getId());
|
||||
assertThat(loaded.getFirstname()).isEqualTo("Walter");
|
||||
assertThat(loaded.getLastname()).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void deleteShouldRemoveEntity() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
User deleted = getUninterruptibly(template.delete(user));
|
||||
|
||||
assertThat(deleted).isNotNull();
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void deleteByIdShouldRemoveEntity() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
Boolean deleted = getUninterruptibly(template.deleteById(user.getId(), User.class));
|
||||
assertThat(deleted).isTrue();
|
||||
|
||||
assertThat(getUser(user.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-606
|
||||
void deleteShouldRemoveEntityWithLwt() {
|
||||
|
||||
DeleteOptions lwtOptions = DeleteOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
assertThat(getUninterruptibly(template.delete(user, lwtOptions)).wasApplied()).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATACASS-606
|
||||
void deleteByQueryShouldRemoveEntityWithLwt() {
|
||||
|
||||
DeleteOptions lwtOptions = DeleteOptions.builder().withIfExists().build();
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
getUninterruptibly(template.insert(user));
|
||||
|
||||
Query query = Query.query(where("id").is("heisenberg")).queryOptions(lwtOptions);
|
||||
assertThat(getUninterruptibly(template.delete(query, User.class))).isTrue();
|
||||
assertThat(getUninterruptibly(template.delete(query, User.class))).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACASS-56
|
||||
void shouldPageRequests() {
|
||||
|
||||
Set<String> expectedIds = new LinkedHashSet<>();
|
||||
|
||||
for (int count = 0; count < 100; count++) {
|
||||
User user = new User("heisenberg" + count, "Walter", "White");
|
||||
expectedIds.add(user.getId());
|
||||
getUninterruptibly(template.insert(user));
|
||||
}
|
||||
|
||||
Set<String> ids = new HashSet<>();
|
||||
|
||||
Query query = Query.empty();
|
||||
|
||||
Slice<User> slice = getUninterruptibly(
|
||||
template.slice(query.pageRequest(CassandraPageRequest.first(10)), User.class));
|
||||
|
||||
int iterations = 0;
|
||||
|
||||
do {
|
||||
|
||||
iterations++;
|
||||
|
||||
assertThat(slice).hasSize(10);
|
||||
|
||||
slice.stream().map(User::getId).forEach(ids::add);
|
||||
|
||||
if (slice.hasNext()) {
|
||||
slice = getUninterruptibly(template.slice(query.pageRequest(slice.nextPageable()), User.class));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (!slice.getContent().isEmpty());
|
||||
|
||||
assertThat(ids).containsAll(expectedIds);
|
||||
assertThat(iterations).isEqualTo(10);
|
||||
}
|
||||
|
||||
private User getUser(String id) {
|
||||
return getUninterruptibly(template.selectOneById(id, User.class));
|
||||
}
|
||||
|
||||
private static <T> T getUninterruptibly(Future<T> future) {
|
||||
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception cause) {
|
||||
throw new IllegalStateException(cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* https://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.data.cassandra.core.legacy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.cassandra.core.query.Criteria.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import org.springframework.data.cassandra.CassandraConnectionFailureException;
|
||||
import org.springframework.data.cassandra.core.DeleteOptions;
|
||||
import org.springframework.data.cassandra.core.UpdateOptions;
|
||||
import org.springframework.data.cassandra.core.mapping.event.BeforeConvertCallback;
|
||||
import org.springframework.data.cassandra.core.mapping.event.BeforeSaveCallback;
|
||||
import org.springframework.data.cassandra.core.query.Filter;
|
||||
import org.springframework.data.cassandra.core.query.Query;
|
||||
import org.springframework.data.cassandra.core.query.Update;
|
||||
import org.springframework.data.cassandra.domain.User;
|
||||
import org.springframework.data.cassandra.domain.VersionedUser;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlIdentifier;
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.NoNodeAvailableException;
|
||||
import com.datastax.oss.driver.api.core.context.DriverContext;
|
||||
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
|
||||
import com.datastax.oss.driver.api.core.cql.ColumnDefinition;
|
||||
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
|
||||
import com.datastax.oss.driver.api.core.cql.Row;
|
||||
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
|
||||
import com.datastax.oss.driver.api.core.cql.Statement;
|
||||
import com.datastax.oss.driver.api.core.type.DataTypes;
|
||||
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
|
||||
import com.datastax.oss.driver.internal.core.type.codec.registry.DefaultCodecRegistry;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AsyncCassandraTemplate}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class AsyncCassandraTemplateUnitTests {
|
||||
|
||||
@Mock CqlSession session;
|
||||
CodecRegistry codecRegistry = new DefaultCodecRegistry("foo");
|
||||
@Mock DriverContext driverContext;
|
||||
@Mock AsyncResultSet resultSet;
|
||||
@Mock Row row;
|
||||
@Mock ColumnDefinition columnDefinition;
|
||||
@Mock ColumnDefinitions columnDefinitions;
|
||||
|
||||
@Captor ArgumentCaptor<SimpleStatement> statementCaptor;
|
||||
|
||||
private AsyncCassandraTemplate template;
|
||||
|
||||
private Object beforeSave;
|
||||
|
||||
private Object beforeConvert;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
when(driverContext.getCodecRegistry()).thenReturn(codecRegistry);
|
||||
when(session.getContext()).thenReturn(driverContext);
|
||||
|
||||
when(session.executeAsync(any(Statement.class))).thenReturn(new TestResultSetFuture(resultSet));
|
||||
when(row.getColumnDefinitions()).thenReturn(columnDefinitions);
|
||||
|
||||
EntityCallbacks callbacks = EntityCallbacks.create();
|
||||
callbacks.addEntityCallback((BeforeSaveCallback<Object>) (entity, tableName, statement) -> {
|
||||
|
||||
assertThat(tableName).isNotNull();
|
||||
assertThat(statement).isNotNull();
|
||||
beforeSave = entity;
|
||||
return entity;
|
||||
});
|
||||
|
||||
callbacks.addEntityCallback((BeforeConvertCallback<Object>) (entity, tableName) -> {
|
||||
|
||||
assertThat(tableName).isNotNull();
|
||||
beforeConvert = entity;
|
||||
return entity;
|
||||
});
|
||||
|
||||
template = new AsyncCassandraTemplate(session);
|
||||
template.setUsePreparedStatements(false);
|
||||
template.setEntityCallbacks(callbacks);
|
||||
}
|
||||
|
||||
@Test // gh-1133
|
||||
void shouldConfigureConverterFromSession() {
|
||||
assertThat(template.getConverter().getCodecRegistry()).isEqualTo(session.getContext().getCodecRegistry());
|
||||
assertThat(template.getConverter()).extracting("userTypeResolver").isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void selectUsingCqlShouldReturnMappedResults() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(columnDefinitions.contains(any(CqlIdentifier.class))).thenReturn(true);
|
||||
|
||||
when(columnDefinitions.get(anyInt())).thenReturn(columnDefinition);
|
||||
when(columnDefinitions.firstIndexOf("id")).thenReturn(0);
|
||||
when(columnDefinitions.firstIndexOf("firstname")).thenReturn(1);
|
||||
when(columnDefinitions.firstIndexOf("lastname")).thenReturn(2);
|
||||
|
||||
when(columnDefinition.getType()).thenReturn(DataTypes.TEXT);
|
||||
|
||||
when(row.getObject(0)).thenReturn("myid");
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
ListenableFuture<List<User>> list = template.select("SELECT * FROM users", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(list)).hasSize(1).contains(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void selectUsingCqlShouldInvokeCallbackWithMappedResults() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singletonList(row));
|
||||
when(columnDefinitions.contains(any(CqlIdentifier.class))).thenReturn(true);
|
||||
when(columnDefinitions.get(anyInt())).thenReturn(columnDefinition);
|
||||
when(columnDefinitions.firstIndexOf("id")).thenReturn(0);
|
||||
when(columnDefinitions.firstIndexOf("firstname")).thenReturn(1);
|
||||
when(columnDefinitions.firstIndexOf("lastname")).thenReturn(2);
|
||||
|
||||
when(columnDefinition.getType()).thenReturn(DataTypes.TEXT);
|
||||
|
||||
when(row.getObject(0)).thenReturn("myid");
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
List<User> list = new ArrayList<>();
|
||||
|
||||
ListenableFuture<Void> result = template.select("SELECT * FROM users", list::add, User.class);
|
||||
|
||||
assertThat(getUninterruptibly(result)).isNull();
|
||||
assertThat(list).hasSize(1).contains(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void selectShouldTranslateException() throws Exception {
|
||||
|
||||
when(resultSet.currentPage()).thenThrow(new NoNodeAvailableException());
|
||||
|
||||
ListenableFuture<List<User>> list = template.select("SELECT * FROM users", User.class);
|
||||
|
||||
try {
|
||||
list.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasRootCauseInstanceOf(NoNodeAvailableException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void selectOneShouldReturnMappedResults() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(columnDefinitions.contains(any(CqlIdentifier.class))).thenReturn(true);
|
||||
|
||||
when(columnDefinitions.get(anyInt())).thenReturn(columnDefinition);
|
||||
when(columnDefinitions.firstIndexOf("id")).thenReturn(0);
|
||||
when(columnDefinitions.firstIndexOf("firstname")).thenReturn(1);
|
||||
when(columnDefinitions.firstIndexOf("lastname")).thenReturn(2);
|
||||
|
||||
when(columnDefinition.getType()).thenReturn(DataTypes.TEXT);
|
||||
|
||||
when(row.getObject(0)).thenReturn("myid");
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
ListenableFuture<User> future = template.selectOne("SELECT * FROM users WHERE id='myid'", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void selectOneByIdShouldReturnMappedResults() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(columnDefinitions.contains(any(CqlIdentifier.class))).thenReturn(true);
|
||||
when(columnDefinitions.get(anyInt())).thenReturn(columnDefinition);
|
||||
when(columnDefinitions.firstIndexOf("id")).thenReturn(0);
|
||||
when(columnDefinitions.firstIndexOf("firstname")).thenReturn(1);
|
||||
when(columnDefinitions.firstIndexOf("lastname")).thenReturn(2);
|
||||
|
||||
when(columnDefinition.getType()).thenReturn(DataTypes.ASCII);
|
||||
|
||||
when(row.getObject(0)).thenReturn("myid");
|
||||
when(row.getObject(1)).thenReturn("Walter");
|
||||
when(row.getObject(2)).thenReturn("White");
|
||||
|
||||
ListenableFuture<User> future = template.selectOneById("myid", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(new User("myid", "Walter", "White"));
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
|
||||
}
|
||||
|
||||
@Test // DATACASS-696
|
||||
void selectOneShouldNull() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
|
||||
ListenableFuture<String> future = template.selectOne("SELECT id FROM users WHERE id='myid'", String.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isNull();
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void existsShouldReturnExistingElement() {
|
||||
|
||||
when(resultSet.one()).thenReturn(row);
|
||||
|
||||
ListenableFuture<Boolean> future = template.exists("myid", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void existsShouldReturnNonExistingElement() {
|
||||
|
||||
ListenableFuture<Boolean> future = template.exists("myid", User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isFalse();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users WHERE id='myid' LIMIT 1");
|
||||
}
|
||||
|
||||
@Test // DATACASS-512
|
||||
void existsByQueryShouldReturnExistingElement() {
|
||||
|
||||
when(resultSet.one()).thenReturn(row);
|
||||
|
||||
ListenableFuture<Boolean> future = template.exists(Query.empty(), User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT * FROM users LIMIT 1");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void countShouldExecuteCountQueryElement() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(row.getLong(0)).thenReturn(42L);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
|
||||
ListenableFuture<Long> future = template.count(User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(42L);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void countByQueryShouldExecuteCountQueryElement() {
|
||||
|
||||
when(resultSet.currentPage()).thenReturn(Collections.singleton(row));
|
||||
when(row.getLong(0)).thenReturn(42L);
|
||||
when(columnDefinitions.size()).thenReturn(1);
|
||||
|
||||
ListenableFuture<Long> future = template.count(Query.empty(), User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(42L);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("SELECT count(1) FROM users");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292, DATACASS-618
|
||||
void insertShouldInsertEntity() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> future = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White')");
|
||||
assertThat(beforeConvert).isSameAs(user);
|
||||
assertThat(beforeSave).isSameAs(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-618
|
||||
void insertShouldInsertVersionedEntity() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
VersionedUser user = new VersionedUser("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<VersionedUser> future = template.insert(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo(
|
||||
"INSERT INTO vusers (firstname,id,lastname,version) VALUES ('Walter','heisenberg','White',0) IF NOT EXISTS");
|
||||
assertThat(beforeConvert).isSameAs(user);
|
||||
assertThat(beforeSave).isSameAs(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void insertShouldTranslateException() throws Exception {
|
||||
|
||||
reset(session);
|
||||
when(session.executeAsync(any(Statement.class)))
|
||||
.thenReturn(TestResultSetFuture.failed(new NoNodeAvailableException()));
|
||||
|
||||
ListenableFuture<User> future = template.insert(new User("heisenberg", "Walter", "White"));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasRootCauseInstanceOf(NoNodeAvailableException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292, DATACASS-618
|
||||
void updateShouldUpdateEntity() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> future = template.update(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg'");
|
||||
assertThat(beforeConvert).isSameAs(user);
|
||||
assertThat(beforeSave).isSameAs(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-618
|
||||
void updateShouldUpdateVersionedEntity() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
VersionedUser user = new VersionedUser("heisenberg", "Walter", "White");
|
||||
user.setVersion(0L);
|
||||
|
||||
ListenableFuture<VersionedUser> future = template.update(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
SimpleStatement value = statementCaptor.getValue();
|
||||
assertThat(render(value)).isEqualTo(
|
||||
"UPDATE vusers SET firstname='Walter', lastname='White', version=1 WHERE id='heisenberg' IF version=0");
|
||||
assertThat(beforeConvert).isSameAs(user);
|
||||
assertThat(beforeSave).isSameAs(user);
|
||||
}
|
||||
|
||||
@Test // DATACASS-575
|
||||
void updateShouldUpdateEntityWithOptions() {
|
||||
|
||||
UpdateOptions updateOptions = UpdateOptions.builder().withIfExists().build();
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
template.update(user, updateOptions);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF EXISTS");
|
||||
}
|
||||
|
||||
@Test // DATACASS-575
|
||||
void updateShouldUpdateEntityWithLwt() {
|
||||
|
||||
UpdateOptions options = UpdateOptions.builder().ifCondition(where("firstname").is("Walter")).build();
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
template.update(user, options);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("UPDATE users SET firstname='Walter', lastname='White' WHERE id='heisenberg' IF firstname='Walter'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-575
|
||||
void updateShouldApplyUpdateQuery() {
|
||||
|
||||
Query query = Query.query(where("id").is("heisenberg"));
|
||||
Update update = Update.update("firstname", "Walter");
|
||||
|
||||
template.update(query, update, User.class);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("UPDATE users SET firstname='Walter' WHERE id='heisenberg'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-575
|
||||
void updateShouldApplyUpdateQueryWitLwt() {
|
||||
|
||||
Filter ifCondition = Filter.from(where("firstname").is("Walter"), where("lastname").is("White"));
|
||||
|
||||
Query query = Query.query(where("id").is("heisenberg"))
|
||||
.queryOptions(UpdateOptions.builder().ifCondition(ifCondition).build());
|
||||
|
||||
Update update = Update.update("firstname", "Walter");
|
||||
|
||||
template.update(query, update, User.class);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo(
|
||||
"UPDATE users SET firstname='Walter' WHERE id='heisenberg' IF firstname='Walter' AND lastname='White'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void updateShouldTranslateException() throws Exception {
|
||||
|
||||
reset(session);
|
||||
when(session.executeAsync(any(Statement.class)))
|
||||
.thenReturn(TestResultSetFuture.failed(new NoNodeAvailableException()));
|
||||
|
||||
ListenableFuture<User> future = template.update(new User("heisenberg", "Walter", "White"));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasRootCauseInstanceOf(NoNodeAvailableException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void deleteByIdShouldRemoveEntity() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<Boolean> future = template.deleteById(user.getId(), User.class);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isTrue();
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void deleteShouldRemoveEntity() {
|
||||
|
||||
when(resultSet.wasApplied()).thenReturn(true);
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
|
||||
ListenableFuture<User> future = template.delete(user);
|
||||
|
||||
assertThat(getUninterruptibly(future)).isEqualTo(user);
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("DELETE FROM users WHERE id='heisenberg'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-575
|
||||
void deleteShouldRemoveEntityWithLwt() {
|
||||
|
||||
User user = new User("heisenberg", "Walter", "White");
|
||||
DeleteOptions options = DeleteOptions.builder().ifCondition(where("firstname").is("Walter")).build();
|
||||
|
||||
template.delete(user, options);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-575
|
||||
void deleteShouldRemoveByQueryWithLwt() {
|
||||
|
||||
DeleteOptions options = DeleteOptions.builder().ifCondition(where("firstname").is("Walter")).build();
|
||||
Query query = Query.query(where("id").is("heisenberg")).queryOptions(options);
|
||||
|
||||
template.delete(query, User.class);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue()))
|
||||
.isEqualTo("DELETE FROM users WHERE id='heisenberg' IF firstname='Walter'");
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void deleteShouldTranslateException() throws Exception {
|
||||
|
||||
reset(session);
|
||||
when(session.executeAsync(any(Statement.class)))
|
||||
.thenReturn(TestResultSetFuture.failed(new NoNodeAvailableException()));
|
||||
|
||||
ListenableFuture<User> future = template.delete(new User("heisenberg", "Walter", "White"));
|
||||
|
||||
try {
|
||||
future.get();
|
||||
|
||||
fail("Missing CassandraConnectionFailureException");
|
||||
} catch (ExecutionException e) {
|
||||
assertThat(e).hasCauseInstanceOf(CassandraConnectionFailureException.class)
|
||||
.hasRootCauseInstanceOf(NoNodeAvailableException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATACASS-292
|
||||
void truncateShouldRemoveEntities() {
|
||||
|
||||
template.truncate(User.class);
|
||||
|
||||
verify(session).executeAsync(statementCaptor.capture());
|
||||
assertThat(render(statementCaptor.getValue())).isEqualTo("TRUNCATE users");
|
||||
}
|
||||
|
||||
private static String render(SimpleStatement statement) {
|
||||
|
||||
String query = statement.getQuery();
|
||||
List<Object> positionalValues = statement.getPositionalValues();
|
||||
for (Object positionalValue : positionalValues) {
|
||||
|
||||
query = query.replaceFirst("\\?",
|
||||
positionalValue != null
|
||||
? CodecRegistry.DEFAULT.codecFor((Class) positionalValue.getClass()).format(positionalValue)
|
||||
: "NULL");
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private static <T> T getUninterruptibly(Future<T> future) {
|
||||
|
||||
try {
|
||||
return future.get();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestResultSetFuture extends CompletableFuture<AsyncResultSet> {
|
||||
|
||||
private TestResultSetFuture() {}
|
||||
|
||||
private TestResultSetFuture(AsyncResultSet resultSet) {
|
||||
complete(resultSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a completed future that reports a failure given {@link Throwable}.
|
||||
*
|
||||
* @param throwable must not be {@literal null}.
|
||||
* @return the completed/failed {@link TestResultSetFuture}.
|
||||
*/
|
||||
private static TestResultSetFuture failed(Throwable throwable) {
|
||||
|
||||
TestResultSetFuture future = new TestResultSetFuture();
|
||||
future.completeExceptionally(throwable);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,14 @@ import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.data.cassandra.domain.Person
|
||||
import org.springframework.scheduling.annotation.AsyncResult
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Unit tests for [AsyncCqlOperationsExtensions].
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class AsyncCqlOperationsExtensionsUnitTests {
|
||||
class LegacyAsyncCqlOperationsExtensionsUnitTests {
|
||||
|
||||
val operations = mockk<AsyncCqlOperations>(relaxed = true)
|
||||
|
||||
@@ -120,7 +120,14 @@ class AsyncCqlOperationsExtensionsUnitTests {
|
||||
@Test // DATACASS-484
|
||||
fun `query(String, ResultSetExtractor, array) extension should call its Java counterpart`() {
|
||||
|
||||
operations.query("", 3) { rs -> AsyncResult(Person("Walter", rs.one()!!.getString(0))) }
|
||||
operations.query("", 3) { rs ->
|
||||
CompletableFuture.completedFuture(
|
||||
Person(
|
||||
"Walter",
|
||||
rs.one()!!.getString(0)
|
||||
)
|
||||
)
|
||||
}
|
||||
verify { operations.query(eq(""), any<AsyncResultSetExtractor<Person>>(), eq(3)) }
|
||||
}
|
||||
|
||||
10
src/main/asciidoc/reference/migration-guide-3.0-to-4.0.adoc
Normal file
10
src/main/asciidoc/reference/migration-guide-3.0-to-4.0.adoc
Normal file
@@ -0,0 +1,10 @@
|
||||
[[cassandra.migration.3.x-to-4.x]]
|
||||
= Migration Guide from Spring Data Cassandra 3.x to 4.x
|
||||
|
||||
Spring Data for Apache Cassandra 4.0 introduces a set of breaking changes when upgrading from earlier versions.
|
||||
|
||||
== Asynchronous Template API
|
||||
|
||||
With the deprecation of `ListenableFuture`, `AsyncCqlOperations` and `AsyncCassandraOperations` and their dependant classes were migrated to `CompletableFuture`.
|
||||
If your application heavily depends on `ListenableFuture` and you cannot easily migrate to `CompletableFuture` then we suggest switching to the legacy `Async…Operations` types in the `legacy` subpackage.
|
||||
That is `org.springframework.data.cassandra.core.cql.legacy` for `AsyncCqlOperations` and `org.springframework.data.cassandra.core.legacy` for `AsyncCassandraOperations`.
|
||||
@@ -4,3 +4,5 @@
|
||||
include::migration-guide-1.5-to-2.0.adoc[leveloffset=+1]
|
||||
|
||||
include::migration-guide-2.2-to-3.0.adoc[leveloffset=+1]
|
||||
|
||||
include::migration-guide-3.0-to-4.0.adoc[leveloffset=+1]
|
||||
|
||||
Reference in New Issue
Block a user