- Remove version placeholders for reactor and rxjava
- Adopt type migration of ReactiveWrappers
- Adopt RxJava to RxJava1 repository interface renaming
- Use ReactiveQueryMethod in ReactiveMongoQuery.
- Remove trailing whitespaces.
- Use ReflectionUtils for method iteration in ReactiveType.
We now support reactive data access with Spring Data Cassandra by adopting Datastax' asynchronous driver.
ReactiveCqlTemplate and ReactiveCassandraTemplate use Project Reactor wrapper types Mono and Flux to implement Template API and repository support. Reactive template supports common operations such as:
* Query/Execution methods for static CQL and prepared statements
* Insert/Save/Update/Delete methods
* Exists and Count projections
* Reactive Callback methods
Person person = new Person("Dave", 25);
template.insert(person) //
.flatMap(p -> template.update(new Person("Sven", 25))) //
.flatMap(p -> template.selectOneById(person.getId(), Person.class)) //
.subscribeWith(TestSubscriber.create()) //
.await() //
.assertValuesWith(result -> {
assertThat(result.getFirstName(), is(equalTo("Sven")));
});
Reactive Repository support is built on top of ReactiveCassandraTemplate using ReactiveCassandraRepository as the store-specific base repository. Reactive repositories are enabled by using @EnableReactiveCassandraRepositories on a @Configuration class to opt-in for reactive support. Reactive repositories can be composed of a reactive base interface such as
* ReactiveCrudRepository
* ReactiveSortingRepository
* RxJava1CrudRepository
* RxJava1SortingRepository
and are identified as reactive repository if one method uses a reactive wrapper type (such as Flux or Observable). If a reactive repository is discovered, it's not implemented by the blocking repository support but with the reactive repository factory. Blocking methods are not (yet) synchronized when using a reactive repository so each repository method must use a reactive wrapper result type. Reactive repository support with Spring Data allows using RxJava1 and Project Reactor types to declare repository methods. Reactive wrapper types are internally converted so the composition library choice on repository level is left up to the user.
There's feature parity between Reactive Cassandra repository support and blocking repository support.
Feature overview:
* Query Methods using String queries and Query Derivation
* Projections
@Configuration
@EnableReactiveCassandraRepositories
class ApplicationConfig extends AbstractReactiveCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "mykeyspace";
}
@Override
protected String getEntityBasePackages() {
return new String[] {"com.springdata.cassandra"};
}
}
public interface PersonRepository extends ReactiveSortingRepository<Person, String> {
Flux<Person> findByFirstname(String firstname);
Flux<Person> findByFirstname(Publisher<String> firstname);
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);
}
public interface PersonRepository extends RxJava1SortingRepository<Person, String> {
Observable<Person> findByFirstname(String firstname);
Observable<Person> findByFirstname(Single<String> firstname);
Single<Person> findByFirstnameAndLastname(String firstname, String lastname);
}
We now support Cassandra User-defined types. UDTs can be created using CQL generators and used inside of mapped domain classes. User-defined types can be used either raw as UDTValue that is passed through or as mapped object. Mapped UDTs must be annotated with @UserDefinedType. Types are included into schema generation so known and defined UDTs are created before any tables are created. UDTs can be used with set and list collection types and in primary keys. UDTs can also be used in repository query methods as query predicates.
Updating UDTs will update the whole UDT.
@UserDefinedType
public class Address {
String city;
String country;
}
@Table
public class Person {
@Id String id;
Address address;
UDTValue genericUdt;
}
The XML namespace support was extended with new schema versions to support provide a User Type resolver so UDTs can be resolved:
<cassandra:mapping>
<cassandra:user-type-resolver keyspace-name="${cassandra.keyspace}" />
</cassandra:mapping>
We now consider registered custom write converters for top-level collection types (like Map<String, Collection<String>>) to convert properties to Java types. Previously, only collection component types and non-collection top-level types were used to determine a converter.
Methods accepting a List of entities perform batching operations (insert/update/delete).
This can be fine for entities sharing a partition key but leads in most cases to distributed batches across a Cassandra cluster which is an anti-pattern. CassandraTemplate exposes CassandraBatchOperations for batching operations. As of Version 1.5, all methods accepting a List of entities are deprecated because there is no alternative of inserting multiple rows in an atomic way that guarantees not to harm Cassandra performance. These methods will be removed in Version 2.0.
We now support named and expression parameters in String-based repository query methods. Name-based parameters are referenced with :parameter. Expression parameters can reference either parameter names (if provided) with :#{expression}/#{#fieldname} or index-based with ?#{[0]}.
String-based query creation now also serializes parameters using the configured CodecRegistry so escaping and serialization is handled by the driver itself which leads to correct queries.
public interface SampleRepository extends Repository<Person, String> {
@Query("SELECT * FROM person WHERE lastname = ?0;")
Person findByLastname(String lastname);
@Query("SELECT * FROM person WHERE lastname = :lastname;")
Person findByNamedParameter(@Param("lastname") String lastname);
@Query("SELECT * FROM person WHERE lastname = ?#{[0]};")
Person findByIndexExpressionParameter(String lastname);
@Query("SELECT * FROM person WHERE lastnames IN (?0) AND age = ?1;")
Person findByLastNamesAndAge(Collection<String> lastname, int age);
@Query("SELECT * FROM person WHERE lastname = :#{#lastname == 'Matthews' ? 'Admin' : #lastname};")
Person findByConditionalExpressionParameter(@Param("lastname") String lastname);
}
Related tickets: DATACASS-122, DATACASS-240
We provide now two separate verifiers for Table types and PrimaryKey types. Exception messages are aligned between both verifiers and the checks use simplified code instead to perform checks. VerifierMappingExceptions introduces now immutability and methods introducing mutability are deprecated.
We now rely on Row.getObject(…) to retrieve data from a Cassandra Column. CodecRegistry and ProtocolVersion are configured on Cluster so there's no need to use a static configured CodecRegistry and ProtocolVersion that might not fit the configured values.
We now consider the ordering of clustered columns when creating a CreateTableSpecification for mapped classes without a primary key class. Previously, the ordering was dropped silently.
High client churn seems to affect Cassandra in a negative way (connection timeouts, driver considers hosts as down). Almost all integration tests bootstrap their own Cluster instance and dispose it once the tests has finished. Identify tests where reuse of a global Cluster instance provided by CassandraRule is possible and switch from context bootstrapping to reuse. This helps to prevent integration test failures.
We now support `NettyOptions` bean references in XML-based configuration.
<bean id="nettyOptions" class="…" />
<cass:cluster contact-points="…" netty-options-ref="nettyOptions" />
Switch heartbeatIntervalSeconds, idleTimeoutSeconds and poolTimeoutMilliseconds to primitive integers and compare values against defaults to decide whether to set these properties on PoolingOptions.
We are now compatible with Datastax’ Cassandra Driver 3.1.1 and support the newly introduced configuration options MaxQueueSize for PoolingOptions.
This change also removes test assertions for PoolTimeoutMillis as this option was deprecated and made unusable with 3.1.1.
Remove the previously added netty dependency exclusion in 237ac88 to include netty as transient dependency. Netty was excluded because of a dependency conflict between cassandra-driver-dse and cassandra-all. The conflict gets visible during build-time on CI hosts with symptoms of 100% CPU usage or network timeouts.
We now use cassandra-all 3.7 and cassandra-driver-core 3.1.0 which both require netty 4.0.37.Final.
Related pull request: #43.
Deprecate our org.springframework.cassandra.core.ConsistencyLevel enum. Having an own consistency level type leads to confusion and it's always behind the driver. We don't want to maintain that type, so we decided to deprecate the own type and use the driver consistency levels.
We allow now the use of the driver retry policies aside of our consistency level enumeration. For most cases, our enumeration is the simpler approach. Some retry policies (IdempotenceAwareRetryPolicy, LoggingRetryPolicies) require further configuration and cannot be applied with just using a static enum value. We now support ReadTimeout, FetchSize, and Tracing via QueryOptions and WriteOptions and provide builders for QueryOptions and WriteOptions.
Original pull request: #81.
Add author tags. Extend date range in headers. Simplify resolution by removing intermediate variables. Add test to verify ConsistencyLevel resolution. Guard consistency level against null.
Originall pull request: #54.
We allow users to provide a ClusterBuilderConfigurer that can be applied to the Cluster Builder. ClusterBuilderConfigurer is a callback interface to handle extended configuration when the DataStax API changes. It allows configuration of options after all provided properties were set.
Original pull request: #79.
Related pull request: #80.
We now support configuration of the cluster name, AddressTranslator, MaxSchemaAgreementWaitSeconds, SpeculativeExecutionPolicy and TimestampGenerator in the Cassandra Cluster factory. The cluster name is derived from the bean name, if not configured otherwise.
Related tickets: DATACASS-120, DATACASS-316, DATACASS-317, DATACASS-319, DATACASS-320.
Original pull request: #79.
Related pull request: #80.
Re-implemented CassandraBatchTemplate to take a vararg array of Object entities rather than a single entity and guarded against null.
Original pull request: #78.
We now support Cassandra batching via CassandraBatchOperations. Batch operations allow to insert/update/delete entities in an atomic way.
Group walter = new Group(new GroupKey("users", "0x1", "walter"));
Group mike = new Group(new GroupKey("users", "0x1", "mike"));
Group tuco = new Group(new GroupKey("users", "0x1", "tuco"));
CassandraBatchOperations batchOperations = cassandraOperations.batchOps();
batchOperations.insert(walter).update(mike).delete(tuco).execute();
Original pull request: #78.
We now support query derivation in Cassandra repositories. Repositories may declare query methods and queries are created based on the repository declaration.
interface PersonRepository extends CassandraRepository<Person> {
List<Person> findByLastname(@CassandraType(type = Name.VARCHAR) String lastname);
List<Person> findByLastname(String lastname, Sort sort);
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
Collection<PersonProjection> findPersonProjectedBy();
interface PersonProjection {
String getFirstname();
}
}
@Table
@Data
public class Person {
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0)
private String lastname;
@PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED, ordinal = 1)
private String firstname;
}
Query derivation supports a basic set of where predicates:
* = (Equals/Simple property)
* >= (Greater or equal)
* > (Greater)
* < (Less)
* <= (Less or equal)
* IN, LIKE (Like, Starting with, Ending with), CONTAINING
* = true (Is true)
* = false (Is false)
Derived queries work with primary-key and non-primary key columns. Non-primary key columns require a secondary index otherwise these fields can't be queried.
Original pull request: #74.