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:
@@ -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 -->
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user