We now support TupleValue as simple Cassandra type within domain objects along with table schema generation. Tuple columns must be annotated with @CassandraType to derive the according Cassandra column type for schema generation.
@Table
class Person {
@Id
String id;
@CassandraType(type = Name.TUPLE, typeArguments = { Name.VARCHAR, Name.BIGINT })
TupleValue tupleValue;
}
We now provide an alternative API for CassandraOperations that allows defining operations in a fluent way. FluentCassandraOperations reduces the number of methods and strips down the interface to a minimum while offering a more readable API.
// select with filter query and projecting return type
template.query(Person.class)
.as(Jedi.class)
.matching(query(where("firstname").is("luke")))
.all();
// insert
template.insert(Person.class)
.inTable(STAR_WARS)
.one(luke);
// update
template.update(Person.class)
.apply(update("firstname", "Han"))
.matching(query(where("id").is("han-solo")))
.all();
// remove all matching
template.delete(Jedi.class)
.inTable(STAR_WARS)
.matching(query(where("name").is("luke")))
.all();
We now support exists and count projections derived from query methods and on Template API level.
Exists queries fetch rows limiting to a single row to optimize fetching.
Exists queries also support count results, i.e. a single row with a single numeric column is considered a count result. It's evaluated by comparing the number being greater to zero.
boolean exists = template.exists(Query.query(where("firstname").is("Walter")), Person.class);
long count = template.count(Query.query(where("lastname").is("White")), Person.class);
interface PersonRepository extends Repository<Person, String> {
long countByLastname(String lastname); // derived query
boolean existsByLastname(String lastname); // derived query
@CountQuery("SELECT COUNT(*) from users WHERE lastname = ?0")
long countQueryByLastname(String lastname); // String-based query
@ExistsQuery("SELECT * from users WHERE lastname = ?0 LIMIT 1")
boolean existsQueryByLastname(String lastname); // String-based query
}
Remove superfluous throws declarations.
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);
}