DATACASS-573 - Add mutation support for immutable types.

We now return new instances that are potentially created by wither/Kotlin copy(…) methods when reading immutable properties.
This commit is contained in:
Mark Paluch
2018-07-03 11:28:10 +02:00
committed by Oliver Gierke
parent c843293fa5
commit fbcf50e27a
20 changed files with 204 additions and 102 deletions

View File

@@ -292,11 +292,11 @@ public interface AsyncCassandraOperations {
*
* @param entity The entity to insert, must not be {@literal null}.
* @param options must not be {@literal null}.
* @return the {@link WriteResult} for this operation.
* @return the {@link EntityWriteResult} for this operation.
* @throws DataAccessException if there is any problem executing the query.
* @see InsertOptions#empty()
*/
ListenableFuture<WriteResult> insert(Object entity, InsertOptions options) throws DataAccessException;
<T> ListenableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) throws DataAccessException;
/**
* Update the given entity and return the entity if the update was applied.
@@ -312,11 +312,11 @@ public interface AsyncCassandraOperations {
*
* @param entity The entity to update, must not be {@literal null}.
* @param options must not be {@literal null}.
* @return the {@link WriteResult} for this operation.
* @return the {@link EntityWriteResult} for this operation.
* @throws DataAccessException if there is any problem executing the query.
* @see UpdateOptions#empty()
*/
ListenableFuture<WriteResult> update(Object entity, UpdateOptions options) throws DataAccessException;
<T> ListenableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) throws DataAccessException;
/**
* Delete the given entity and return the entity if the delete was applied.

View File

@@ -497,7 +497,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations, Applica
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#insert(java.lang.Object, org.springframework.data.cassandra.core.InsertOptions)
*/
@Override
public ListenableFuture<WriteResult> insert(Object entity, InsertOptions options) {
public <T> ListenableFuture<EntityWriteResult<T>> insert(T entity, InsertOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "InsertOptions must not be null");
@@ -511,7 +511,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations, Applica
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().execute(new AsyncStatementCallback(insert)),
resultSet -> {
maybeEmitEvent(new AfterSaveEvent<>(entity, tableName));
return WriteResult.of(resultSet);
return EntityWriteResult.of(resultSet, entity);
});
}
@@ -520,14 +520,14 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations, Applica
*/
@Override
public <T> ListenableFuture<T> update(T entity) {
return new MappingListenableFutureAdapter<>(update(entity, UpdateOptions.empty()), writeResult -> entity);
return new MappingListenableFutureAdapter<>(update(entity, UpdateOptions.empty()), EntityWriteResult::getEntity);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.AsyncCassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
*/
@Override
public ListenableFuture<WriteResult> update(Object entity, UpdateOptions options) {
public <T> ListenableFuture<EntityWriteResult<T>> update(T entity, UpdateOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "UpdateOptions must not be null");
@@ -540,7 +540,7 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations, Applica
return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().execute(new AsyncStatementCallback(update)),
resultSet -> {
maybeEmitEvent(new AfterSaveEvent<>(entity, tableName));
return WriteResult.of(resultSet);
return EntityWriteResult.of(resultSet, entity);
});
}

View File

@@ -310,39 +310,41 @@ public interface CassandraOperations extends FluentCassandraOperations {
* 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.
*/
void insert(Object entity) throws DataAccessException;
<T> 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 WriteResult} for this operation.
* @return the {@link EntityWriteResult} for this operation.
* @throws DataAccessException if there is any problem executing the query.
* @see InsertOptions#empty()
*/
WriteResult insert(Object entity, InsertOptions options) throws DataAccessException;
<T> 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.
*/
void update(Object entity) throws DataAccessException;
<T> 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 WriteResult} for this operation.
* @return the {@link EntityWriteResult} for this operation.
* @throws DataAccessException if there is any problem executing the query.
* @see UpdateOptions#empty()
*/
WriteResult update(Object entity, UpdateOptions options) throws DataAccessException;
<T> EntityWriteResult<T> update(T entity, UpdateOptions options) throws DataAccessException;
/**
* Delete the given entity and return the entity if the delete was applied.

View File

@@ -531,15 +531,15 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
* @see org.springframework.data.cassandra.core.CassandraOperations#insert(java.lang.Object)
*/
@Override
public void insert(Object entity) {
insert(entity, InsertOptions.empty());
public <T> T insert(T entity) {
return insert(entity, InsertOptions.empty()).getEntity();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#insert(java.lang.Object, org.springframework.data.cassandra.core.InsertOptions)
*/
@Override
public WriteResult insert(Object entity, InsertOptions options) {
public <T> EntityWriteResult<T> insert(T entity, InsertOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "InsertOptions must not be null");
@@ -547,7 +547,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
return doInsert(entity, options, getTableName(entity));
}
WriteResult doInsert(Object entity, WriteOptions options, CqlIdentifier tableName) {
<T> EntityWriteResult<T> doInsert(T entity, WriteOptions options, CqlIdentifier tableName) {
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
@@ -560,22 +560,22 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
maybeEmitEvent(new AfterSaveEvent<>(entity, tableName));
return result;
return EntityWriteResult.of(result, entity);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#update(java.lang.Object)
*/
@Override
public void update(Object entity) {
update(entity, UpdateOptions.empty());
public <T> T update(T entity) {
return update(entity, UpdateOptions.empty()).getEntity();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
*/
@Override
public WriteResult update(Object entity, UpdateOptions options) {
public <T> EntityWriteResult<T> update(T entity, UpdateOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "UpdateOptions must not be null");
@@ -590,7 +590,7 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP
maybeEmitEvent(new AfterSaveEvent<>(entity, tableName));
return result;
return EntityWriteResult.of(result, entity);
}
/* (non-Javadoc)

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.core;
import java.util.List;
import com.datastax.driver.core.ExecutionInfo;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
/**
* The result of a write operation for an entity.
*
* @author Mark Paluch
* @since 2.1
* @see WriteResult
*/
public class EntityWriteResult<T> extends WriteResult {
private final T entity;
EntityWriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows, T entity) {
super(executionInfo, wasApplied, rows);
this.entity = entity;
}
EntityWriteResult(ResultSet resultSet, T entity) {
super(resultSet);
this.entity = entity;
}
/**
* Create a {@link EntityWriteResult} from {@link WriteResult} and an entity.
*
* @param result must not be {@literal null}.
* @param entity must not be {@literal null}.
* @return the {@link EntityWriteResult} for {@link WriteResult} and an entity.
*/
static <T> EntityWriteResult<T> of(WriteResult result, T entity) {
return new EntityWriteResult<>(result.getExecutionInfo(), result.wasApplied(), result.getRows(), entity);
}
/**
* Create a {@link EntityWriteResult} from {@link ResultSet} and an entity.
*
* @param resultSet must not be {@literal null}.
* @param entity must not be {@literal null}.
* @return the {@link EntityWriteResult} for {@link ResultSet} and an entity.
*/
static <T> EntityWriteResult<T> of(ResultSet resultSet, T entity) {
return new EntityWriteResult<>(resultSet, entity);
}
/**
* @return the entity associated with this write operation result.
*/
public T getEntity() {
return entity;
}
}

View File

@@ -114,10 +114,11 @@ public interface ExecutableInsertOperation {
* Insert exactly one {@link Object}.
*
* @param object {@link Object} to insert; must not be {@literal null}.
* @return the {@link EntityWriteResult} for this operation.
* @throws IllegalArgumentException if {@link Object} is {@literal null}.
* @see org.springframework.data.cassandra.core.WriteResult
* @see org.springframework.data.cassandra.core.EntityWriteResult
*/
WriteResult one(T object);
EntityWriteResult<T> one(T object);
}

View File

@@ -91,7 +91,7 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
* @see org.springframework.data.cassandra.core.ExecutableInsertOperation.TerminatingInsert#one(java.lang.Object)
*/
@Override
public WriteResult one(T object) {
public EntityWriteResult<T> one(T object) {
Assert.notNull(object, "Object must not be null");

View File

@@ -254,11 +254,11 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
*
* @param entity The entity to insert, must not be {@literal null}.
* @param options must not be {@literal null}.
* @return the {@link WriteResult} for this operation.
* @return the {@link EntityWriteResult} for this operation.
* @throws DataAccessException if there is any problem issuing the execution.
* @see InsertOptions#empty()
*/
Mono<WriteResult> insert(Object entity, InsertOptions options) throws DataAccessException;
<T> Mono<EntityWriteResult<T>> insert(T entity, InsertOptions options) throws DataAccessException;
/**
* Update the given entity and emit the entity if the update was applied.
@@ -274,11 +274,11 @@ public interface ReactiveCassandraOperations extends ReactiveFluentCassandraOper
*
* @param entity The entity to update, must not be {@literal null}.
* @param options must not be {@literal null}.
* @return the {@link WriteResult} for this operation.
* @return the {@link EntityWriteResult} for this operation.
* @throws DataAccessException if there is any problem issuing the execution.
* @see UpdateOptions#empty()
*/
Mono<WriteResult> update(Object entity, UpdateOptions options) throws DataAccessException;
<T> Mono<EntityWriteResult<T>> update(T entity, UpdateOptions options) throws DataAccessException;
/**
* Delete the given entity and emit the entity if the delete was applied.

View File

@@ -461,14 +461,14 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations, A
*/
@Override
public <T> Mono<T> insert(T entity) {
return insert(entity, InsertOptions.empty()).map(writeResult -> 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<WriteResult> insert(Object entity, InsertOptions options) {
public <T> Mono<EntityWriteResult<T>> insert(T entity, InsertOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "InsertOptions must not be null");
@@ -476,15 +476,18 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations, A
return doInsert(entity, options, getTableName(entity));
}
Mono<WriteResult> doInsert(Object entity, WriteOptions options, CqlIdentifier tableName) {
<T> Mono<EntityWriteResult<T>> doInsert(T entity, WriteOptions options, CqlIdentifier tableName) {
CassandraPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter(), persistentEntity);
// noinspection ConstantConditions
Mono<WriteResult> result = getReactiveCqlOperations().execute(new StatementCallback(insert))
.doOnSubscribe(it -> maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, insert))).next();
Mono<EntityWriteResult<T>> result = getReactiveCqlOperations() //
.execute(new StatementCallback(insert)) //
.doOnSubscribe(it -> maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, insert))) //
.map(it -> EntityWriteResult.of(it, entity)) //
.next();
return result.doOnNext(it -> maybeEmitEvent(new AfterSaveEvent<>(entity, tableName)));
}
@@ -501,7 +504,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations, A
* @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#update(java.lang.Object, org.springframework.data.cassandra.core.UpdateOptions)
*/
@Override
public Mono<WriteResult> update(Object entity, UpdateOptions options) {
public <T> Mono<EntityWriteResult<T>> update(T entity, UpdateOptions options) {
Assert.notNull(entity, "Entity must not be null");
Assert.notNull(options, "UpdateOptions must not be null");
@@ -509,8 +512,11 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations, A
CqlIdentifier tableName = getTableName(entity);
Update update = QueryUtils.createUpdateQuery(tableName.toCql(), entity, options, getConverter());
Mono<WriteResult> result = getReactiveCqlOperations().execute(new StatementCallback(update))
.doOnSubscribe(it -> maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, update))).next();
Mono<EntityWriteResult<T>> result = getReactiveCqlOperations() //
.execute(new StatementCallback(update)) //
.doOnSubscribe(it -> maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, update))) //
.map(it -> EntityWriteResult.of(it, entity)) //
.next();
return result.doOnNext(it -> maybeEmitEvent(new AfterSaveEvent<>(entity, tableName)));
}
@@ -535,8 +541,10 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations, A
CqlIdentifier tableName = getTableName(entity);
Delete delete = QueryUtils.createDeleteQuery(tableName.toCql(), entity, options, getConverter());
Mono<WriteResult> result = getReactiveCqlOperations().execute(new StatementCallback(delete))
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(delete, entity.getClass(), tableName))).next();
Mono<WriteResult> result = getReactiveCqlOperations() //
.execute(new StatementCallback(delete)) //
.doOnSubscribe(it -> maybeEmitEvent(new BeforeDeleteEvent<>(delete, entity.getClass(), tableName))) //
.next();
return result.doOnNext(it -> maybeEmitEvent(new AfterDeleteEvent<>(delete, entity.getClass(), tableName)));
}

View File

@@ -118,11 +118,12 @@ public interface ReactiveInsertOperation {
* Insert exactly one {@link Object}.
*
* @param object {@link Object} to insert; must not be {@literal null}.
* @return the {@link EntityWriteResult} for this operation.
* @throws IllegalArgumentException if {@link Object} is {@literal null}.
* @see org.springframework.data.cassandra.core.WriteResult
* @see org.springframework.data.cassandra.core.EntityWriteResult
* @see reactor.core.publisher.Mono
*/
Mono<WriteResult> one(T object);
Mono<EntityWriteResult<T>> one(T object);
}

View File

@@ -19,7 +19,6 @@ import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Mono;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
@@ -86,7 +85,7 @@ class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
* @see org.springframework.data.cassandra.core.ReactiveInsertOperation.TerminatingInsert#one(java.lang.Object)
*/
@Override
public Mono<WriteResult> one(T object) {
public Mono<EntityWriteResult<T>> one(T object) {
Assert.notNull(object, "Object must not be null");

View File

@@ -35,7 +35,9 @@ import com.datastax.driver.core.Row;
public class WriteResult {
private final boolean wasApplied;
private final List<ExecutionInfo> executionInfo;
private final List<Row> rows;
WriteResult(List<ExecutionInfo> executionInfo, boolean wasApplied, List<Row> rows) {
@@ -45,7 +47,7 @@ public class WriteResult {
this.rows = rows;
}
private WriteResult(ResultSet resultSet) {
WriteResult(ResultSet resultSet) {
this.executionInfo = resultSet.getAllExecutionInfo();
this.wasApplied = resultSet.wasApplied();

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.cassandra.core.convert;
import lombok.AllArgsConstructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -23,8 +25,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.ApplicationContext;
@@ -57,9 +59,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
@@ -277,9 +276,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
S instance = instantiator.createInstance(entity, parameterValueProvider);
readProperties(entity, valueProvider, newConvertingPropertyAccessor(instance, entity));
ConvertingPropertyAccessor propertyAccessor = newConvertingPropertyAccessor(instance, entity);
readProperties(entity, valueProvider, propertyAccessor);
return instance;
return (S) propertyAccessor.getBean();
}
private void readProperties(CassandraPersistentEntity<?> entity, CassandraValueProvider valueProvider,
@@ -309,10 +309,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
// now recurse on using the key this time
readProperties(keyEntity, valueProvider, newConvertingPropertyAccessor(key, keyEntity));
ConvertingPropertyAccessor pkPropertyAccessor = newConvertingPropertyAccessor(key, keyEntity);
readProperties(keyEntity, valueProvider, pkPropertyAccessor);
// now that the key's properties have been populated, set the key property on the entity
propertyAccessor.setProperty(property, key);
propertyAccessor.setProperty(property, pkPropertyAccessor.getBean());
return;
}

View File

@@ -77,9 +77,7 @@ public class SimpleCassandraRepository<T, ID> implements CassandraRepository<T,
Assert.notNull(entity, "Entity must not be null");
operations.insert(entity, INSERT_NULLS);
return entity;
return operations.insert(entity, INSERT_NULLS).getEntity();
}
/* (non-Javadoc)
@@ -93,9 +91,7 @@ public class SimpleCassandraRepository<T, ID> implements CassandraRepository<T,
List<S> result = new ArrayList<>();
for (S entity : entities) {
result.add(entity);
operations.insert(entity, INSERT_NULLS);
result.add(operations.insert(entity, INSERT_NULLS).getEntity());
}
return result;
@@ -121,9 +117,7 @@ public class SimpleCassandraRepository<T, ID> implements CassandraRepository<T,
Assert.notNull(entity, "Entity must not be null");
operations.insert(entity);
return entity;
return operations.insert(entity);
}
/* (non-Javadoc)
@@ -137,8 +131,7 @@ public class SimpleCassandraRepository<T, ID> implements CassandraRepository<T,
List<S> result = new ArrayList<>();
for (S entity : entities) {
operations.insert(entity);
result.add(entity);
result.add(operations.insert(entity));
}
return result;

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.data.cassandra.core;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.cassandra.core.query.Criteria.*;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -25,7 +25,6 @@ import java.util.concurrent.Future;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.cql.AsyncCqlTemplate;
import org.springframework.data.cassandra.core.query.CassandraPageRequest;
@@ -120,7 +119,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
User user = new User("heisenberg", "Walter", "White");
ListenableFuture<WriteResult> inserted = template.insert(user, lwtOptions);
ListenableFuture<EntityWriteResult<User>> inserted = template.insert(user, lwtOptions);
assertThat(getUninterruptibly(inserted).wasApplied()).isTrue();
}
@@ -136,7 +135,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
user.setFirstname("Walter Hartwell");
ListenableFuture<WriteResult> lwt = template.insert(user, lwtOptions);
ListenableFuture<EntityWriteResult<User>> lwt = template.insert(user, lwtOptions);
assertThat(getUninterruptibly(lwt).wasApplied()).isFalse();
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter");
@@ -147,9 +146,10 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
User user = new User("heisenberg", "Walter", "White");
getUninterruptibly(template.insert(user));
User result = getUninterruptibly(template.insert(user));
ListenableFuture<Long> count = template.count(User.class);
assertThat(result).isSameAs(user);
assertThat(getUninterruptibly(count)).isEqualTo(1L);
}
@@ -196,7 +196,7 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
User user = new User("heisenberg", "Walter", "White");
ListenableFuture<WriteResult> lwt = template.update(user, lwtOptions);
ListenableFuture<EntityWriteResult<User>> lwt = template.update(user, lwtOptions);
assertThat(getUninterruptibly(lwt).wasApplied()).isFalse();
assertThat(getUser(user.getId())).isNull();
@@ -212,9 +212,10 @@ public class AsyncCassandraTemplateIntegrationTests extends AbstractKeyspaceCrea
user.setFirstname("Walter Hartwell");
ListenableFuture<WriteResult> updated = template.update(user, lwtOptions);
ListenableFuture<EntityWriteResult<User>> updated = template.update(user, lwtOptions);
assertThat(getUninterruptibly(updated).wasApplied()).isTrue();
assertThat(getUninterruptibly(updated).getEntity()).isSameAs(user);
assertThat(getUser(user.getId()).getFirstname()).isEqualTo("Walter Hartwell");
}

View File

@@ -146,31 +146,33 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
assertThat(loaded).isEqualTo(token1);
}
@Test // DATACASS-292
@Test // DATACASS-292, DATACASS-573
public void insertShouldInsertEntity() {
User user = new User("heisenberg", "Walter", "White");
assertThat(template.selectOneById(user.getId(), User.class)).isNull();
template.insert(user);
User result = template.insert(user);
assertThat(result).isSameAs(user);
assertThat(template.selectOneById(user.getId(), User.class)).isEqualTo(user);
}
@Test // DATACASS-250
@Test // DATACASS-250, DATACASS-573
public void insertShouldCreateEntityWithLwt() {
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
User user = new User("heisenberg", "Walter", "White");
template.insert(user, lwtOptions);
EntityWriteResult<User> result = template.insert(user, lwtOptions);
assertThat(result.getEntity()).isSameAs(user);
assertThat(template.selectOneById(user.getId(), User.class)).isNotNull();
}
@Test // DATACASS-250
@Test // DATACASS-250, DATACASS-573
public void insertShouldNotUpdateEntityWithLwt() {
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
@@ -181,7 +183,7 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
user.setFirstname("Walter Hartwell");
WriteResult lwt = template.insert(user, lwtOptions);
EntityWriteResult<User> lwt = template.insert(user, lwtOptions);
assertThat(lwt.wasApplied()).isFalse();
assertThat(template.selectOneById(user.getId(), User.class).getFirstname()).isEqualTo("Walter");
@@ -239,7 +241,7 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
assertThat(template.exists(Query.query(where("id").is("foo")), User.class)).isFalse();
}
@Test // DATACASS-292
@Test // DATACASS-292, DATACASS-573
public void updateShouldUpdateEntity() {
User user = new User("heisenberg", "Walter", "White");
@@ -247,8 +249,9 @@ public class CassandraTemplateIntegrationTests extends AbstractKeyspaceCreatingI
user.setFirstname("Walter Hartwell");
template.update(user);
User result = template.update(user);
assertThat(result).isSameAs(user);
assertThat(template.selectOneById(user.getId(), User.class)).isEqualTo(user);
}

View File

@@ -92,16 +92,20 @@ public class ReactiveCassandraTemplateIntegrationTests extends AbstractKeyspaceC
verifyUser(user.getId()).expectNext(user).verifyComplete();
}
@Test // DATACASS-250
@Test // DATACASS-250, DATACASS-573
public void insertShouldCreateEntityWithLwt() {
InsertOptions lwtOptions = InsertOptions.builder().withIfNotExists().build();
User user = new User("heisenberg", "Walter", "White");
Mono<WriteResult> inserted = template.insert(user, lwtOptions);
Mono<EntityWriteResult<User>> inserted = template.insert(user, lwtOptions);
StepVerifier.create(inserted.map(WriteResult::wasApplied)).expectNext(true).verifyComplete();
StepVerifier.create(inserted).consumeNextWith(actual -> {
assertThat(actual.wasApplied()).isTrue();
assertThat(actual.getEntity()).isSameAs(user);
}).verifyComplete();
}
@Test // DATACASS-250

View File

@@ -15,18 +15,16 @@
*/
package org.springframework.data.cassandra.core;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collections;
import static org.assertj.core.api.Assertions.*;
import lombok.Data;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.cql.CqlIdentifier;
@@ -90,21 +88,26 @@ public class ReactiveInsertOperationSupportIntegrationTests extends AbstractKeys
this.template.insert(Person.class).inTable((String) null);
}
@Test // DATACASS-485
@Test // DATACASS-485, DATACASS-573
public void insertOne() {
Mono<WriteResult> writeResult = this.template.insert(Person.class).inTable("person").one(han);
Mono<EntityWriteResult<Person>> writeResult = this.template.insert(Person.class).inTable("person").one(han);
StepVerifier.create(writeResult).consumeNextWith(actual -> {
assertThat(actual.wasApplied()).isTrue();
assertThat(actual.getEntity()).isSameAs(han);
}).verifyComplete();
StepVerifier.create(writeResult.map(WriteResult::wasApplied)).expectNext(true).verifyComplete();
StepVerifier.create(template.selectOneById(han.id, Person.class)).expectNext(han).verifyComplete();
}
@Test // DATACASS-485
@Test // DATACASS-485, DATACASS-573
public void insertOneWithOptions() {
this.template.insert(Person.class).inTable("person").one(han);
Mono<WriteResult> writeResult = this.template
Mono<EntityWriteResult<Person>> writeResult = this.template
.insert(Person.class).inTable("person")
.withOptions(InsertOptions.builder().withIfNotExists().build())
.one(han);

View File

@@ -211,7 +211,7 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC
assertThat(repository.count()).isEqualTo(3);
}
@Test // DATACASS-396
@Test // DATACASS-396, DATACASS-573
public void saveEntityShouldUpdateExistingEntity() {
dave.setFirstname("Hello, Dave");
@@ -219,7 +219,7 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC
User saved = repository.save(dave);
assertThat(saved).isEqualTo(saved);
assertThat(saved).isSameAs(saved);
Optional<User> loaded = repository.findById(dave.getId());
@@ -257,14 +257,14 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC
assertThat(loaded).contains(User);
}
@Test // DATACASS-396, DATACASS-416
@Test // DATACASS-396, DATACASS-416, DATACASS-573
public void saveIterableOfNewEntitiesShouldInsertEntity() {
repository.deleteAll();
List<User> saved = repository.saveAll(Arrays.asList(dave, oliver, boyd));
assertThat(saved).hasSize(3);
assertThat(saved).hasSize(3).contains(dave, oliver, boyd);
assertThat(repository.count()).isEqualTo(3);
}

View File

@@ -30,6 +30,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.EntityWriteResult;
import org.springframework.data.cassandra.core.InsertOptions;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.cql.CqlOperations;
@@ -63,6 +64,7 @@ public class SimpleCassandraRepositoryUnitTests {
@Mock CqlOperations cqlOperations;
@Mock UserTypeResolver userTypeResolver;
@Mock UserType userType;
@Mock EntityWriteResult writeResult;
@Captor ArgumentCaptor<Insert> insertCaptor;
@@ -71,7 +73,7 @@ public class SimpleCassandraRepositoryUnitTests {
mappingContext.setUserTypeResolver(userTypeResolver);
}
@Test // DATACASS-428, DATACASS-560
@Test // DATACASS-428, DATACASS-560, DATACASS-573
public void saveShouldInsertNewPrimaryKeyOnlyEntity() {
CassandraPersistentEntity<?> entity = converter.getMappingContext().getRequiredPersistentEntity(SimplePerson.class);
@@ -81,12 +83,15 @@ public class SimpleCassandraRepositoryUnitTests {
SimplePerson person = new SimplePerson();
when(cassandraOperations.insert(eq(person), any())).thenReturn(writeResult);
when(writeResult.getEntity()).thenReturn(person);
repository.save(person);
verify(cassandraOperations).insert(person, InsertOptions.builder().withInsertNulls().build());
}
@Test // DATACASS-428, DATACASS-560
@Test // DATACASS-428, DATACASS-560, DATACASS-573
public void saveShouldUpdateNewEntity() {
CassandraPersistentEntity<?> entity = converter.getMappingContext().getRequiredPersistentEntity(Person.class);
@@ -96,12 +101,15 @@ public class SimpleCassandraRepositoryUnitTests {
Person person = new Person();
when(cassandraOperations.insert(eq(person), any())).thenReturn(writeResult);
when(writeResult.getEntity()).thenReturn(person);
repository.save(person);
verify(cassandraOperations).insert(person, InsertOptions.builder().withInsertNulls().build());
}
@Test // DATACASS-428, DATACASS-560
@Test // DATACASS-428, DATACASS-560, DATACASS-573
public void saveShouldUpdateExistingEntity() {
CassandraPersistentEntity<?> entity = converter.getMappingContext().getRequiredPersistentEntity(Person.class);
@@ -113,6 +121,9 @@ public class SimpleCassandraRepositoryUnitTests {
person.setFirstname("foo");
person.setLastname("bar");
when(cassandraOperations.insert(eq(person), any())).thenReturn(writeResult);
when(writeResult.getEntity()).thenReturn(person);
repository.save(person);
verify(cassandraOperations).insert(person, InsertOptions.builder().withInsertNulls().build());