org.springframework.data.cassandra.core.ReactiveCassandraTemplate Maven / Gradle / Ivy
Show all versions of spring-data-cassandra Show documentation
/*
* Copyright 2016-2020 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;
import lombok.Value;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SynchronousSink;
import java.util.Collections;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.cassandra.ReactiveResultSet;
import org.springframework.data.cassandra.ReactiveSession;
import org.springframework.data.cassandra.ReactiveSessionFactory;
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.convert.QueryMapper;
import org.springframework.data.cassandra.core.convert.UpdateMapper;
import org.springframework.data.cassandra.core.cql.CassandraAccessor;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.core.cql.CqlProvider;
import org.springframework.data.cassandra.core.cql.QueryOptions;
import org.springframework.data.cassandra.core.cql.ReactiveCqlOperations;
import org.springframework.data.cassandra.core.cql.ReactiveCqlTemplate;
import org.springframework.data.cassandra.core.cql.ReactiveSessionCallback;
import org.springframework.data.cassandra.core.cql.RowMapper;
import org.springframework.data.cassandra.core.cql.WriteOptions;
import org.springframework.data.cassandra.core.cql.session.DefaultReactiveSessionFactory;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.event.AfterConvertEvent;
import org.springframework.data.cassandra.core.mapping.event.AfterDeleteEvent;
import org.springframework.data.cassandra.core.mapping.event.AfterLoadEvent;
import org.springframework.data.cassandra.core.mapping.event.AfterSaveEvent;
import org.springframework.data.cassandra.core.mapping.event.BeforeDeleteEvent;
import org.springframework.data.cassandra.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.cassandra.core.mapping.event.CassandraMappingEvent;
import org.springframework.data.cassandra.core.mapping.event.ReactiveBeforeConvertCallback;
import org.springframework.data.cassandra.core.mapping.event.ReactiveBeforeSaveCallback;
import org.springframework.data.cassandra.core.query.Columns;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Truncate;
import com.datastax.driver.core.querybuilder.Update;
/**
* Primary implementation of {@link ReactiveCassandraOperations}. It simplifies the use of Reactive Cassandra usage and
* helps to avoid common errors. It executes core Cassandra workflow. This class executes CQL queries or updates,
* initiating iteration over {@link ReactiveResultSet} and catching Cassandra exceptions and translating them to the
* generic, more informative exception hierarchy defined in the {@code org.springframework.dao} package.
*
* Can be used within a service implementation via direct instantiation with a {@link ReactiveSessionFactory} reference,
* or get prepared in an application context and given to services as bean reference.
*
* Note: The {@link ReactiveSessionFactory} 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.
*
* @author Mark Paluch
* @author John Blum
* @author Lukasz Antoniak
* @author Hleb Albau
* @since 2.0
*/
public class ReactiveCassandraTemplate
implements ReactiveCassandraOperations, ApplicationEventPublisherAware, ApplicationContextAware {
private @Nullable ApplicationEventPublisher eventPublisher;
private @Nullable ReactiveEntityCallbacks entityCallbacks;
private final CassandraConverter converter;
private final EntityOperations entityOperations;
private final ReactiveCqlOperations cqlOperations;
private final SpelAwareProxyProjectionFactory projectionFactory;
private final StatementFactory statementFactory;
/**
* Creates an instance of {@link ReactiveCassandraTemplate} initialized with the given {@link ReactiveSession} and a
* default {@link MappingCassandraConverter}.
*
* @param session {@link ReactiveSession} used to interact with Cassandra; must not be {@literal null}.
* @see CassandraConverter
* @see Session
*/
public ReactiveCassandraTemplate(ReactiveSession session) {
this(session, newConverter());
}
/**
* Create an instance of {@link CassandraTemplate} initialized with the given {@link ReactiveSession} and
* {@link CassandraConverter}.
*
* @param session {@link ReactiveSession} used to interact with Cassandra; must not be {@literal null}.
* @param converter {@link CassandraConverter} used to convert between Java and Cassandra types; must not be
* {@literal null}.
* @see org.springframework.data.cassandra.core.convert.CassandraConverter
* @see com.datastax.driver.core.Session
*/
public ReactiveCassandraTemplate(ReactiveSession session, CassandraConverter converter) {
this(new DefaultReactiveSessionFactory(session), converter);
}
/**
* Create an instance of {@link ReactiveCassandraTemplate} initialized with the given {@link ReactiveSessionFactory}
* and {@link CassandraConverter}.
*
* @param sessionFactory {@link ReactiveSessionFactory} used to interact with Cassandra; must not be {@literal null}.
* @param converter {@link CassandraConverter} used to convert between Java and Cassandra types; must not be
* {@literal null}.
* @see org.springframework.data.cassandra.core.convert.CassandraConverter
* @see com.datastax.driver.core.Session
*/
public ReactiveCassandraTemplate(ReactiveSessionFactory sessionFactory, CassandraConverter converter) {
this(new ReactiveCqlTemplate(sessionFactory), converter);
}
/**
* Create an instance of {@link ReactiveCassandraTemplate} initialized with the given {@link ReactiveCqlOperations}
* and {@link CassandraConverter}.
*
* @param reactiveCqlOperations {@link ReactiveCqlOperations} used to interact with Cassandra; must not be
* {@literal null}.
* @param converter {@link CassandraConverter} used to convert between Java and Cassandra types; must not be
* {@literal null}.
* @see org.springframework.data.cassandra.core.convert.CassandraConverter
* @see com.datastax.driver.core.Session
*/
public ReactiveCassandraTemplate(ReactiveCqlOperations reactiveCqlOperations, CassandraConverter converter) {
Assert.notNull(reactiveCqlOperations, "ReactiveCqlOperations must not be null");
Assert.notNull(converter, "CassandraConverter must not be null");
this.converter = converter;
this.cqlOperations = reactiveCqlOperations;
this.entityOperations = new EntityOperations(converter.getMappingContext());
this.projectionFactory = new SpelAwareProxyProjectionFactory();
this.statementFactory = new StatementFactory(new QueryMapper(converter), new UpdateMapper(converter));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#batchOps()
*/
@Override
public ReactiveCassandraBatchOperations batchOps() {
return new ReactiveCassandraBatchTemplate(this);
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
*/
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.eventPublisher = applicationEventPublisher;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (entityCallbacks == null) {
setEntityCallbacks(ReactiveEntityCallbacks.create(applicationContext));
}
projectionFactory.setBeanFactory(applicationContext);
projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
}
/**
* Configure {@link EntityCallbacks} to pre-/post-process entities during persistence operations.
*
* @param entityCallbacks
*/
public void setEntityCallbacks(@Nullable ReactiveEntityCallbacks entityCallbacks) {
this.entityCallbacks = entityCallbacks;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#getConverter()
*/
@Override
public CassandraConverter getConverter() {
return this.converter;
}
/**
* Returns the {@link EntityOperations} used to perform data access operations on an entity inside a Cassandra data
* source.
*
* @return the configured {@link EntityOperations} for this template.
* @see org.springframework.data.cassandra.core.EntityOperations
*/
protected EntityOperations getEntityOperations() {
return this.entityOperations;
}
/**
* Returns a reference to the configured {@link ProjectionFactory} used by this template to process CQL query
* projections.
*
* @return a reference to the configured {@link ProjectionFactory} used by this template to process CQL query
* projections.
* @see org.springframework.data.projection.SpelAwareProxyProjectionFactory
* @since 2.1
*/
protected SpelAwareProxyProjectionFactory getProjectionFactory() {
return this.projectionFactory;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#getReactiveCqlOperations()
*/
@Override
public ReactiveCqlOperations getReactiveCqlOperations() {
return this.cqlOperations;
}
private CassandraPersistentEntity getRequiredPersistentEntity(Class entityType) {
return getEntityOperations().getRequiredPersistentEntity(entityType);
}
/**
* Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements.
*
* @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements.
* @see org.springframework.data.cassandra.core.StatementFactory
* @since 2.1
*/
protected StatementFactory getStatementFactory() {
return this.statementFactory;
}
CqlIdentifier getTableName(Class entityClass) {
return getEntityOperations().getTableName(entityClass);
}
// -------------------------------------------------------------------------
// Methods dealing with static CQL
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#select(java.lang.String, java.lang.Class)
*/
@Override
public Flux select(String cql, Class entityClass) {
Assert.hasText(cql, "CQL must not be empty");
return select(new SimpleStatement(cql), entityClass);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#selectOne(java.lang.String, java.lang.Class)
*/
@Override
public Mono selectOne(String cql, Class entityClass) {
return select(cql, entityClass).next();
}
// -------------------------------------------------------------------------
// Methods dealing with com.datastax.driver.core.Statement
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#select(com.datastax.driver.core.Statement, java.lang.Class)
*/
@Override
public Flux select(Statement statement, Class entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
Function mapper = getMapper(entityClass, entityClass, EntityQueryUtils.getTableName(statement));
return getReactiveCqlOperations().query(statement, (row, rowNum) -> mapper.apply(row));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#selectOne(com.datastax.driver.core.Statement, java.lang.Class)
*/
@Override
public Mono selectOne(Statement statement, Class entityClass) {
return select(statement, entityClass).next();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#slice(com.datastax.driver.core.Statement, java.lang.Class)
*/
@Override
public Mono> slice(Statement statement, Class entityClass) {
Assert.notNull(statement, "Statement must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
Mono resultSetMono = getReactiveCqlOperations().queryForResultSet(statement);
Mono effectiveFetchSizeMono = getEffectiveFetchSize(statement);
RowMapper rowMapper = (row, i) -> getConverter().read(entityClass, row);
return resultSetMono.zipWith(effectiveFetchSizeMono).flatMap(tuple -> {
ReactiveResultSet resultSet = tuple.getT1();
Integer effectiveFetchSize = tuple.getT2();
return resultSet.availableRows().collectList().map(it -> EntityQueryUtils.readSlice(it,
resultSet.getExecutionInfo().getPagingState(), rowMapper, 1, effectiveFetchSize));
}).defaultIfEmpty(new SliceImpl<>(Collections.emptyList()));
}
// -------------------------------------------------------------------------
// Methods dealing with org.springframework.data.cassandra.core.query.Query
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#select(org.springframework.data.cassandra.core.query.Query, java.lang.Class)
*/
@Override
public Flux select(Query query, Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return doSelect(query, entityClass, getTableName(entityClass), entityClass);
}
Flux doSelect(Query query, Class entityClass, CqlIdentifier tableName, Class returnType) {
CassandraPersistentEntity persistentEntity = getRequiredPersistentEntity(entityClass);
Columns columns = getStatementFactory().computeColumnsForProjection(query.getColumns(), persistentEntity,
returnType);
Query queryToUse = query.columns(columns);
RegularStatement select = getStatementFactory().select(queryToUse, persistentEntity, tableName);
Function mapper = getMapper(entityClass, returnType, tableName);
return getReactiveCqlOperations().query(select, (row, rowNum) -> mapper.apply(row));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#selectOne(org.springframework.data.cassandra.core.query.Query, java.lang.Class)
*/
@Override
public Mono selectOne(Query query, Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return select(query, entityClass).next();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#slice(org.springframework.data.cassandra.core.query.Query, java.lang.Class)
*/
@Override
public Mono> slice(Query query, Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
RegularStatement select = getStatementFactory().select(query, getRequiredPersistentEntity(entityClass));
return slice(select, entityClass);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(org.springframework.data.cassandra.core.query.Query, org.springframework.data.cassandra.core.query.Update, java.lang.Class)
*/
@Override
public Mono update(Query query, org.springframework.data.cassandra.core.query.Update update,
Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(update, "Update must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return doUpdate(query, update, entityClass, getTableName(entityClass)).map(WriteResult::wasApplied);
}
Mono doUpdate(Query query, org.springframework.data.cassandra.core.query.Update update,
Class entityClass, CqlIdentifier tableName) {
RegularStatement statement = getStatementFactory().update(query, update, getRequiredPersistentEntity(entityClass),
tableName);
return getReactiveCqlOperations().execute(new StatementCallback(statement)).next();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#delete(org.springframework.data.cassandra.core.query.Query, java.lang.Class)
*/
@Override
public Mono delete(Query query, Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return doDelete(query, entityClass, getTableName(entityClass)).map(WriteResult::wasApplied);
}
Mono doDelete(Query query, Class entityClass, CqlIdentifier tableName) {
RegularStatement delete = getStatementFactory().delete(query, getRequiredPersistentEntity(entityClass), tableName);
Mono writeResult = getReactiveCqlOperations().execute(new StatementCallback(delete))
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(delete, entityClass, tableName))).next();
return writeResult.doOnNext(it -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entityClass, tableName)));
}
// -------------------------------------------------------------------------
// Methods dealing with entities
// -------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#count(java.lang.Class)
*/
@Override
public Mono count(Class entityClass) {
Assert.notNull(entityClass, "Entity type must not be null");
Select select = QueryBuilder.select().countAll().from(getTableName(entityClass).toCql());
return getReactiveCqlOperations().queryForObject(select, Long.class);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#count(org.springframework.data.cassandra.core.query.Query, java.lang.Class)
*/
@Override
public Mono count(Query query, Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return doCount(query, entityClass, getTableName(entityClass));
}
Mono doCount(Query query, Class entityClass, CqlIdentifier tableName) {
RegularStatement count = getStatementFactory().count(query, getRequiredPersistentEntity(entityClass), tableName);
return getReactiveCqlOperations().queryForObject(count, Long.class).switchIfEmpty(Mono.just(0L));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#exists(java.lang.Object, java.lang.Class)
*/
@Override
public Mono exists(Object id, Class entityClass) {
Assert.notNull(id, "Id must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
CassandraPersistentEntity entity = getRequiredPersistentEntity(entityClass);
Select select = QueryBuilder.select().from(entity.getTableName().toCql());
getConverter().write(id, select.where(), entity);
return getReactiveCqlOperations().queryForRows(select).hasElements();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#exists(org.springframework.data.cassandra.core.query.Query, java.lang.Class)
*/
@Override
public Mono exists(Query query, Class entityClass) throws DataAccessException {
Assert.notNull(query, "Query must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
return doExists(query, entityClass, getTableName(entityClass));
}
Mono doExists(Query query, Class entityClass, CqlIdentifier tableName) {
RegularStatement select = getStatementFactory().select(query.limit(1), getRequiredPersistentEntity(entityClass),
tableName);
return getReactiveCqlOperations().queryForRows(select).hasElements();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#selectOneById(java.lang.Object, java.lang.Class)
*/
@Override
public Mono selectOneById(Object id, Class entityClass) {
Assert.notNull(id, "Id must not be null");
Assert.notNull(entityClass, "Entity type must not be null");
CassandraPersistentEntity entity = getRequiredPersistentEntity(entityClass);
Select select = QueryBuilder.select().all().from(entity.getTableName().toCql());
getConverter().write(id, select.where(), entity);
return selectOne(select, entityClass);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#insert(java.lang.Object)
*/
@Override
public Mono insert(T entity) {
return insert(entity, InsertOptions.empty()).map(EntityWriteResult::getEntity);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#insert(java.lang.Object, org.springframework.data.cassandra.core.InsertOptions)
*/
@Override
public Mono> insert(T entity, InsertOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "InsertOptions must not be null");
return doInsert(entity, options, getTableName(entity.getClass()));
}
Mono> doInsert(T entity, WriteOptions options, CqlIdentifier tableName) {
return maybeCallBeforeConvert(entity, tableName).flatMap(entityToInsert -> {
AdaptibleEntity source = this.entityOperations.forEntity(entityToInsert,
getConverter().getConversionService());
CassandraPersistentEntity persistentEntity = getRequiredPersistentEntity(entityToInsert.getClass());
T entityToUse = source.isVersionedEntity() ? source.initializeVersionProperty() : entityToInsert;
Insert insert = EntityQueryUtils.createInsertQuery(tableName.toCql(), entityToUse, options, getConverter(),
persistentEntity);
return source.isVersionedEntity() ? doInsertVersioned(insert.ifNotExists(), entityToUse, source, tableName)
: doInsert(insert, entityToUse, tableName);
});
}
private Mono> doInsertVersioned(Insert insert, T entity, AdaptibleEntity source,
CqlIdentifier tableName) {
return executeSave(entity, tableName, insert, (result, sink) -> {
if (!result.wasApplied()) {
sink.error(new OptimisticLockingFailureException(
String.format("Cannot insert entity %s with version %s into table %s as it already exists", entity,
source.getVersion(), tableName)));
return;
}
sink.next(result);
});
}
private Mono> doInsert(Insert insert, T entity, CqlIdentifier tableName) {
return executeSave(entity, tableName, insert);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(java.lang.Object)
*/
@Override
public Mono update(T entity) {
return update(entity, UpdateOptions.empty()).map(EntityWriteResult::getEntity);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
*/
@Override
public Mono> update(T entity, UpdateOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "UpdateOptions must not be null");
AdaptibleEntity source = this.entityOperations.forEntity(entity, getConverter().getConversionService());
CassandraPersistentEntity persistentEntity = getRequiredPersistentEntity(entity.getClass());
CqlIdentifier tableName = persistentEntity.getTableName();
return maybeCallBeforeConvert(entity, tableName).flatMap(entityToUpdate -> {
return source.isVersionedEntity() ? doUpdateVersioned(entity, options, tableName, persistentEntity)
: doUpdate(entity, options, tableName, persistentEntity);
});
}
private Mono> doUpdateVersioned(T entity, UpdateOptions options, CqlIdentifier tableName,
CassandraPersistentEntity persistentEntity) {
AdaptibleEntity source = getEntityOperations().forEntity(entity, getConverter().getConversionService());
Number previousVersion = source.getVersion();
T toSave = source.incrementVersion();
Update update = getStatementFactory().update(toSave, options, getConverter(), persistentEntity, tableName);
return executeSave(toSave, tableName, source.appendVersionCondition(update, previousVersion), (result, sink) -> {
if (!result.wasApplied()) {
sink.error(new OptimisticLockingFailureException(
String.format("Cannot save entity %s with version %s to table %s. Has it been modified meanwhile?", toSave,
source.getVersion(), tableName)));
return;
}
sink.next(result);
});
}
private Mono> doUpdate(T entity, UpdateOptions options, CqlIdentifier tableName,
CassandraPersistentEntity persistentEntity) {
Update update = getStatementFactory().update(entity, options, getConverter(), persistentEntity, tableName);
return executeSave(entity, tableName, update);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#delete(java.lang.Object)
*/
@Override
public Mono delete(T entity) {
return delete(entity, QueryOptions.empty()).map(reactiveWriteResult -> entity);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#delete(java.lang.Object, org.springframework.data.cql.core.QueryOptions)
*/
@Override
public Mono delete(Object entity, QueryOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "QueryOptions must not be null");
AdaptibleEntity