DATACASS-206 - Fix delete using custom column names.

We now use the specified column name from the entity annotation to construct the where clause in a delete statement.

Original pull request: #73.
This commit is contained in:
Mark Paluch
2016-06-30 15:04:39 +02:00
committed by John Blum
parent f63d2cb26a
commit 580adfb5c1
5 changed files with 187 additions and 14 deletions

View File

@@ -9,6 +9,8 @@
<logger name="org.springframework" level="ERROR" />
<logger name="org.springframework.cassandra" level="ERROR" />
<logger name="org.springframework.data.cassandra" level="ERROR" />
<logger name="com.datastax" level="ERROR" />
<!-- See https://issues.apache.org/jira/browse/CASSANDRA-8220 -->

View File

@@ -358,7 +358,13 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
if (id instanceof MapId) {
for (Map.Entry<String, Serializable> entry : ((MapId) id).entrySet()) {
where.and(QueryBuilder.eq(entry.getKey(), entry.getValue()));
CassandraPersistentProperty persistentProperty = entity.getPersistentProperty(entry.getKey());
if (persistentProperty != null) {
where.and(QueryBuilder.eq(persistentProperty.getColumnName().toCql(), entry.getValue()));
} else {
where.and(QueryBuilder.eq(entry.getKey(), entry.getValue()));
}
}
return;
}

View File

@@ -33,11 +33,13 @@ import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -52,6 +54,7 @@ import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.core.SpringVersion;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.data.cassandra.RowMockUtil;
import org.springframework.data.cassandra.domain.UserToken;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraType;
@@ -69,6 +72,7 @@ import com.datastax.driver.core.Row;
import com.datastax.driver.core.querybuilder.Assignment;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Delete.Where;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
@@ -742,6 +746,45 @@ public class MappingCassandraConverterUnitTests {
assertThat(getAssignmentValues(update), contains((Object) LocalDate.fromYearMonthDay(2010, 7, 4)));
}
/**
* @see DATACASS-206
*/
@Test
public void updateShouldUseSpecifiedColumnNames() {
UserToken userToken = new UserToken();
userToken.setUserId(UUID.randomUUID());
userToken.setToken(UUID.randomUUID());
userToken.setAdminComment("admin comment");
userToken.setUserComment("user comment");
Update update = QueryBuilder.update("table");
mappingCassandraConverter.write(userToken, update);
assertThat(getAssignments(update), hasEntry("admincomment", (Object) "admin comment"));
assertThat(getAssignments(update), hasEntry("user_comment", (Object) "user comment"));
assertThat(getWherePredicates(update), hasEntry("user_id", (Object) userToken.getUserId()));
}
/**
* @see DATACASS-206
*/
@Test
public void deleteShouldUseSpecifiedColumnNames() {
UserToken userToken = new UserToken();
userToken.setUserId(UUID.randomUUID());
userToken.setToken(UUID.randomUUID());
userToken.setAdminComment("admin comment");
userToken.setUserComment("user comment");
Delete delete = QueryBuilder.delete().from("table");
mappingCassandraConverter.write(userToken, delete.where());
assertThat(getWherePredicates(delete), hasEntry("user_id", (Object) userToken.getUserId()));
}
@SuppressWarnings("unchecked")
private <T> List<T> getListValue(Insert statement) {
List<Object> values = getValues(statement);
@@ -774,30 +817,47 @@ public class MappingCassandraConverterUnitTests {
}
@SuppressWarnings("unchecked")
private List<Object> getAssignmentValues(Update statement) {
private Collection<Object> getAssignmentValues(Update statement) {
return getAssignments(statement).values();
}
List<Object> result = new ArrayList<Object>();
@SuppressWarnings("unchecked")
private Map<String, Object> getAssignments(Update statement) {
Map<String, Object> result = new LinkedHashMap<String, Object>();
Assignments assignments = (Assignments) ReflectionTestUtils.getField(statement, "assignments");
List<Assignment> listOfAssignments = (List<Assignment>) ReflectionTestUtils.getField(assignments, "assignments");
for (Assignment assignment : listOfAssignments) {
result.add(ReflectionTestUtils.getField(assignment, "value"));
result.put(assignment.getColumnName(), ReflectionTestUtils.getField(assignment, "value"));
}
return result;
}
private List<Object> getWhereValues(Update statement) {
return getWhereValues(statement.where());
private Collection<Object> getWhereValues(Update update) {
return getWherePredicates(update.where()).values();
}
private List<Object> getWhereValues(BuiltStatement where) {
private Collection<Object> getWhereValues(BuiltStatement where) {
return getWherePredicates(where).values();
}
List<Object> result = new ArrayList<Object>();
private Map<String, Object> getWherePredicates(Update statement) {
return getWherePredicates(statement.where());
}
private Map<String, Object> getWherePredicates(Delete statement) {
return getWherePredicates(statement.where());
}
private Map<String, Object> getWherePredicates(BuiltStatement where) {
Map<String, Object> result = new LinkedHashMap<String, Object>();
List<Clause> clauses = (List<Clause>) ReflectionTestUtils.getField(where, "clauses");
for (Clause clause : clauses) {
result.add(ReflectionTestUtils.getField(clause, "value"));
result.put((String) ReflectionTestUtils.invokeMethod(clause, "name"), ReflectionTestUtils.getField(clause, "value"));
}
return result;

View File

@@ -0,0 +1,44 @@
/*
* Copyright 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.
* 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.domain;
import java.util.UUID;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import com.datastax.driver.core.DataType.Name;
import lombok.Data;
/**
* @author Mark Paluch
*/
@Table("user_tokens")
@Data
public class UserToken {
@PrimaryKeyColumn(name = "user_id", type = PrimaryKeyType.PARTITIONED,
ordinal = 0) @CassandraType(type = Name.UUID) private UUID userId;
@PrimaryKeyColumn(name = "auth_token", type = PrimaryKeyType.CLUSTERED,
ordinal = 1) @CassandraType(type = Name.UUID) private UUID token;
@Column("user_comment") String userComment;
String adminComment;
}

View File

@@ -36,6 +36,8 @@ 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.domain.UserToken;
import org.springframework.data.cassandra.repository.support.BasicMapId;
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;
@@ -46,6 +48,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.utils.UUIDs;
/**
* Integration tests for {@link CassandraOperations}.
@@ -63,12 +66,11 @@ public class CassandraOperationsIntegrationTests extends AbstractSpringDataEmbed
@Override
public String[] getEntityBasePackages() {
return new String[] { Book.class.getPackage().getName() };
return new String[] { Book.class.getPackage().getName(), UserToken.class.getPackage().getName() };
}
}
@Autowired
CassandraOperations template;
@Autowired CassandraOperations template;
@Before
public void before() {
@@ -749,13 +751,72 @@ public class CassandraOperationsIntegrationTests extends AbstractSpringDataEmbed
assertThat(selectedBooks.get(0), is(instanceOf(Book.class)));
}
/**
* @see <a href="https://jira.spring.io/browse/DATACASS-206">DATACASS-206</a>
*/
@Test
public void shouldUseSpecifiedColumnNamesForSingleEntityModifyingOperations() {
UserToken userToken = new UserToken();
userToken.setToken(UUIDs.startOf(System.currentTimeMillis()));
userToken.setUserId(UUIDs.endOf(System.currentTimeMillis()));
template.insert(userToken);
userToken.setUserComment("comment");
template.update(userToken);
UserToken loaded = template.selectOneById(UserToken.class,
BasicMapId.id("userId", userToken.getUserId()).with("token", userToken.getToken()));
assertThat(loaded, is(notNullValue()));
assertThat(loaded.getUserComment(), is(equalTo("comment")));
template.delete(userToken);
UserToken loadAfterDelete = template.selectOneById(UserToken.class,
BasicMapId.id("userId", userToken.getUserId()).with("token", userToken.getToken()));
assertThat(loadAfterDelete, is(nullValue()));
}
/**
* @see <a href="https://jira.spring.io/browse/DATACASS-206">DATACASS-206</a>
*/
@Test
public void shouldUseSpecifiedColumnNamesForMultiEntityModifyingOperations() {
UserToken userToken = new UserToken();
userToken.setToken(UUIDs.startOf(System.currentTimeMillis()));
userToken.setUserId(UUIDs.endOf(System.currentTimeMillis()));
template.insert(Arrays.asList(userToken));
userToken.setUserComment("comment");
template.update(Arrays.asList(userToken));
UserToken loaded = template.selectOneById(UserToken.class,
BasicMapId.id("userId", userToken.getUserId()).with("token", userToken.getToken()));
assertThat(loaded, is(notNullValue()));
assertThat(loaded.getUserComment(), is(equalTo("comment")));
template.delete(Arrays.asList(userToken));
UserToken loadAfterDelete = template.selectOneById(UserToken.class,
BasicMapId.id("userId", userToken.getUserId()).with("token", userToken.getToken()));
assertThat(loadAfterDelete, is(nullValue()));
}
WriteOptions newWriteOptions(ConsistencyLevel consistencyLevel, RetryPolicy retryPolicy, int timeToLive) {
return new WriteOptions(consistencyLevel, retryPolicy, timeToLive);
}
<T> Iterable<T> toIterable(final Iterator<T> iterator) {
return new Iterable<T>() {
@Override public Iterator<T> iterator() {
@Override
public Iterator<T> iterator() {
return iterator;
}
};