How can I ensure CassandraOperations.selectOneById

2019-03-03 20:03发布

问题:

I'm using Spring Data Cassandra 1.3.4.RELEASE to persist instances of a class that I have. The class is written in Groovy, but I don't think that really matters. I have implemented a CrudRepository, and I'm injecting an instance of CassandraOperations into the repo implementation class. I can insert, delete, and do most of the other operations successfully. However, there's a scenario I'm running into which breaks my test case. My entity class looks something like this:

@Table("foo")
class FooData {
  @PrimaryKey("id")
  long id
  @Column("created")
  long updated
  @Column("name")
  String name
  @Column("user_data")
  String userData
  @Column("numbers")
  List numberList = []
}

In my test case, I happened to only set a few fields like 'id' and 'updated' before calling CassandraOperations.insert(entity), so most of them were null in the entity instance at the time of insertion. But the numberList field was not null, it was an empty List. Directly after the insert(), I'm calling CassandraOperations.selectOneById(FooData.class, id). I get a FooData instance back, and the fields that were initialized when I saved it are populated with data. However, I was checking content equality in my test, and it failed because the empty list was not returned as an empty list in the POJO coming back from CassandraOperations.selectOneById(). It's actually null. I assume this may be some sort of Cassandra optimization. It seems to happen in the CGLIB code that instantiates the POJO/entity. Is this a known "feature"? Is there some annotation I can mark the 'numberList' field with to indicate that it cannot be null? Any leads are appreciated. Thanks.

回答1:

In short

Cassandra stores empty collections as null and Spring Data Cassandra overwrites initialized fields.

Explanation

Cassandra list/set typed columns represent an empty collection as null. It does not matter whether the list/set (as viewed from Java/Groovy) was empty or null. Storing an empty list yields therefore in null. From here one can't tell whether the state was null or empty at the time saving the value.

Spring Data Cassandra overwrites all fields with values retrieved from the result set and so your pre-initialized fields is set to null. I created a ticket DATACASS-266 to track the state of this issue.

Workaround

Spring Data uses setters if possible so you have a chance to intervene. A very simple null guard could be:

public void setMyList(List<Long> myList) { if(myList == null){ this.myList = new ArrayList<>(); return; } this.myList = myList; }



回答2:

As important addition to mp911de answer you have to set @org.springframework.data.annotation.AccessType(AccessType.Type.PROPERTY) to make this solution work.