Adding element to set via update query fails [DATACASS-770] #906
Comments
Mark Paluch commented Good catch. Do you want to submit a pull request to fix the issue as you've already went through the code? |
Marko commented Yes, I will do it today |
Mark Paluch commented Can you share a bit more of details here as the unit tests in the PR succeed without the change and integration tests succeed as well. Fixing the bug in |
Marko commented Obviously I was a little bit naive since it seemed like an obvious bug :) I drilled down a bit and this are my findings: @Table("foo")
public class Foo {
@PrimaryKeyColumn(name = "foo_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private UUID fooId;
@PrimaryKeyColumn(name = "type_id", ordinal = 1, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.ASCENDING)
private String typeId;
@Column("set_col") @CassandraType(type = CassandraType.Name.SET, typeArguments = CassandraType.Name.UUID)
private Set<UUID> set; } Everything works as expected (without fix) as long as you use attribute name (set) as a column name in addTo() and not column name used in database ("set_col"). addTo("set") > works (Tests with success: 2, 3, 8, 9, other (with "sel_col") fail). Test number can be seen below in Tests, next to DATACASS-770. If we fix the line 291 in DefaultColumnTypeResolved.java (change from listOf -> setOf), also the tests 6 and 7 pass. The problem remains with the appendAll and prependAll. I dug further and found that the problem is that the field property is not set if we have a column name ("set_col") and not attribute name ("set"). The reason is that field.property (QueryMapper.java line 123) info is obtained from the map by the attribute name ("set"), which returns null in case of a column name ("set_col"). Because of that, the object is not converted to the correct value as in case of attribute name (Updatemapper.java line 205 -> if clause is false in case of column name ("set_col") ) I am not sure if it was meant by design to even use the column names (as they are in the database), but on the other side I used them on all other parts of the query, and even the documentation says (columnName)? What would you suggest how to resolve this issue, if it even is an issue? :) Let me know what should I include in the pull request.
Tests src/test/java/org/springframework/data/cassandra/core/convert/UpdateMapperUnitTests.java:
@Test // DATACASS-770 - TEST 1
public void shouldPrependAllToSetViaColumnName() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set_col").prependAll("foo", currency), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = {'foo','Euro'} + set_col"); }
@Test // DATACASS-770 - TEST 2
public void shouldPrependAllToSet() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set").prependAll("foo", currency), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = {'foo','Euro'} + set_col"); }
@Test // DATACASS-770 - TEST 3
public void shouldAppendAllToSet() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set").appendAll("foo", currency), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'foo','Euro'}"); }
@Test // DATACASS-770 - TEST 4
public void shouldAppendAllToSetViaColumnName() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set_col").appendAll("foo", currency), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'foo','Euro'}"); }
@Test // DATACASS-770 - TEST 5
public void shouldAppendAllToSetViaColumnNameSingleElement() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set_col").appendAll(currencyUSD), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'US Dollar'}"); }
@Test // DATACASS-770 - TEST 6
public void shouldAppendAllToSetViaColumnNameCollectionOfElement() {
Set<Currency> tmp = new LinkedHashSet<>();
tmp.add(currencyUSD);
tmp.add(currency);
Update update = updateMapper.getMappedObject(Update.empty().addTo("set_col").appendAll(tmp), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'US Dollar','Euro'}");
}
@Test // DATACASS-770 - TEST 7
public void shouldAppendToSetViaColumnName() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set_col").append("foo"), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'foo'}");
}
@Test // DATACASS-770 - TEST 8
public void shouldAppendToSet() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set").append("foo"), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'foo'}");
}
@Test // DATACASS-770 - TEST 9
public void shouldRemoveFromSet() {
Update update = updateMapper.getMappedObject(Update.empty().remove("set", currency), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col - {'Euro'}");
}
|
Mark Paluch commented Thanks for your feedback. While you can use both, property names and column names, we need to look at what's possible in which case. Column names are generally not attempted for resolution against the model. Therefore we need to use in query the type information that is available from the query/update. In this case,
The preferred approach, therefore, is to use property names as we can map query and update objects against these. Collection values from e.g. Feel free to include two tests for the cases of column name usage and property name usage in |
Marko commented I added additional tests as suggested and committed all the changes. I removed the following test: public void shouldAppendToSetViaColumnName() {
Update update = updateMapper.getMappedObject(Update.empty().addTo("set_col").append("foo"), persistentEntity);
assertThat(update.getUpdateOperations()).hasSize(1);
assertThat(update.toString()).isEqualTo("set_col = set_col + {'foo'}");
} since this is only working, because the current implementation of append() calls appendAll() with a collection of a Set type. public Update append(Object value) {
return appendAll(Collections.singleton(value));
} Thanks for all the clarifications. I also managed to get my code working now. :) |
Marko opened DATACASS-770 and commented
When you try to add a new element to a Set the query fails since it is transformed as adding an element to a List.
Example:
While debugging I found the source of a problem. In class DefaultColumnTypeResolved.java in package org.springframework.data.cassandra.core.convert in the line 293. Currently is:
Affects: 3.0 GA (Neumann)
Referenced from: pull request #174
Backported to: 3.0.1 (Neumann SR1)
The text was updated successfully, but these errors were encountered: