diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java index 3756c2759..9be84554b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java @@ -502,8 +502,9 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations, Applica Assert.notNull(entity, "Entity must not be null"); Assert.notNull(options, "InsertOptions must not be null"); - CqlIdentifier tableName = getTableName(entity); - Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter()); + CassandraPersistentEntity persistentEntity = getRequiredPersistentEntity(entity.getClass()); + CqlIdentifier tableName = persistentEntity.getTableName(); + Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter(), persistentEntity); maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, insert)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java index b352af046..3989f5d94 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraBatchTemplate.java @@ -20,6 +20,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity; +import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; import org.springframework.util.Assert; import com.datastax.driver.core.querybuilder.Batch; @@ -109,10 +111,16 @@ class CassandraBatchTemplate implements CassandraBatchOperations { Assert.notNull(entities, "Entities must not be null"); Assert.notNull(options, "WriteOptions must not be null"); + CassandraMappingContext mappingContext = operations.getConverter().getMappingContext(); + for (Object entity : entities) { Assert.notNull(entity, "Entity must not be null"); - batch.add(QueryUtils.createInsertQuery(getTableName(entity), entity, options, operations.getConverter())); + + BasicCassandraPersistentEntity persistentEntity = mappingContext + .getRequiredPersistentEntity(entity.getClass()); + batch.add(QueryUtils.createInsertQuery(persistentEntity.getTableName().toCql(), entity, options, + operations.getConverter(), persistentEntity)); } return this; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 373c7291d..214c6daf9 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -549,7 +549,9 @@ public class CassandraTemplate implements CassandraOperations, ApplicationEventP WriteResult doInsert(Object entity, WriteOptions options, CqlIdentifier tableName) { - Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter()); + CassandraPersistentEntity persistentEntity = getRequiredPersistentEntity(entity.getClass()); + + Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter(), persistentEntity); maybeEmitEvent(new BeforeSaveEvent<>(entity, tableName, insert)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java index e6ac46588..e27a9e7d7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/InsertOptions.java @@ -41,13 +41,16 @@ public class InsertOptions extends WriteOptions { private boolean ifNotExists; + private boolean insertNulls; + private InsertOptions(@Nullable ConsistencyLevel consistencyLevel, @Nullable RetryPolicy retryPolicy, @Nullable Boolean tracing, @Nullable Integer fetchSize, Duration readTimeout, Duration ttl, - @Nullable Long timestamp, boolean ifNotExists) { + @Nullable Long timestamp, boolean ifNotExists, boolean insertNulls) { super(consistencyLevel, retryPolicy, tracing, fetchSize, readTimeout, ttl, timestamp); this.ifNotExists = ifNotExists; + this.insertNulls = insertNulls; } /** @@ -86,6 +89,14 @@ public class InsertOptions extends WriteOptions { return this.ifNotExists; } + /** + * @return {@literal true} to insert {@literal null} values from an entity. + * @since 2.1 + */ + public boolean isInsertNulls() { + return this.insertNulls; + } + /** * Builder for {@link InsertOptions}. * @@ -97,6 +108,8 @@ public class InsertOptions extends WriteOptions { private boolean ifNotExists; + private boolean insertNulls; + private InsertOptionsBuilder() {} private InsertOptionsBuilder(InsertOptions insertOptions) { @@ -104,6 +117,7 @@ public class InsertOptions extends WriteOptions { super(insertOptions); this.ifNotExists = insertOptions.ifNotExists; + this.insertNulls = insertOptions.insertNulls; } /* (non-Javadoc) @@ -247,6 +261,34 @@ public class InsertOptions extends WriteOptions { return this; } + /** + * Insert {@literal null} values from an entity. This allows the usage of {@code INSERT} statements as upsert by + * ensuring * that the whole entity state is persisted. Inserting {@literal null}s in Cassandra creates tombstones + * so this * option should be used with caution. + * + * @return {@code this} {@link InsertOptionsBuilder} + * @since 2.1 + */ + public InsertOptionsBuilder withInsertNulls() { + return withInsertNulls(true); + } + + /** + * Insert {@literal null} values from an entity. This allows the usage of {@code INSERT} statements as upsert by + * ensuring that the whole entity state is persisted. Inserting {@literal null}s in Cassandra creates tombstones so + * this option should be used with caution. + * + * @param insertNulls {@literal true} to enable insertion of {@literal null} values. + * @return {@code this} {@link InsertOptionsBuilder} + * @since 2.1 + */ + public InsertOptionsBuilder withInsertNulls(boolean insertNulls) { + + this.insertNulls = insertNulls; + + return this; + } + /** * Builds a new {@link InsertOptions} with the configured values. * @@ -254,7 +296,7 @@ public class InsertOptions extends WriteOptions { */ public InsertOptions build() { return new InsertOptions(this.consistencyLevel, this.retryPolicy, this.tracing, this.fetchSize, this.readTimeout, - this.ttl, this.timestamp, this.ifNotExists); + this.ttl, this.timestamp, this.ifNotExists, this.insertNulls); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java index 16c3e79f8..f84332881 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/QueryUtils.java @@ -16,16 +16,21 @@ package org.springframework.data.cassandra.core; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.data.cassandra.core.convert.CassandraConverter; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.cql.QueryOptionsUtil; import org.springframework.data.cassandra.core.cql.RowMapper; import org.springframework.data.cassandra.core.cql.WriteOptions; +import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.core.query.CassandraPageRequest; import org.springframework.data.convert.EntityWriter; import org.springframework.data.domain.PageRequest; @@ -64,17 +69,20 @@ class QueryUtils { * @param objectToUpdate the object to save, must not be {@literal null}. * @param options optional {@link WriteOptions} to apply to the {@link Insert} statement, may be {@literal null}. * @param entityWriter the {@link EntityWriter} to write insert values. + * @param entity must not be {@literal null}. * @return The Query object to run with session.execute(); */ static Insert createInsertQuery(String tableName, Object objectToUpdate, WriteOptions options, - EntityWriter entityWriter) { + CassandraConverter entityWriter, CassandraPersistentEntity entity) { Assert.hasText(tableName, "TableName must not be empty"); Assert.notNull(objectToUpdate, "Object to insert must not be null"); - Assert.notNull(entityWriter, "EntityWriter must not be null"); + Assert.notNull(entityWriter, "CassandraConverter must not be null"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); Insert insert = QueryOptionsUtil.addWriteOptions(QueryBuilder.insertInto(tableName), options); + boolean insertNulls = false; if (options instanceof InsertOptions) { InsertOptions insertOptions = (InsertOptions) options; @@ -82,9 +90,22 @@ class QueryUtils { if (insertOptions.isIfNotExists()) { insert = insert.ifNotExists(); } + + insertNulls = insertOptions.isInsertNulls(); } - entityWriter.write(objectToUpdate, insert); + if (insertNulls) { + + Map toInsert = new LinkedHashMap<>(); + + entityWriter.write(objectToUpdate, toInsert, entity); + + for (Entry entry : toInsert.entrySet()) { + insert.value(entry.getKey(), entry.getValue()); + } + } else { + entityWriter.write(objectToUpdate, insert); + } return insert; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java index f427412ba..cf0440040 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java @@ -15,13 +15,12 @@ */ package org.springframework.data.cassandra.core; -import java.util.function.Function; - import lombok.Value; - import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.function.Function; + import org.reactivestreams.Publisher; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; @@ -433,7 +432,9 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations, A Mono doInsert(Object entity, WriteOptions options, CqlIdentifier tableName) { - Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter()); + CassandraPersistentEntity persistentEntity = getRequiredPersistentEntity(entity.getClass()); + + Insert insert = QueryUtils.createInsertQuery(tableName.toCql(), entity, options, getConverter(), persistentEntity); // noinspection ConstantConditions Mono result = getReactiveCqlOperations().execute(new StatementCallback(insert)) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java index 0d939da0e..fda5baff5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepository.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.repository.support; -import static org.springframework.data.cassandra.core.query.Criteria.where; +import static org.springframework.data.cassandra.core.query.Criteria.*; import java.util.ArrayList; import java.util.List; @@ -23,6 +23,7 @@ import java.util.Optional; import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.core.InsertOptions; import org.springframework.data.cassandra.core.query.Query; import org.springframework.data.cassandra.repository.CassandraRepository; import org.springframework.data.cassandra.repository.query.CassandraEntityInformation; @@ -46,13 +47,15 @@ import com.datastax.driver.core.querybuilder.Select; */ public class SimpleCassandraRepository implements CassandraRepository { + private static final InsertOptions INSERT_NULLS = InsertOptions.builder().withInsertNulls().build(); + private final CassandraEntityInformation entityInformation; private final CassandraOperations operations; /** - * Create a new {@link SimpleCassandraRepository} for the given {@link CassandraEntityInformation} - * and {@link CassandraTemplate}. + * Create a new {@link SimpleCassandraRepository} for the given {@link CassandraEntityInformation} and + * {@link CassandraTemplate}. * * @param metadata must not be {@literal null}. * @param operations must not be {@literal null}. @@ -74,9 +77,7 @@ public class SimpleCassandraRepository implements CassandraRepository implements CassandraRepository implements CassandraRepository Insert createInsert(S entity) { return InsertUtil.createInsert(operations.getConverter(), entity); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleReactiveCassandraRepository.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleReactiveCassandraRepository.java index 243e6f909..2471e22ba 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleReactiveCassandraRepository.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/support/SimpleReactiveCassandraRepository.java @@ -18,13 +18,13 @@ package org.springframework.data.cassandra.repository.support; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.reactivestreams.Publisher; +import org.springframework.data.cassandra.core.InsertOptions; import org.springframework.data.cassandra.core.ReactiveCassandraOperations; import org.springframework.data.cassandra.repository.ReactiveCassandraRepository; import org.springframework.data.cassandra.repository.query.CassandraEntityInformation; import org.springframework.util.Assert; -import org.reactivestreams.Publisher; - import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; @@ -38,6 +38,8 @@ import com.datastax.driver.core.querybuilder.Select; */ public class SimpleReactiveCassandraRepository implements ReactiveCassandraRepository { + private static final InsertOptions INSERT_NULLS = InsertOptions.builder().withInsertNulls().build(); + private final CassandraEntityInformation entityInformation; private final ReactiveCassandraOperations operations; @@ -67,7 +69,7 @@ public class SimpleReactiveCassandraRepository implements ReactiveCassand Assert.notNull(entity, "Entity must not be null"); - return operations.getReactiveCqlOperations().execute(createInsert(entity)).map(it -> entity); + return operations.insert(entity, INSERT_NULLS).thenReturn(entity); } /** @@ -75,6 +77,8 @@ public class SimpleReactiveCassandraRepository implements ReactiveCassand * * @param entity the entity, must not be {@literal null}. * @return the constructed {@link Insert} statement. + * @deprecated since 2.1, use {@link InsertOptions#isInsertNulls()} with + * {@link ReactiveCassandraOperations#insert(Object, InsertOptions)}. */ private Insert createInsert(S entity) { return InsertUtil.createInsert(operations.getConverter(), entity); @@ -100,7 +104,7 @@ public class SimpleReactiveCassandraRepository implements ReactiveCassand Assert.notNull(entityStream, "The given Publisher of entities must not be null"); return Flux.from(entityStream) - .flatMap(entity -> operations.getReactiveCqlOperations().execute(createInsert(entity)).map(it -> entity)); + .flatMap(entity -> operations.insert(entity, INSERT_NULLS).thenReturn(entity)); } /* (non-Javadoc) diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java index 716ccdfb2..fa02dcdf4 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/CassandraTemplateUnitTests.java @@ -15,14 +15,11 @@ */ package org.springframework.data.cassandra.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; @@ -36,7 +33,6 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; - import org.springframework.data.cassandra.CassandraConnectionFailureException; import org.springframework.data.cassandra.core.query.Query; import org.springframework.data.cassandra.domain.User; @@ -250,8 +246,24 @@ public class CassandraTemplateUnitTests { .isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES ('Walter','heisenberg','White') IF NOT EXISTS;"); } + @Test // DATACASS-560 + public void insertShouldInsertWithNulls() { + + InsertOptions insertOptions = InsertOptions.builder().withInsertNulls().build(); + + when(resultSet.wasApplied()).thenReturn(true); + + User user = new User("heisenberg", null, null); + + template.insert(user, insertOptions); + + verify(session).execute(statementCaptor.capture()); + assertThat(statementCaptor.getValue().toString()) + .isEqualTo("INSERT INTO users (firstname,id,lastname) VALUES (null,'heisenberg',null);"); + } + @Test // DATACASS-292 - public void insertShouldTranslateException() throws Exception { + public void insertShouldTranslateException() { reset(session); when(session.execute(any(Statement.class))).thenThrow(new NoHostAvailableException(Collections.emptyMap())); @@ -351,7 +363,7 @@ public class CassandraTemplateUnitTests { } @Test // DATACASS-292 - public void deleteShouldTranslateException() throws Exception { + public void deleteShouldTranslateException() { reset(session); when(session.execute(any(Statement.class))).thenThrow(new NoHostAvailableException(Collections.emptyMap())); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryIntegrationTests.java index f78bf6194..d6ba02b97 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryIntegrationTests.java @@ -15,23 +15,29 @@ */ package org.springframework.data.cassandra.repository.support; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener; +import org.springframework.data.cassandra.core.mapping.event.AfterSaveEvent; +import org.springframework.data.cassandra.core.mapping.event.BeforeSaveEvent; +import org.springframework.data.cassandra.core.mapping.event.CassandraMappingEvent; import org.springframework.data.cassandra.core.query.CassandraPageRequest; import org.springframework.data.cassandra.domain.User; import org.springframework.data.cassandra.repository.CassandraRepository; @@ -59,9 +65,15 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC public String[] getEntityBasePackages() { return new String[] { User.class.getPackage().getName() }; } + + @Bean + CaptureEventListener eventListener() { + return new CaptureEventListener(); + } } @Autowired private CassandraOperations operations; + @Autowired private CaptureEventListener eventListener; private BeanFactory beanFactory; private CassandraRepositoryFactory factory; @@ -99,6 +111,8 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC boyd = new User("45", "Boyd", "Tinsley"); repository.saveAll(Arrays.asList(oliver, dave, carter, boyd)); + + eventListener.clear(); } @Test // DATACASS-396 @@ -217,6 +231,18 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC }); } + @Test // DATACASS-560 + public void saveShouldEmitEvents() { + + dave.setFirstname("Hello, Dave"); + dave.setLastname("Bowman"); + + repository.save(dave); + + assertThat(eventListener.getBeforeSave()).hasSize(1); + assertThat(eventListener.getAfterSave()).hasSize(1); + } + @Test // DATACASS-396 public void saveEntityShouldInsertNewEntity() { @@ -304,4 +330,35 @@ public class SimpleCassandraRepositoryIntegrationTests extends AbstractKeyspaceC interface UserRepostitory extends CassandraRepository { } + static class CaptureEventListener extends AbstractCassandraEventListener { + + private final List> events = new CopyOnWriteArrayList<>(); + + @Override + public void onBeforeSave(BeforeSaveEvent event) { + events.add(event); + } + + @Override + public void onAfterSave(AfterSaveEvent event) { + events.add(event); + } + + private void clear() { + events.clear(); + } + + List> getBeforeSave() { + return filter(BeforeSaveEvent.class); + } + + List> getAfterSave() { + return filter(AfterSaveEvent.class); + } + + @SuppressWarnings("unchecked") + private List filter(Class targetType) { + return (List) events.stream().filter(targetType::isInstance).map(targetType::cast).collect(Collectors.toList()); + } + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryUnitTests.java index f960128ae..d47cf856e 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/support/SimpleCassandraRepositoryUnitTests.java @@ -15,15 +15,12 @@ */ package org.springframework.data.cassandra.repository.support; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.Serializable; +import static org.mockito.Mockito.*; import lombok.Data; +import java.io.Serializable; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,11 +28,10 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; 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.InsertOptions; import org.springframework.data.cassandra.core.convert.MappingCassandraConverter; -import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.cql.CqlOperations; import org.springframework.data.cassandra.core.cql.QueryOptions; import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; @@ -72,15 +68,10 @@ public class SimpleCassandraRepositoryUnitTests { @Before public void before() { - mappingContext.setUserTypeResolver(userTypeResolver); - - when(cassandraOperations.getConverter()).thenReturn(converter); - when(cassandraOperations.getCqlOperations()).thenReturn(cqlOperations); - when(userTypeResolver.resolveType(CqlIdentifier.of("address"))).thenReturn(userType); } - @Test // DATACASS-428 + @Test // DATACASS-428, DATACASS-560 public void saveShouldInsertNewPrimaryKeyOnlyEntity() { CassandraPersistentEntity entity = converter.getMappingContext().getRequiredPersistentEntity(SimplePerson.class); @@ -92,11 +83,10 @@ public class SimpleCassandraRepositoryUnitTests { repository.save(person); - verify(cqlOperations).execute(insertCaptor.capture()); - assertThat(insertCaptor.getValue().toString()).isEqualTo("INSERT INTO simpleperson (id) VALUES (null);"); + verify(cassandraOperations).insert(person, InsertOptions.builder().withInsertNulls().build()); } - @Test // DATACASS-428 + @Test // DATACASS-428, DATACASS-560 public void saveShouldUpdateNewEntity() { CassandraPersistentEntity entity = converter.getMappingContext().getRequiredPersistentEntity(Person.class); @@ -108,10 +98,10 @@ public class SimpleCassandraRepositoryUnitTests { repository.save(person); - verify(cqlOperations).execute(any(Insert.class)); + verify(cassandraOperations).insert(person, InsertOptions.builder().withInsertNulls().build()); } - @Test // DATACASS-428 + @Test // DATACASS-428, DATACASS-560 public void saveShouldUpdateExistingEntity() { CassandraPersistentEntity entity = converter.getMappingContext().getRequiredPersistentEntity(Person.class); @@ -125,10 +115,7 @@ public class SimpleCassandraRepositoryUnitTests { repository.save(person); - verify(cqlOperations).execute(insertCaptor.capture()); - assertThat(insertCaptor.getValue().toString()) - .contains("INSERT INTO person (lastname,firstname,alternativeaddresses,"); - assertThat(insertCaptor.getValue().toString()).contains("VALUES ('bar','foo',null"); + verify(cassandraOperations).insert(person, InsertOptions.builder().withInsertNulls().build()); } @Test // DATACASS-428