We now no longer use a Scheduler to offload ResultSet's blocking paging but request the next result page asynchronously by adapting ResultSet.fetchMoreResults(). Result pages are requested once all elements of the previous ResultSet are emitted and the row publisher completes successfully. The Scheduler is no longer required.
The request progress is stored in a MonoProcessor to extend the result stream. Increase visibility of utility methods to avoid synthetic accessor creation.
We now resolve user-defined type references to type stubs when constructing the create specification for a user-defined type instead of looking up the type from Cassandra if the particular property is annotated with @CassandraType(type = UDT). This applies to user-defined types nested in the to-be-created type. Stubbing is necessary to not prevent user-type creation during create specification construction. The actual execution happens after all creation specifications are built.
We now support keyspace alteration using XML configuration during after CassandraClusterFactoryBean initialization.
Previously, ALTER keyspace actions resulted in IllegalStateException.
We now support forward-only paging with Cassandra through the Template API and Repositories. Results in Cassandra are paged by navigating forward-only through pages described by a binary paging state encapsulated by CassandraPageRequest and accessible via the returned Slice. Spring Data Page's do not fit to Cassandra's paging concept because Cassandra paging is not based on limit/offset.
Page requests are applicable to a Query and as parameter of query methods.
Query query = Query.empty().pageRequest(CassandraPageRequest.first(10));
Slice<User> slice = template.slice(query, User.class);
do {
// consume slice
if (slice.hasNext()) {
slice = template.select(query, slice.nextPageable(), User.class);
} else {
break;
}
} while (!slice.getContent().isEmpty());
assertThat(ids).hasSize(100);
assertThat(iterations).isEqualTo(10);
interface UserRepository implements Repository<User, String> {
Slice<User> findAllByName(String name, Pageable pageRequest);
}
We now allow query option mutation through QueryOptions.mutate() returning an initialized builder. The mutation builder is initialized with the state of the QueryOptions object and allows further customization without changing the previous state of the immutable QueryOptions object.
QueryOptions queryOptions = …;
QueryOptions mutated = queryOptions.mutate().readTimeout(Duration.ofSeconds(5)).build();
We now support Repository query methods with query options. Query options can be passed either as an additional parameter to a Repository query method or applied with annotation.
Annotation-based query options are supported via @Consistency. A query options parameter has precedence over the annotation if a method declares both, an annotation-based consistency level and accepts a query options parameter.
interface SampleRepository extends Repository<Person, String> {
@Query("SELECT * FROM person WHERE lastname = ?0;")
@Consistency(ConsistencyLevel.LOCAL_ONE)
Person findByLastname(String lastname);
@Consistency(ConsistencyLevel.LOCAL_ONE)
Person findByAge(int age);
Person findByAge(int age, QueryOptions options);
}
SampleRepository repository = …;
repository.findByAge(42, QueryOptions.builder().fetchSize(44).build());
Expose createInsert(…) in the synchronous and reactive repository base classes to simplify base class extension. Derived classes are no longer required to implement createInsert(…) themselves but can reuse the existing methods. Extract createInsert(…) body to InsertUtil.