From e10c71722dec1f1caea5b71e2133ec23e79762fb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 28 Jun 2016 13:45:57 +0200 Subject: [PATCH] DATACASS-182 - Add ability to update/store null into fields. We now support the removal of columns for properties with null values by allowing null values in Insert and Update statements written by MappingCassandraConverter. Objects are stored with all persistent properties. Insert and update perform no longer conditional inserting of non-null values but take all values into account. Original pull request: #72. --- .../convert/MappingCassandraConverter.java | 28 +++--- .../support/SimpleCassandraRepository.java | 3 +- .../MappingCassandraConverterUnitTests.java | 2 +- .../CassandraOperationsIntegrationTests.java | 90 ++++++++++++++++++- .../UserRepositoryIntegrationTests.java | 19 +++- ...erRepositoryIntegrationTestsDelegator.java | 9 ++ 6 files changed, 136 insertions(+), 15 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java index 736503350..81b921e89 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java @@ -272,12 +272,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return; } - if (value != null) { - if (log.isDebugEnabled()) { - log.debug("Adding insert.value [{}] - [{}]", prop.getColumnName().toCql(), value); - } - insert.value(prop.getColumnName().toCql(), value); + if (log.isDebugEnabled()) { + log.debug("Adding insert.value [{}] - [{}]", prop.getColumnName().toCql(), value); } + insert.value(prop.getColumnName().toCql(), value); } }); } @@ -303,12 +301,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return; } - if (value != null) { - if (prop.isIdProperty() || entity.isCompositePrimaryKey() || prop.isPrimaryKeyColumn()) { - update.where(QueryBuilder.eq(prop.getColumnName().toCql(), value)); - } else { - update.with(QueryBuilder.set(prop.getColumnName().toCql(), value)); - } + if (isPrimaryKeyPart(prop)) { + update.where(QueryBuilder.eq(prop.getColumnName().toCql(), value)); + } else { + update.with(QueryBuilder.set(prop.getColumnName().toCql(), value)); } } }); @@ -444,4 +440,14 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return new ConvertingPropertyAccessor(accessor, conversionService); } + + /** + * Returns whether the property is part of the primary key. + * + * @param property + * @return + */ + private boolean isPrimaryKeyPart(CassandraPersistentProperty property) { + return property.isCompositePrimaryKey() || property.isPrimaryKeyColumn() || property.isIdProperty(); + } } 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 2725ba3a5..ec7eeb0a9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors + * Copyright 2013-2016 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. @@ -32,6 +32,7 @@ import com.datastax.driver.core.querybuilder.Select; * * @author Alex Shvid * @author Matthew T. Adams + * @author Mark Paluch */ public class SimpleCassandraRepository implements TypedIdCassandraRepository { diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java index f4158fba5..28cbbc0ee 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java @@ -103,7 +103,7 @@ public class MappingCassandraConverterUnitTests { mappingCassandraConverter.write(withEnumColumns, insert); - assertThat(getValues(insert), contains((Object) "MINT")); + assertThat(getValues(insert), hasItem((Object) "MINT")); } /** diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/CassandraOperationsIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/CassandraOperationsIntegrationTests.java index bb16e2353..f3d62a48f 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/CassandraOperationsIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/CassandraOperationsIntegrationTests.java @@ -19,6 +19,8 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; @@ -34,13 +36,16 @@ import org.springframework.cassandra.core.RetryPolicy; import org.springframework.cassandra.core.WriteOptions; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.core.CassandraOperations; +import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.test.integration.simpletons.Book; import org.springframework.data.cassandra.test.integration.simpletons.BookCondition; +import org.springframework.data.cassandra.test.integration.simpletons.BookReference; import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest; import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import com.datastax.driver.core.querybuilder.Delete; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; @@ -691,7 +696,6 @@ public class CassandraOperationsIntegrationTests extends AbstractSpringDataEmbed assertThat(template.count(Book.class), is(equalTo(count))); } - @Test public void insertAndSelect() { @@ -703,6 +707,90 @@ public class CassandraOperationsIntegrationTests extends AbstractSpringDataEmbed assertThat(template.count(Book.class), is(equalTo(count))); } + /** + * @see DATACASS-182 + */ + @Test + public void updateShouldRemoveFields() { + + Book book = new Book(); + book.setIsbn("isbn"); + book.setTitle("title"); + book.setAuthor("author"); + + template.insert(book); + + book.setTitle(null); + template.update(book); + + Book loaded = template.selectOneById(Book.class, book.getIsbn()); + + assertThat(loaded.getTitle(), is(nullValue())); + assertThat(loaded.getAuthor(), is(equalTo("author"))); + } + + /** + * @see DATACASS-182 + */ + @Test + public void insertShouldRemoveFields() { + + Book book = new Book(); + book.setIsbn("isbn"); + book.setTitle("title"); + book.setAuthor("author"); + + template.insert(book); + + book.setTitle(null); + template.insert(book); + + Book loaded = template.selectOneById(Book.class, book.getIsbn()); + + assertThat(loaded.getTitle(), is(nullValue())); + assertThat(loaded.getAuthor(), is(equalTo("author"))); + } + + /** + * @see DATACASS-182 + */ + @Test + public void updateShouldInsertEntity() { + + Book book = new Book(); + book.setIsbn("isbn"); + book.setTitle("title"); + book.setAuthor("author"); + + template.update(book); + + Book loaded = template.selectOneById(Book.class, book.getIsbn()); + + assertThat(loaded, is(notNullValue())); + } + + /** + * @see DATACASS-182 + */ + @Test + public void insertAndUpdateToEmptyCollection() { + + BookReference bookReference = new BookReference(); + + bookReference.setIsbn("isbn"); + bookReference.setBookmarks(Arrays.asList(1, 2, 3, 4)); + + template.insert(bookReference); + + bookReference.setBookmarks(Collections. emptyList()); + template.update(bookReference); + + BookReference loaded = template.selectOneById(BookReference.class, bookReference.getIsbn()); + + assertThat(loaded.getTitle(), is(nullValue())); + assertThat(loaded.getBookmarks(), is(nullValue())); + } + /** * @see DATACASS-297 */ diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTests.java index 7f14d3a82..4a39481cd 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTests.java @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.Assert; @@ -32,6 +33,7 @@ import com.google.common.collect.Lists; * @author Alex Shvid * @author Matthew T. Adams * @author David Webb + * @author Mark Paluch */ public class UserRepositoryIntegrationTests { @@ -150,6 +152,22 @@ public class UserRepositoryIntegrationTests { } + /** + * @see DATACASS-182 + */ + public void save() { + + tom.setPassword(null); + tom.setFriends(Collections.emptySet()); + + repository.save(tom); + + User loadedTom = repository.findOne(tom.getUsername()); + + assertThat(loadedTom.getPassword(), is(nullValue())); + assertThat(loadedTom.getFriends(), is(nullValue())); + } + private static void assertEquals(User user1, User user2) { Assert.assertEquals(user1.getUsername(), user2.getUsername()); Assert.assertEquals(user1.getFirstName(), user2.getFirstName()); @@ -157,5 +175,4 @@ public class UserRepositoryIntegrationTests { Assert.assertEquals(user1.getPlace(), user2.getPlace()); Assert.assertEquals(user1.getPassword(), user2.getPassword()); } - } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTestsDelegator.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTestsDelegator.java index 59454a9b2..98270c051 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTestsDelegator.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/simple/UserRepositoryIntegrationTestsDelegator.java @@ -26,6 +26,7 @@ import org.springframework.data.cassandra.test.integration.support.AbstractSprin * * @author Matthew T. Adams * @author David Webb + * @author Mark Paluch */ public abstract class UserRepositoryIntegrationTestsDelegator extends AbstractSpringDataEmbeddedCassandraIntegrationTest { @@ -75,4 +76,12 @@ public abstract class UserRepositoryIntegrationTestsDelegator public void exists() { tests.exists(); } + + /** + * @see DATACASS-182 + */ + @Test + public void save() { + tests.save(); + } }