Migrate docs to Antora.

Closes #1427
This commit is contained in:
Mark Paluch
2023-08-29 13:36:24 +02:00
parent 66de8a8c91
commit 746541447c
94 changed files with 2458 additions and 2414 deletions

5
.gitignore vendored
View File

@@ -15,4 +15,9 @@ build
.idea
download
work
build/
node_modules
node
package.json
package-lock.json

View File

@@ -15,24 +15,40 @@
<artifactId>spring-data-cassandra-distribution</artifactId>
<packaging>pom</packaging>
<name>Spring Data for Apache Cassandra - Distribution</name>
<description>Distribution build for Spring Data for Apache Cassandra</description>
<url>https://github.com/spring-projects/spring-data-cassandra/tree/master/spring-data-cassandra-distribution</url>
<properties>
<project.root>${basedir}/..</project.root>
<dist.key>SDCASS</dist.key>
<antora.playbook>${project.basedir}/../src/main/antora/antora-playbook.yml</antora.playbook>
</properties>
<build>
<resources>
<resource>
<directory>${project.basedir}/../src/main/antora/resources/antora-resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<groupId>io.spring.maven.antora</groupId>
<artifactId>antora-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

View File

@@ -0,0 +1,42 @@
# PACKAGES antora@3.2.0-alpha.2 @antora/atlas-extension:1.0.0-alpha.1 @antora/collector-extension@1.0.0-alpha.3 @springio/antora-extensions@1.1.0-alpha.2 @asciidoctor/tabs@1.0.0-alpha.12 @opendevise/antora-release-line-extension@1.0.0-alpha.2
#
# The purpose of this Antora playbook is to build the docs in the current branch.
antora:
extensions:
- '@antora/collector-extension'
- require: '@springio/antora-extensions/root-component-extension'
root_component_name: 'data-cassandra'
site:
title: Spring Data Cassandra
url: https://docs.spring.io/spring-data-cassandra/reference/
content:
sources:
- url: ./../../..
branches: HEAD
start_path: src/main/antora
worktrees: true
- url: https://github.com/spring-projects/spring-data-commons
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: [ main, 3.2.x ]
start_path: src/main/antora
asciidoc:
attributes:
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
chomp: 'all'
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
sourcemap: true
urls:
latest_version_segment: ''
runtime:
log:
failure_level: warn
format: pretty
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.5/ui-bundle.zip
snapshot: true

View File

@@ -0,0 +1,12 @@
name: data-cassandra
version: true
title: Spring Data Cassandra
nav:
- modules/ROOT/nav.adoc
ext:
collector:
- run:
command: ./mvnw validate process-resources -pl :spring-data-cassandra-distribution -am -Pantora-process-resources
local: true
scan:
dir: spring-data-cassandra-distribution/target/classes/

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https:://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.example;
import static org.springframework.data.cassandra.core.query.Criteria.*;
import static org.springframework.data.cassandra.core.query.Query.*;
import org.springframework.data.cassandra.core.CassandraTemplate;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
/**
* @author Mark Paluch
*/
// @formatter:off
public class ReactiveCassandraTemplateExamples {
private ReactiveCassandraTemplate template = null;
void examples() {
// tag::preparedStatement[]
template.setUsePreparedStatements(true);
Mono<Actor> actorByQuery = template.selectOne(query(where("id").is(42)), Actor.class);
Mono<Actor> actorByStatement = template.selectOne(
SimpleStatement.newInstance("SELECT id, name FROM actor WHERE id = ?", 42),
Actor.class);
// end::preparedStatement[]
}
static class Actor {
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2020-2023 the original author or authors.
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https:://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -73,6 +73,13 @@ public class ReactiveCqlTemplateExamples {
}
});
// end::listOfRowMapper[]
// tag::preparedStatement[]
Flux<String> lastNames = reactiveCqlTemplate.query(
session -> session.prepare("SELECT last_name FROM t_actor WHERE id = ?"),
ps -> ps.bind(1212L),
(row, rowNum) -> row.getString(0));
// end::preparedStatement[]
}
// tag::findAllActors[]

View File

@@ -0,0 +1,45 @@
* xref:index.adoc[Overview]
** xref:commons/upgrade.adoc[]
* xref:cassandra.adoc[]
** xref:cassandra/getting-started.adoc[]
** xref:cassandra/configuration.adoc[]
** xref:cassandra/schema-management.adoc[]
** xref:cassandra/cql-template.adoc[]
** xref:cassandra/reactive-cassandra.adoc[]
** xref:cassandra/template.adoc[]
** xref:cassandra/prepared-statements.adoc[]
** xref:object-mapping.adoc[]
** xref:cassandra/converters.adoc[]
** xref:cassandra/events.adoc[]
** xref:cassandra/auditing.adoc[]
* xref:repositories.adoc[]
** xref:repositories/core-concepts.adoc[]
** xref:repositories/definition.adoc[]
** xref:cassandra/repositories/repositories.adoc[]
** xref:repositories/create-instances.adoc[]
** xref:repositories/query-methods-details.adoc[]
** xref:cassandra/repositories/query-methods.adoc[]
** xref:repositories/projections.adoc[]
** xref:repositories/custom-implementations.adoc[]
** xref:repositories/core-domain-events.adoc[]
** xref:repositories/null-handling.adoc[]
** xref:cassandra/repositories/cdi-integration.adoc[]
** xref:repositories/query-keywords-reference.adoc[]
** xref:repositories/query-return-types-reference.adoc[]
* xref:observability.adoc[]
* xref:kotlin.adoc[]
** xref:kotlin/requirements.adoc[]
** xref:kotlin/null-safety.adoc[]
** xref:kotlin/object-mapping.adoc[]
** xref:kotlin/extensions.adoc[]
** xref:kotlin/coroutines.adoc[]
* xref:migration-guides.adoc[]
** xref:migration-guide/migration-guide-1.5-to-2.0.adoc[]
** xref:migration-guide/migration-guide-2.2-to-3.0.adoc[]
** xref:migration-guide/migration-guide-3.0-to-4.0.adoc[]
* https://github.com/spring-projects/spring-data-commons/wiki[Wiki]

View File

@@ -0,0 +1,17 @@
[[cassandra.core]]
= Cassandra Support
:page-section-summary-toc: 1
Spring Data support for Apache Cassandra contains a wide range of features:
* Spring configuration support with Java-based `@Configuration` classes or the XML namespace.
* The `CqlTemplate`, `AsyncCqlTemplate`, and `ReactiveCqlTemplate` helper classes that increases productivity by properly handling common Cassandra data access operations.
* The `CassandraTemplate`, `AsyncCassandraTemplate`, and `ReactiveCassandraTemplate` helper classes that provide object mapping between CQL Tables and POJOs.
* Exception translation into Spring's portable {springDocsUrl}data-access.html#dao-exceptions[Data Access Exception Hierarchy].
* Feature rich object mapping integrated with _Spring's_ {springDocsUrl}core.html#core-convert[Conversion Service].
* Annotation-based mapping metadata that is extensible to support other metadata formats.
* Java-based query, criteria, and update DSLs.
* Automatic implementation of imperative and reactive `Repository` interfaces including support for custom finder methods.
For most data-oriented tasks, you can use the `[Reactive|Async]CassandraTemplate` or the `Repository` support, both of which use the rich object-mapping functionality. `[Reactive|Async]CqlTemplate` is commonly used to increment counters or perform ad-hoc CRUD operations. `[Reactive|Async]CqlTemplate` also provides callback methods that make it easy to get low-level API objects, such as `com.datastax.oss.driver.api.core.CqlSession`, which lets you communicate directly with Cassandra.
Spring Data for Apache Cassandra uses consistent naming conventions on objects in various APIs to those found in the DataStax Java Driver so that they are familiar and so that you can map your existing knowledge onto the Spring APIs.

View File

@@ -1,12 +1,14 @@
[[cassandra.auditing]]
== General Auditing Configuration for Cassandra
= Auditing Configuration for Cassandra
To activate auditing functionality, create a configuration as the following example shows:
.Activating auditing by using XML configuration
====
.Java
[source,java,role="primary"]
.Activating auditing through configuration
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableCassandraAuditing
@@ -19,8 +21,9 @@ class Config {
}
----
.XML
[source,xml,role="secondary"]
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@@ -35,7 +38,7 @@ class Config {
<cassandra:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/>
</beans>
----
====
======
If you expose a bean of type `AuditorAware` to the `ApplicationContext`, the auditing infrastructure picks it up automatically and uses it to determine the current user to be set on domain types.
If you have multiple implementations registered in the `ApplicationContext`, you can select the one to be used by explicitly setting the `auditorAwareRef` attribute of `@EnableCassandraAuditing`.

View File

@@ -0,0 +1,168 @@
[[cassandra.connectors]]
= Connecting to Cassandra with Spring
One of the first tasks when using Apache Cassandra with Spring is to create a `com.datastax.oss.driver.api.core.CqlSession` object by using the Spring IoC container.
You can do so either by using Java-based bean metadata or by using XML-based bean metadata.
These are discussed in the following sections.
NOTE: For those not familiar with how to configure the Spring container using Java-based bean metadata instead of XML-based metadata, see the high-level introduction in the reference docs
https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.0.html#new-java-configuration[here]
as well as the detailed documentation {springDocsUrl}core.html#beans-java-instantiating-container[here].
[[cassandra.cassandra-java-config]]
== Registering a Session Instance by Using Java-based Metadata
The following example shows how to use Java-based bean metadata to register an instance of a `com.datastax.oss.driver.api.core.CqlSession`:
.Registering a `com.datastax.oss.driver.api.core.CqlSession` object by using Java-based bean metadata
====
[source,java]
----
include::example$AppConfig.java[tags=class]
----
====
This approach lets you use the standard `com.datastax.oss.driver.api.core.CqlSession` API that you may already know.
An alternative is to register an instance of `com.datastax.oss.driver.api.core.CqlSession` with the container by using Spring's `CqlSessionFactoryBean`.
As compared to instantiating a `com.datastax.oss.driver.api.core.CqlSession` instance directly, the `FactoryBean` approach has the added advantage of also providing the container with an `ExceptionTranslator` implementation that translates Cassandra exceptions to exceptions in Spring's portable `DataAccessException` hierarchy.
This hierarchy and the use of
`@Repository` is described in {springDocsUrl}data-access.html[Spring's DAO support features].
The following example shows Java-based factory class usage:
.Registering a com.datastax.oss.driver.api.core.CqlSession object by using Spring's `CqlSessionFactoryBean`:
====
[source,java]
----
include::example$FactoryBeanAppConfig.java[tags=class]
----
====
Using `CassandraTemplate` with object mapping and repository support requires a `CassandraTemplate`,
`CassandraMappingContext`, `CassandraConverter`, and enabling repository support.
The following example shows how to register components to configure object mapping and repository support:
.Registering components to configure object mapping and repository support
====
[source,java]
----
include::example$CassandraConfig.java[tags=class]
----
====
Creating configuration classes that register Spring Data for Apache Cassandra components can be an exhausting challenge, so Spring Data for Apache Cassandra comes with a pre-built configuration support class.
Classes that extend from
`AbstractCassandraConfiguration` register beans for Spring Data for Apache Cassandra use.
`AbstractCassandraConfiguration` lets you provide various configuration options, such as initial entities, default query options, pooling options, socket options, and many more. `AbstractCassandraConfiguration` also supports you with schema generation based on initial entities, if any are provided.
Extending from
`AbstractCassandraConfiguration` requires you to at least provide the keyspace name by implementing the `getKeyspaceName` method.
The following example shows how to register beans by using `AbstractCassandraConfiguration`:
.Registering Spring Data for Apache Cassandra beans by using `AbstractCassandraConfiguration`
====
[source,java]
----
include::example$CassandraConfiguration.java[tags=class]
----
====
[[cassandra-connectors.xmlconfig]]
=== XML Configuration
This section describes how to configure Spring Data Cassandra with XML.
[[cassandra-connectors.xmlconfig.ext_properties]]
=== Externalizing Connection Properties
To externalize connection properties, you should first create a properties file that contains the information needed to connect to Cassandra. `contactpoints` and `keyspace` are the required fields.
The following example shows our properties file, called `cassandra.properties`:
====
[source]
----
cassandra.contactpoints=10.1.55.80:9042,10.1.55.81:9042
cassandra.keyspace=showcase
----
====
In the next two examples, we use Spring to load these properties into the Spring context.
[[registering-a-session-instance-by-using-xml-based-metadata]]
=== Registering a Session Instance by using XML-based Metadata
While you can use Spring's traditional `<beans/>` XML namespace to register an instance of
`com.datastax.oss.driver.api.core.CqlSession` with the container, the XML can be quite verbose, because it is general purpose.
XML namespaces are a better alternative to configuring commonly used objects, such as the `CqlSession` instance.
The `cassandra` namespace let you create a `CqlSession` instance.
The following example shows how to configure the `cassandra` namespace:
.XML schema to configure Cassandra by using the `cassandra` namespace
====
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra
https://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Default bean name is 'cassandraSession' -->
<cassandra:session contact-points="localhost" port="9042">
<cassandra:keyspace action="CREATE_DROP" name="mykeyspace" />
</cassandra:session>
<cassandra:session-factory>
<cassandra:script
location="classpath:/org/springframework/data/cassandra/config/schema.cql"/>
</cassandra:session-factory>
</beans>
----
====
The XML configuration elements for more advanced Cassandra configuration are shown below.
These elements all use default bean names to keep the configuration code clean and readable.
While the preceding example shows how easy it is to configure Spring to connect to Cassandra, there are many other options.
Basically, any option available with the DataStax Java Driver is also available in the Spring Data for Apache Cassandra configuration.
This includes but is not limited to authentication, load-balancing policies, retry policies, and pooling options.
All of the Spring Data for Apache Cassandra method names and XML elements are named exactly (or as close as possible) like the configuration options on the driver so that mapping any existing driver configuration should be straight forward.
The following example shows how to configure Spring Data components by using XML
.Configuring Spring Data components by using XML
====
[source,xml]
----
<!-- Loads the properties into the Spring Context and uses them to fill
in placeholders in the bean definitions -->
<context:property-placeholder location="classpath:cassandra.properties" />
<!-- REQUIRED: The Cassandra Session -->
<cassandra:session contact-points="${cassandra.contactpoints}" keyspace-name="${cassandra.keyspace}" />
<!-- REQUIRED: The default Cassandra mapping context used by `CassandraConverter` -->
<cassandra:mapping>
<cassandra:user-type-resolver keyspace-name="${cassandra.keyspace}" />
</cassandra:mapping>
<!-- REQUIRED: The default Cassandra converter used by `CassandraTemplate` -->
<cassandra:converter />
<!-- REQUIRED: The Cassandra template is the foundation of all Spring
Data Cassandra -->
<cassandra:template id="cassandraTemplate" />
<!-- OPTIONAL: If you use Spring Data for Apache Cassandra repositories, add
your base packages to scan here -->
<cassandra:repositories base-package="org.spring.cassandra.example.repo" />
----
====

View File

@@ -1,3 +1,5 @@
include::{commons}@data-commons::page$custom-conversions.adoc[]
[[cassandra.custom-converters]]
== Overriding Default Mapping with Custom Converters
@@ -17,7 +19,7 @@ with Jackson 2:
[source,java]
----
include::../{example-root}/PersonWriteConverter.java[tags=class]
include::example$PersonWriteConverter.java[tags=class]
----
[[customconversions.reader]]
@@ -29,7 +31,7 @@ The following example uses a `Converter` that converts a `java.lang.String` into
[source,java]
----
include::../{example-root}/PersonReadConverter.java[tags=class]
include::example$PersonReadConverter.java[tags=class]
----
[[customconversions.java]]
@@ -41,7 +43,5 @@ The following configuration snippet shows how to manually register converters as
[source,java]
----
include::../{example-root}/ConverterConfiguration.java[tags=class]
include::example$ConverterConfiguration.java[tags=class]
----
include::../{spring-data-commons-docs}/custom-conversions.adoc[leveloffset=+3]

View File

@@ -0,0 +1,287 @@
[[cassandra.cql-template]]
= CQL Template API
The `CqlTemplate` class (and its reactive variant `ReactiveCqlTemplate`) is the central class in the core CQL package.
It handles the creation and release of resources.
It performs the basic tasks of the core CQL workflow, such as statement creation and execution, and leaves application code to provide CQL and extract results.
The `CqlTemplate` class executes CQL queries and update statements, performs iteration over `ResultSet` instances and extraction of returned parameter values.
It also catches CQL exceptions and translates them to the generic, more informative, exception hierarchy defined in the `org.springframework.dao` package.
When you use the `CqlTemplate` for your code, you need only implement callback interfaces, which have a clearly defined contract.
Given a `Connection`, the `PreparedStatementCreator` callback interface creates a xref:cassandra/prepared-statements.adoc#cassandra.template.prepared-statements.cql[prepared statement] with the provided CQL and any necessary parameter arguments.
The `RowCallbackHandler` interface extracts values from each row of a `ResultSet`.
The `CqlTemplate` can be used within a DAO implementation through direct instantiation with a `SessionFactory` reference or be configured in the Spring container and given to DAOs as a bean reference. `CqlTemplate` is a foundational building block for xref:cassandra/template.adoc[`CassandraTemplate`].
All CQL issued by this class is logged at the `DEBUG` level under the category corresponding to the fully-qualified class name of the template instance (typically `CqlTemplate`, but it may be different if you use a custom subclass of the `CqlTemplate` class).
You can control fetch size, consistency level, and retry policy defaults by configuring these parameters on the CQL API instances: `CqlTemplate`, `AsyncCqlTemplate`, and `ReactiveCqlTemplate`.
Defaults apply if the particular query option is not set.
NOTE: `CqlTemplate` comes in different execution model flavors.
The basic `CqlTemplate` uses a blocking execution model.
You can use `AsyncCqlTemplate` for asynchronous execution and synchronization with `ListenableFuture` instances or
<<cassandra.reactive.cql-template,`ReactiveCqlTemplate`>> for reactive execution.
[[cassandracql-template.examples]]
== Examples of `CqlTemplate` Class Usage
This section provides some examples of the `CqlTemplate` class in action.
These examples are not an exhaustive list of all functionality exposed by the `CqlTemplate`.
See the https://docs.spring.io/spring-data/cassandra/docs/{version}/api/[Javadoc] for that.
[[cassandra.cql-template.examples.query]]
=== Querying (SELECT) with `CqlTemplate`
The following query gets the number of rows in a table:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=rowCount]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=rowCount]
----
======
The following query uses a bind variable:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=countOfActorsNamedJoe]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=countOfActorsNamedJoe]
----
======
The following example queries for a `String`:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=lastName]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=lastName]
----
======
The following example queries and populates a single domain object:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=rowMapper]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=rowMapper]
----
======
The following example queries and populates multiple domain objects:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=listOfRowMapper]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=listOfRowMapper]
----
======
If the last two snippets of code actually existed in the same application, it would make sense to remove the duplication present in the two `RowMapper` anonymous inner classes and extract them out into a single class (typically a `static` nested class) that can then be referenced by DAO methods.
For example, it might be better to write the last code snippet as follows:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=findAllActors]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=findAllActors]
----
======
[[cassandra.cql-template.examples.update]]
=== `INSERT`, `UPDATE`, and `DELETE` with `CqlTemplate`
You can use the `execute(…)` method to perform `INSERT`, `UPDATE`, and `DELETE` operations.
Parameter values are usually provided as variable arguments or, alternatively, as an object array.
The following example shows how to perform an `INSERT` operation with `CqlTemplate`:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=insert]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=insert]
----
======
The following example shows how to perform an `UPDATE` operation with `CqlTemplate`:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=update]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=update]
----
======
The following example shows how to perform an `DELETE` operation with `CqlTemplate`:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=delete]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=delete]
----
======
[[cassandra.cql-template.examples.other]]
=== Other `CqlTemplate` operations
You can use the `execute(..)` method to execute any arbitrary CQL.
As a result, the method is often used for DDL statements.
It is heavily overloaded with variants that take callback interfaces, bind variable arrays, and so on.
The following example shows how to create and drop a table by using different API objects that are all passed to the `execute()` methods:
====
[source,java]
----
include::example$CqlTemplateExamples.java[tags=other]
----
====
[[cassandra.connections]]
== Controlling Cassandra Connections
Applications connect to Apache Cassandra by using `CqlSession` objects.
A Cassandra `CqlSession` keeps track of multiple connections to the individual nodes and is designed to be a thread-safe, long-lived object.
Usually, you can use a single `CqlSession` for the whole application.
Spring acquires a Cassandra `CqlSession` through a `SessionFactory`. `SessionFactory` is part of Spring Data for Apache Cassandra and is a generalized connection factory.
It lets the container or framework hide connection handling and routing issues from the application code.
The following example shows how to configure a default `SessionFactory`:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
CqlSession session = … // get a Cassandra Session
CqlTemplate template = new CqlTemplate();
template.setSessionFactory(new DefaultSessionFactory(session));
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
CqlSession session = … // get a Cassandra Session
ReactiveCqlTemplate template = new ReactiveCqlTemplate(new DefaultBridgedReactiveSession(session));
----
======
`CqlTemplate` and other Template API implementations obtain a `CqlSession` for each operation.
Due to their long-lived nature, sessions are not closed after invoking the desired operation.
Responsibility for proper resource disposal lies with the container or framework that uses the session.
You can find various `SessionFactory` implementations within the `org.springframework.data.cassandra.core.cql.session`
package.
[[exception-translation]]
== Exception Translation
The Spring Framework provides exception translation for a wide variety of database and mapping technologies.
This has traditionally been for JDBC and JPA.
Spring Data for Apache Cassandra extends this feature to Apache Cassandra by providing an implementation of the `org.springframework.dao.support.PersistenceExceptionTranslator` interface.
The motivation behind mapping to Spring's {springDocsUrl}html/dao.html#dao-exceptions[consistent data access exception hierarchy]
is to let you write portable and descriptive exception handling code without resorting to coding against and handling specific Cassandra exceptions.
All of Spring's data access exceptions are inherited from the
`DataAccessException` class, so you can be sure that you can catch all database-related exceptions within a single try-catch block.
`ReactiveCqlTemplate` and `ReactiveCassandraTemplate` propagate exceptions as early as possible.
Exceptions that occur during the processing of the reactive sequence are emitted as error signals.

View File

@@ -0,0 +1,68 @@
[[cassandra.mapping-usage.events]]
= Lifecycle Events
The Cassandra mapping framework has several built-in `org.springframework.context.ApplicationEvent` events that your application can respond to by registering special beans in the `ApplicationContext`.
Being based on Spring's application context event infrastructure lets other products, such as Spring Integration, easily receive these events as they are a well known eventing mechanism in Spring-based applications.
To intercept an object before it goes into the database, you can register a subclass of `org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener` that overrides the `onBeforeSave(…)` method.
When the event is dispatched, your listener is called and passed the domain object (which is a Java entity).
Entity lifecycle events can be costly and you may notice a change in the performance profile when loading large result sets.
You can disable lifecycle events on the link:https://docs.spring.io/spring-data/cassandra/docs/{version}/api/org/springframework/data/cassandra/core/CassandraTemplate.html#setEntityLifecycleEventsEnabled(boolean)[Template API].
The following example uses the `onBeforeSave` method:
====
[source,java]
----
include::example$mapping/BeforeSaveListener.java[tags=class]
----
====
Declaring these beans in your Spring `ApplicationContext` will cause them to be invoked whenever the event is dispatched.
The `AbstractCassandraEventListener` has the following callback methods:
* `onBeforeSave`: Called in `CassandraTemplate.insert(…)` and `.update(…)` operations before inserting or updating a row in the database.
* `onAfterSave`: Called in `CassandraTemplate…insert(…)` and `.update(…)` operations after inserting or updating a row in the database.
* `onBeforeDelete`: Called in `CassandraTemplate.delete(…)` operations before deleting row from the database.
* `onAfterDelete`: Called in `CassandraTemplate.delete(…)` operations after deleting row from the database.
* `onAfterLoad`: Called in the `CassandraTemplate.select(…)`, `.slice(…)`, and `.stream(…)` methods after each row is retrieved from the database.
* `onAfterConvert`: Called in the `CassandraTemplate.select(…)`, `.slice(…)`, and `.stream(…)` methods after converting a row retrieved from the database to a POJO.
NOTE: Lifecycle events are emitted only for root-level types.
Complex types used as properties within an aggregate root are not subject to event publication.
include::{commons}@data-commons::page$entity-callbacks.adoc[leveloffset=+1]
[[cassandra.entity-callbacks]]
=== Store specific EntityCallbacks
Spring Data for Apache Cassandra uses the `EntityCallback` API for its auditing support and reacts on the following callbacks.
.Supported Entity Callbacks
[%header,cols="4"]
|===
| Callback
| Method
| Description
| Order
| `ReactiveBeforeConvertCallback`
`BeforeConvertCallback`
| `onBeforeConvert(T entity, CqlIdentifier tableName)`
| Invoked before a domain object is converted to `com.datastax.driver.core.Statement`.
| `Ordered.LOWEST_PRECEDENCE`
| `ReactiveAuditingEntityCallback`
`AuditingEntityCallback`
| `onBeforeConvert(Object entity, CqlIdentifier tableName)`
| Marks an auditable entity _created_ or _modified_
| 100
| `ReactiveBeforeSaveCallback`
`BeforeSaveCallback`
| `onBeforeSave(T entity, CqlIdentifier tableName, Statement statement)`
| Invoked before a domain object is saved. +
Can modify the target, to be persisted, `com.datastax.driver.core.Statement` containing all mapped entity information.
| `Ordered.LOWEST_PRECEDENCE`
|===

View File

@@ -0,0 +1,103 @@
[[cassandra.getting-started]]
= Getting Started
Spring Data for Apache Cassandra requires Apache Cassandra 2.1 or later and Datastax Java Driver 4.0 or later.
An easy way to quickly set up and bootstrap a working environment is to create a Spring-based project in https://spring.io/tools[Spring Tools] or use https://start.spring.io/[Spring Initializer].
[[cassandra.examples-repo]]
== Examples Repository
To get a feel for how the library works, you can download and play around with
https://github.com/spring-projects/spring-data-examples[several examples].
[[cassandra.hello-world]]
== Hello World
First, you need to set up a running Apache Cassandra server.
See the
https://cassandra.apache.org/doc/latest/getting_started/index.html[Apache Cassandra Quick Start Guide]
for an explanation on how to start Apache Cassandra.
Once installed, starting Cassandra is typically a matter of executing the following command: `CASSANDRA_HOME/bin/cassandra -f`.
To create a Spring project in STS, go to File -> New -> Spring Template Project -> Simple Spring Utility Project and press Yes when prompted.
Then enter a project and a package name, such as `org.spring.data.cassandra.example`.
Then you can add the following dependency declaration to your pom.xml file's `dependencies` section.
====
[source,xml,subs="verbatim,attributes"]
----
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
<version>{version}</version>
</dependency>
</dependencies>
----
====
Also, you should change the version of Spring in the pom.xml file to be as follows:
====
[source,xml,subs="verbatim,attributes"]
----
<spring.version>{springVersion}</spring.version>
----
====
If using a milestone release instead of a GA release, you also need to add the location of the Spring Milestone repository for Maven to your pom.xml file so that it is at the same level of your `<dependencies/>` element, as follows:
[source,xml]
----
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
----
The repository is also https://repo.spring.io/milestone/org/springframework/data/[browseable here].
You can also browse all Spring repositories https://repo.spring.io/webapp/#/home[here].
Now you can create a simple Java application that stores and reads a domain object to and from Cassandra.
To do so, first create a simple domain object class to persist, as the following example shows:
====
[source,java]
----
include::example$Person.java[tags=file]
----
====
Next, create the main application to run, as the following example shows:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CassandraApplication.java[tags=file]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCassandraApplication.java[tags=file]
----
======
Even in this simple example, there are a few notable things to point out:
* You can create an instance of `CassandraTemplate` (or `ReactiveCassandraTemplate` for reactive usage) with a Cassandra `CqlSession`.
* You must annotate your POJO as a Cassandra `@Table` entity and also annotate the `@PrimaryKey`.
Optionally, you can override these mapping names to match your Cassandra database table and column names.
* You can either use raw CQL or the DataStax `QueryBuilder` API to construct your queries.

View File

@@ -3,11 +3,9 @@
This part of the reference documentation explains the core functionality offered by Spring Data for Apache Cassandra.
<<cassandra.core>> introduces the Cassandra module feature set.
<<cassandra.reactive>> explains reactive Cassandra specifics.
<<cassandra.repositories>> introduces repository support for Cassandra.
* xref:cassandra.adoc[Cassandra Support] introduces the Cassandra module feature set.
* xref:cassandra/reactive-cassandra.adoc[Reactive Cassandra Support] explains reactive Cassandra specifics.
* xref:repositories.adoc[Cassandra Repositories] introduces repository support for Cassandra.
[[cassandra.modules]]
== Spring CQL and Spring Data for Apache Cassandra Modules
@@ -70,13 +68,13 @@ Spring's support for Apache Cassandra comes in different flavors.
Once you start using one of these approaches, you can still mix and match to include a feature from a different approach.
The following approaches work well:
* <<cassandra.cql-template,`CqlTemplate`>> and <<cassandra.reactive.cql-template,`ReactiveCqlTemplate`>> are the classic Spring CQL approach and the most popular.
* xref:cassandra/cql-template.adoc[`CqlTemplate`] and xref:cassandra/reactive-cassandra.adoc#cassandra.reactive.cql-template[`ReactiveCqlTemplate`] are the classic Spring CQL approach and the most popular.
This is the "`lowest-level`" approach.
Note that components like `CassandraTemplate`
use `CqlTemplate` under-the-hood.
* <<cassandra.template,`CassandraTemplate`>> wraps a `CqlTemplate` to provide query result-to-object mapping and the use of `SELECT`, `INSERT`, `UPDATE`, and `DELETE` methods instead of writing CQL statements.
* xref:cassandra/template.adoc[`CassandraTemplate`] wraps a `CqlTemplate` to provide query result-to-object mapping and the use of `SELECT`, `INSERT`, `UPDATE`, and `DELETE` methods instead of writing CQL statements.
This approach provides better documentation and ease of use.
* <<cassandra.reactive.template,`ReactiveCassandraTemplate`>> wraps a `ReactiveCqlTemplate` to provide query result-to-object mapping and the use of `SELECT`, `INSERT`, `UPDATE`, and `DELETE` methods instead of writing CQL statements.
* xref:cassandra/reactive-cassandra.adoc#cassandra.reactive.template[`ReactiveCassandraTemplate`] wraps a `ReactiveCqlTemplate` to provide query result-to-object mapping and the use of `SELECT`, `INSERT`, `UPDATE`, and `DELETE` methods instead of writing CQL statements.
This approach provides better documentation and ease of use.
* Repository Abstraction lets you create repository declarations in your data access layer.
The goal of Spring Data's repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.

View File

@@ -0,0 +1,104 @@
[[cassandra.template.prepared-statements]]
= Prepared Statements
CQL statements that are executed multiple times can be prepared and stored in a `PreparedStatement` object to improve query performance.
Both, the driver and Cassandra maintain a mapping of `PreparedStatement` queries to their metadata.
You can use prepared statements through the following abstractions:
* xref:cassandra/cql-template.adoc[`CqlTemplate`, `AsyncCqlTemplate`, or `ReactiveCqlTemplate`] through the choice of API
* xref:cassandra/template.adoc[`CassandraTemplate`, `AsyncCassandraTemplate`, or `ReactiveCassandraTemplate`] by enabling prepared statements
* xref:repositories.adoc[Cassandra repositories] as they are built on top of the Template API
[[cassandra.template.prepared-statements.cql]]
== Using `CqlTemplate`
The `CqlTemplate` class (and its asynchronous and reactive variants) offers various methods accepting static CQL, `Statement` objects and `PreparedStatementCreator`.
Methods accepting static CQL without additional arguments typically run the CQL statement as-is without further processing.
Methods accepting static CQL in combination with an arguments array (such as `execute(String cql, Object... args)` and `queryForRows(String cql, Object... args)`) use prepared statements.
Internally, these methods create a `PreparedStatementCreator` and `PreparedStatementBinder` objects to prepare the statement and later on to bind values to the statement to run it.
Spring Data Cassandra generally uses index-based parameter bindings for prepared statements.
Since Cassandra Driver version 4, prepared statements are cached on the driver level which removes the need to keep track of prepared statements in the application.
The following example shows how to issue a query with a parametrized prepared statement:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=lastName]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=lastName]
----
======
In cases where you require more control over statement preparation and parameter binding (for example, using named binding parameters), you can fully control prepared statement creation and parameter binding by calling query methods with `PreparedStatementCreator` and `PreparedStatementBinder` arguments:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CqlTemplateExamples.java[tags=preparedStatement]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCqlTemplateExamples.java[tags=preparedStatement]
----
======
Spring Data Cassandra ships with classes supporting that pattern in the `cql` package:
* `SimplePreparedStatementCreator` - utility class to create a prepared statement.
* `ArgumentPreparedStatementBinder` - utility class to bind arguments to a prepared statement.
[[cassandra.template.prepared-statements.cassandra-template]]
=== Using `CassandraTemplate`
The `CassandraTemplate` class is built on top of `CqlTemplate` to provide a higher level of abstraction.
The use of prepared statements can be controlled directly on `CassandraTemplate` (and its asynchronous and reactive variants) by calling `setUsePreparedStatements(false)` respective `setUsePreparedStatements(true)`.
Note that the use of prepared statements by `CassandraTemplate` is enabled by default.
The following example shows the use of methods that generate and that accept CQL:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
include::example$CassandraTemplateExamples.java[tags=preparedStatement]
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
include::example$ReactiveCassandraTemplateExamples.java[tags=preparedStatement]
----
======
Calling entity-bound methods such as `select(Query, Class<T>)` or `update(Query, Update, Class<T>)` build CQL statements themselves to perform the intended operations.
Some `CassandraTemplate` methods (such as `select(Statement<?>, Class<T>)`) also accepts CQL `Statement` objects as part of their API.
It's possible to participate in prepared statements when calling methods accepting a `Statement` with a `SimpleStatement` object.
The template API extracts the query string and parameters (positional and named parameters) and uses these to prepare, bind, and run the statement.
Non-``SimpleStatement`` objects cannot be used with prepared statements.
[[cassandra.template.prepared-statements.caching]]
== Caching Prepared Statements
Since Cassandra driver 4.0, prepared statements are cached by the `CqlSession` cache so it is okay to prepare the same string twice.
Previous versions required caching of prepared statements outside of the driver.
See also the https://docs.datastax.com/en/developer/java-driver/latest/manual/core/statements/prepared/[Driver documentation on Prepared Statements] for further reference.

View File

@@ -0,0 +1,36 @@
[[cassandra.reactive]]
= Reactive Infrastructure
The reactive Cassandra support contains a wide range of features:
* Spring configuration support using Java-based `@Configuration` classes.
* `ReactiveCqlTemplate` helper class that increases productivity by properly handling common Cassandra data access operations.
* `ReactiveCassandraTemplate` helper class that increases productivity by using `ReactiveCassandraOperations` in a reactive manner.
It includes integrated object mapping between tables and POJOs.
* Exception translation into Spring's portable {springDocsUrl}data-access.html#dao-exceptions[Data Access Exception Hierarchy].
* Feature rich object mapping integrated with Spring's {springDocsUrl}core.html#core-convert[Conversion Service].
* Java-based Query, Criteria, and Update DSLs.
* Automatic implementation of `Repository` interfaces, including support for custom finder methods.
For most data-oriented tasks, you can use the `ReactiveCassandraTemplate` or the repository support, which use the rich object mapping functionality. `ReactiveCqlTemplate` is commonly used to increment counters or perform ad-hoc CRUD operations. `ReactiveCqlTemplate` also provides callback methods that make it easy to get low-level API objects, such as `com.datastax.oss.driver.api.core.CqlSession`, which let you communicate directly with Cassandra.
Spring Data for Apache Cassandra uses consistent naming conventions on objects in various APIs to those found in the DataStax Java Driver so that they are immediately familiar and so that you can map your existing knowledge onto the Spring APIs.
Reactive usage is broken up into two phases: Composition and Execution.
Calling repository methods lets you compose a reactive sequence by obtaining `Publisher` instances and applying operators.
No I/O happens until you subscribe.
Passing the reactive sequence to a reactive execution infrastructure, such as {springDocsUrl}web.html#web-reactive[Spring WebFlux]
or https://vertx.io/docs/vertx-reactive-streams/java/[Vert.x]), subscribes to the publisher and initiate the actual execution.
See https://projectreactor.io/docs/core/release/reference/#reactive.subscribe[the Project reactor documentation] for more detail.
[[cassandra.reactive.repositories.libraries]]
== Reactive Composition Libraries
The reactive space offers various reactive composition libraries.
The most common libraries are
https://github.com/ReactiveX/RxJava[RxJava] and https://projectreactor.io/[Project Reactor].
Spring Data for Apache Cassandra is built on top of the https://github.com/datastax/java-driver[DataStax Cassandra Driver].
The driver is not reactive but the asynchronous capabilities allow us to adopt and expose the `Publisher` APIs to provide maximum interoperability by relying on the https://www.reactive-streams.org/[Reactive Streams] initiative.
Static APIs, such as `ReactiveCassandraOperations`, are provided by using Project Reactor's `Flux` and `Mono` types.
Project Reactor offers various adapters to convert reactive wrapper types (`Flux` to `Observable` and back), but conversion can easily clutter your code.

View File

@@ -0,0 +1,25 @@
[[cassandra.repositories.misc.cdi-integration]]
= CDI Integration
Instances of the repository interfaces are usually created by a container, and the Spring container is the most natural choice when working with Spring Data.
Spring Data for Apache Cassandra ships with a custom CDI extension that allows using the repository abstraction in CDI environments.
The extension is part of the JAR.To activate it, drop the Spring Data for Apache Cassandra JAR into your classpath.
You can now set up the infrastructure by implementing a CDI Producer for the
`CassandraTemplate`, as the following examlpe shows:
====
[source,java]
----
include::example$CassandraTemplateProducer.java[tags=class]
----
====
The Spring Data for Apache Cassandra CDI extension picks up `CassandraOperations` as a CDI bean and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container.
Thus, obtaining an instance of a Spring Data repository is a matter of declaring an injected property, as the following example shows:
====
[source,java]
----
include::example$RepositoryClient.java[tags=class]
----
====

View File

@@ -0,0 +1,240 @@
[[cassandra.repositories.queries]]
= Cassandra-specific Query Methods
NOTE: This chapter explains Cassandra-specific query methods.
This documentation uses imperative types.
By using reactive return types, the same semantics apply to reactive repositories as well.
Most of the data access operations you usually trigger on a repository result in a query being executed against the Apache Cassandra database.
Defining such a query is a matter of declaring a method on the repository interface.
The following example shows a number of such method declarations:
.PersonRepository with query methods
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByLastname(String lastname); <1>
Slice<Person> findByFirstname(String firstname, Pageable pageRequest); <2>
Window<Person> findByFirstname(String firstname, CassandraScrollPosition pos, Limit limit); <3>
List<Person> findByFirstname(String firstname, QueryOptions opts); <4>
List<Person> findByFirstname(String firstname, Sort sort); <5>
List<Person> findByFirstname(String firstname, Limit limit); <6>
Person findByShippingAddress(Address address); <7>
Person findFirstByShippingAddress(Address address); <8>
Stream<Person> findAllBy(); <9>
@AllowFiltering
List<Person> findAllByAge(int age); <10>
}
----
<1> The method shows a query for all people with the given `lastname`.
The query is derived from parsing the method name for constraints, which can be concatenated with `And`.
Thus, the method name results in a query expression of `SELECT * FROM person WHERE lastname = 'lastname'`.
<2> Applies pagination to a query.
You can equip your method signature with a `Pageable` parameter and let the method return a `Slice` instance, and we automatically page the query accordingly.
<3> Applies scrolling to a query.
Scrolling wraps Cassandra's `PagingState` into `CassandraScrollPosition` and allows dynamic limiting.
You can also use `findTop…` for a static limit.
<4> Passing a `QueryOptions` object applies the query options to the resulting query before its execution.
<5> Applies dynamic sorting to a query.
You can add a `Sort` parameter to your method signature, and Spring Data automatically applies ordering to the query.
<6> Applies dynamic result limiting to a query.
Query results can be limited using `SELECT … LIMIT`.
<7> Shows that you can query based on properties that are not a primitive type by using `Converter` instances registered in `CustomConversions`.
Throws `IncorrectResultSizeDataAccessException` if more than one match is found.
<8> Uses the `First` keyword to restrict the query to only the first result.
Unlike the preceding method, this method does not throw an exception if more than one match is found.
<9> Uses a Java 8 `Stream` to read and convert individual elements while iterating the stream.
<10> Shows a query method annotated with `@AllowFiltering`, to allow server-side filtering.
Reactive::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {
Flux<Person> findByFirstname(String firstname); <1>
Flux<Person> findByFirstname(Publisher<String> firstname); <2>
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); <3>
Mono<Person> findFirstByFirstname(String firstname); <4>
@AllowFiltering
Flux<Person> findByAge(int age); <5>
}
----
<1> A query for all people with the given `firstname`.
The query is derived by parsing the method name for constraints, which can be concatenated with `And` and `Or`.
Thus, the method name results in a query expression of `SELECT * FROM person WHERE firstname = :firstname`.
<2> A query for all people with the given `firstname` once the `firstname` is emitted from the given `Publisher`.
<3> Find a single entity for the given criteria.
Completes with `IncorrectResultSizeDataAccessException` on non-unique results.
<4> Unlike the preceding query, the first entity is always emitted even if the query yields more result rows.
<5> A query method annotated with `@AllowFiltering`, which allows server-side filtering.
======
NOTE: Querying non-primary key properties requires secondary indexes.
The following table shows short examples of the keywords that you can use in query methods:
[cols="1,2,3",options="header"]
.Supported keywords for query methods
|===
| Keyword
| Sample
| Logical result
| `After`
| `findByBirthdateAfter(Date date)`
| `birthdate > date`
| `GreaterThan`
| `findByAgeGreaterThan(int age)`
| `age > age`
| `GreaterThanEqual`
| `findByAgeGreaterThanEqual(int age)`
| `age >= age`
| `Before`
| `findByBirthdateBefore(Date date)`
| `birthdate < date`
| `LessThan`
| `findByAgeLessThan(int age)`
| `age < age`
| `LessThanEqual`
| `findByAgeLessThanEqual(int age)`
| `age <= age`
| `Between`
| `findByAgeBetween(int from, int to)` and `findByAgeBetween(Range<Integer> range)`
| ``age > from AND age < to`` and
lower / upper bounds (`>` / `>=` & `<` / `<=`) according to `Range`
| `In`
| `findByAgeIn(Collection ages)`
| `age IN (ages...)`
| `Like`, `StartingWith`, `EndingWith`
| `findByFirstnameLike(String name)`
| `firstname LIKE (name as like expression)`
| `Containing` on String
| `findByFirstnameContaining(String name)`
| `firstname LIKE (name as like expression)`
| `Containing` on Collection
| `findByAddressesContaining(Address address)`
| `addresses CONTAINING address`
| `(No keyword)`
| `findByFirstname(String name)`
| `firstname = name`
| `IsTrue`, `True`
| `findByActiveIsTrue()`
| `active = true`
| `IsFalse`, `False`
| `findByActiveIsFalse()`
| `active = false`
|===
[[cassandra.repositories.queries.delete]]
== Repository Delete Queries
The keywords in the preceding table can be used in conjunction with `delete…By` to create queries that delete matching documents.
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
interface PersonRepository extends Repository<Person, String> {
void deleteWithoutResultByLastname(String lastname);
boolean deleteByLastname(String lastname);
}
----
Reactive::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface PersonRepository extends Repository<Person, String> {
Mono<Void> deleteWithoutResultByLastname(String lastname);
Mono<Boolean> deleteByLastname(String lastname);
}
----
======
Delete queries return whether the query was applied or terminate without returning a value using `void`.
[[cassandra.repositories.queries.options]]
=== Query Options
You can specify query options for query methods by passing a `QueryOptions` object.
The options apply to the query before the actual query execution.
`QueryOptions` is treated as a non-query parameter and is not considered to be a query parameter value.
Query options apply to derived and string `@Query` repository methods.
To statically set the consistency level, use the `@Consistency` annotation on query methods.
The declared consistency level is applied to the query each time it is executed.
The following example sets the consistency level to `ConsistencyLevel.LOCAL_ONE`:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
interface PersonRepository extends CrudRepository<Person, String> {
@Consistency(ConsistencyLevel.LOCAL_ONE)
List<Person> findByLastname(String lastname);
List<Person> findByFirstname(String firstname, QueryOptions options);
}
----
Reactive::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface PersonRepository extends ReactiveCrudRepository<Person, String> {
@Consistency(ConsistencyLevel.LOCAL_ONE)
Flux<Person> findByLastname(String lastname);
Flux<Person> findByFirstname(String firstname, QueryOptions options);
}
----
======
The DataStax Cassandra documentation includes https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html[a good discussion of the available consistency levels].
NOTE: You can control fetch size, consistency level, and retry policy defaults by configuring the following parameters on the CQL API instances: `CqlTemplate`, `AsyncCqlTemplate`, and `ReactiveCqlTemplate`.
Defaults apply if the particular query option is not set.

View File

@@ -0,0 +1,246 @@
[[cassandra.repositories]]
= Cassandra Repositories
To access domain entities stored in Apache Cassandra, you can use Spring Data's sophisticated repository support, which significantly eases implementing DAOs.
To do so, create an interface for your repository, as the following example shows:
.Sample Person entity
====
[source,java]
----
@Table
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
// … getters and setters omitted
}
----
====
Note that the entity has a property named `id` of type `String`.
The default conversion mechanism used in `MappingCassandraConverter` (which backs the repository support) regards properties named `id` as being the row ID.
The following example shows a repository definition to persist `Person` entities:
.Basic repository interface to persist `Person` entities
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
interface PersonRepository extends CrudRepository<Person, String> {
// additional custom finder methods go here
}
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface PersonRepository extends ReactiveCrudRepository<Person, String> {
// additional custom finder methods go here
}
----
======
Right now, the interface in the preceding example serves only typing purposes, but we add additional methods to it later.
Next, in your Spring configuration, add the following (if you use Java for configuration):
If you want to use Java configuration, use the `@EnableCassandraRepositories` respective `@EnableReactiveCassandraRepositories` annotation.
The annotation carries the same attributes as the namespace element.
If no base package is configured, the infrastructure scans the package of the annotated configuration class.
The following example show how to the different configuration approaches:
.Configuration for repositories
[tabs]
======
Imperative Java Configuration::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableCassandraRepositories
class ApplicationConfig extends AbstractCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "keyspace";
}
public String[] getEntityBasePackages() {
return new String[] { "com.oreilly.springdata.cassandra" };
}
}
----
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra
https://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<cassandra:session port="9042" keyspace-name="keyspaceName"/>
<cassandra:mapping
entity-base-packages="com.acme.*.entities">
</cassandra:mapping>
<cassandra:converter/>
<cassandra:template/>
<cassandra:repositories base-package="com.acme.*.entities"/>
</beans>
----
Reactive Java Configuration::
+
[source,java,indent=0,subs="verbatim,quotes",role="third"]
----
@Configuration
@EnableReactiveCassandraRepositories
class ApplicationConfig extends AbstractReactiveCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "keyspace";
}
public String[] getEntityBasePackages() {
return new String[] { "com.oreilly.springdata.cassandra" };
}
}
----
======
The `cassandra:repositories` namespace element causes the base packages to be scanned for interfaces that extend `CrudRepository` and create Spring beans for each one found.
By default, the repositories are wired with a `CassandraTemplate` Spring bean called `cassandraTemplate`, so you only need to configure
`cassandra-template-ref` explicitly if you deviate from this convention.
Because our domain repository extends `CrudRepository` respective `ReactiveCrudRepository`, it provides you with basic CRUD operations.
Working with the repository instance is a matter of injecting the repository as a dependency into a client, as the following example does by autowiring `PersonRepository`:
.Basic access to Person entities
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ExtendWith(SpringExtension.class)
class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
void readsPersonTableCorrectly() {
List<Person> persons = repository.findAll();
assertThat(persons.isEmpty()).isFalse();
}
}
----
Reactive::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
public class PersonRepositoryTests {
@Autowired ReactivePersonRepository repository;
@Test
public void sortsElementsCorrectly() {
Flux<Person> people = repository.findAll(Sort.by(new Order(ASC, "lastname")));
}
}
----
======
Cassandra repositories support paging and sorting for paginated and sorted access to the entities.
Cassandra paging requires a paging state to forward-only navigate through pages.
A `Slice` keeps track of the current paging state and allows for creation of a `Pageable` to request the next page.
The following example shows how to set up paging access to `Person` entities:
.Paging access to `Person` entities
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ExtendWith(SpringExtension.class)
class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
void readsPagesCorrectly() {
Slice<Person> firstBatch = repository.findAll(CassandraPageRequest.first(10));
assertThat(firstBatch).hasSize(10);
Slice<Person> nextBatch = repository.findAll(firstBatch.nextPageable());
// …
}
}
----
Reactive::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ExtendWith(SpringExtension.class)
class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
void readsPagesCorrectly() {
Mono<Slice<Person>> firstBatch = repository.findAll(CassandraPageRequest.first(10));
Mono<Slice<Person>> nextBatch = firstBatch.flatMap(it -> repository.findAll(it.nextPageable()));
// …
}
}}
----
======
NOTE: Cassandra repositories do not extend `PagingAndSortingRepository`, because classic paging patterns using limit/offset are not applicable to Cassandra.
The preceding example creates an application context with Spring's unit test support, which performs annotation-based dependency injection into the test class.
Inside the test cases (the test methods), we use the repository to query the data store.
We invoke the repository query method that requests all `Person` instances.
[[cassandra.repositories.reactive]]
== Reactive Repositories
Spring Data's repository abstraction is a dynamic API that is mostly defined by you and your requirements as you declare query methods.
Reactive Cassandra repositories can be implemented by using either RxJava or Project Reactor wrapper types by extending from one of the library-specific repository interfaces:
* `ReactiveCrudRepository`
* `ReactiveSortingRepository`
* `RxJava3CrudRepository`
* `RxJava3SortingRepository`
Spring Data converts reactive wrapper types behind the scenes so that you can stick to your favorite composition library.

View File

@@ -0,0 +1,253 @@
[[cassandra.schema-management]]
= Schema Management
Apache Cassandra is a data store that requires a schema definition prior to any data interaction.
Spring Data for Apache Cassandra can support you with schema creation.
[[keyspaces-and-lifecycle-scripts]]
== Keyspaces and Lifecycle Scripts
The first thing to start with is a Cassandra keyspace.
A keyspace is a logical grouping of tables that share the same replication factor and replication strategy.
Keyspace management is located in the `CqlSession` configuration, which has the `KeyspaceSpecification` and startup and shutdown CQL script execution.
Declaring a keyspace with a specification allows creating and dropping of the Keyspace.
It derives CQL from the specification so that you need not write CQL yourself.
The following example specifies a Cassandra keyspace by using XML:
.Specifying a Cassandra keyspace
====
.Java
[source,java,role="primary"]
----
include::example$CreateKeyspaceConfiguration.java[tags=class]
----
.XML
[source,xml,role="secondary"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra
https://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<cassandra:session>
<cassandra:keyspace action="CREATE_DROP" durable-writes="true" name="my_keyspace">
<cassandra:replication class="NETWORK_TOPOLOGY_STRATEGY">
<cassandra:data-center name="foo" replication-factor="1" />
<cassandra:data-center name="bar" replication-factor="2" />
</cassandra:replication>
</cassandra:keyspace>
</cassandra:session>
</beans>
----
====
NOTE: Keyspace creation allows rapid bootstrapping without the need of external keyspace management.
This can be useful for certain scenarios but should be used with care.
Dropping a keyspace on application shutdown removes the keyspace and all data from the tables in the keyspace.
[[cassandra.schema-management.initializing]]
== Initializing a `SessionFactory`
The `org.springframework.data.cassandra.core.cql.session.init` package provides support for initializing an existing `SessionFactory`.
You may sometimes need to initialize a keyspace that runs on a server somewhere.
[[cassandra.schema-management.initializing.config]]
=== Initializing a Keyspace
You can provide arbitrary CQL that is executed on `CqlSession` initialization and shutdown in the configured keyspace, as the following Java configuration example shows:
====
.Java
[source,java,role="primary"]
----
include::example$KeyspacePopulatorConfiguration.java[tags=class]
----
.XML
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory">
<cassandra:script location="classpath:com/foo/cql/db-schema.cql"/>
<cassandra:script location="classpath:com/foo/cql/db-test-data.cql"/>
</cassandra:initialize-keyspace>
----
====
The preceding example runs the two specified scripts against the keyspace.
The first script creates a schema, and the second populates tables with a test data set.
The script locations can also be patterns with wildcards in the usual Ant style used for resources in Spring (for example, `classpath{asterisk}:/com/foo/{asterisk}{asterisk}/cql/{asterisk}-data.cql`).
If you use a pattern, the scripts are run in the lexical order of their URL or filename.
The default behavior of the keyspace initializer is to unconditionally run the provided scripts.
This may not always be what you want -- for instance, if you run the scripts against a keyspace that already has test data in it.
The likelihood of accidentally deleting data is reduced by following the common pattern (shown earlier) of creating the tables first and then inserting the data.
The first step fails if the tables already exist.
However, to gain more control over the creation and deletion of existing data, the XML namespace provides a few additional options.
The first is a flag to switch the initialization on and off.
You can set this according to the environment (such as pulling a boolean value from system properties or from an environment bean).
The following example gets a value from a system property:
====
[source,xml,indent=0,subs="verbatim,quotes"]
----
<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory"
enabled="#{systemProperties.INITIALIZE_KEYSPACE}"> <1>
<cassandra:script location="..."/>
</cassandra:initialize-database>
----
<1> Get the value for `enabled` from a system property called `INITIALIZE_KEYSPACE`.
====
The second option to control what happens with existing data is to be more tolerant of failures.
To this end, you can control the ability of the initializer to ignore certain errors in the CQL it executes from the scripts, as the following example shows:
====
.Java
[source,java,role="primary"]
----
include::example$KeyspacePopulatorFailureConfiguration.java[tags=class]
----
.XML
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory" ignore-failures="DROPS">
<cassandra:script location="..."/>
</cassandra:initialize-database>
----
====
In the preceding example, we are saying that we expect that, sometimes, the scripts are run against an empty keyspace, and there are some `DROP` statements in the scripts that would, therefore, fail.
So failed CQL `DROP` statements will be ignored, but other failures will cause an exception.
This is useful if you don't want tu use support `DROP ... IF EXISTS` (or similar) but you want to unconditionally remove all test data before re-creating it.
In that case the first script is usually a set of `DROP` statements, followed by a set of `CREATE` statements.
The `ignore-failures` option can be set to `NONE` (the default), `DROPS` (ignore failed drops), or `ALL` (ignore all failures).
Each statement should be separated by `;` or a new line if the `;` character is not present at all in the script.
You can control that globally or script by script, as the following example shows:
====
.Java
[source,java,role="primary"]
----
include::example$SessionFactoryInitializerConfiguration.java[tags=class]
----
.XML
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<cassandra:initialize-keyspace session-factory-ref="cassandraSessionFactory" separator="@@">
<cassandra:script location="classpath:com/myapp/cql/db-schema.cql" separator=";"/>
<cassandra:script location="classpath:com/myapp/cql/db-test-data-1.cql"/>
<cassandra:script location="classpath:com/myapp/cql/db-test-data-2.cql"/>
</cassandra:initialize-keyspace>
----
====
In this example, the two `test-data` scripts use `@@` as statement separator and only the `db-schema.cql` uses `;`.
This configuration specifies that the default separator is `@@` and overrides that default for the `db-schema` script.
If you need more control than you get from the XML namespace, you can use the `SessionFactoryInitializer` directly and define it as a component in your application.
[[cassandra.schema-management.initializing.component]]
==== Initialization of Other Components that Depend on the Keyspace
A large class of applications (those that do not use the database until after the Spring context has started) can use the database initializer with no further complications.
If your application is not one of those, you might need to read the rest of this section.
The database initializer depends on a `SessionFactory` instance and runs the scripts provided in its initialization callback (analogous to an `init-method` in an XML bean definition, a `@PostConstruct` method in a component, or the `afterPropertiesSet()` method in a component that implements `InitializingBean`).
If other beans depend on the same data source and use the session factory in an initialization callback, there might be a problem because the data has not yet been initialized.
A common example of this is a cache that initializes eagerly and loads data from the database on application startup.
To get around this issue, you have two options: change your cache initialization strategy to a later phase or ensure that the keyspace initializer is initialized first.
Changing your cache initialization strategy might be easy if the application is in your control and not otherwise.
Some suggestions for how to implement this include:
* Make the cache initialize lazily on first usage, which improves application startup time.
* Have your cache or a separate component that initializes the cache implement `Lifecycle` or `SmartLifecycle`.
When the application context starts, you can automatically start a `SmartLifecycle` by setting its `autoStartup` flag, and you can manually start a `Lifecycle` by calling `ConfigurableApplicationContext.start()` on the enclosing context.
* Use a Spring `ApplicationEvent` or similar custom observer mechanism to trigger the cache initialization. `ContextRefreshedEvent` is always published by the context when it is ready for use (after all beans have been initialized), so that is often a useful hook (this is how the `SmartLifecycle` works by default).
Ensuring that the keyspace initializer is initialized first can also be easy.
Some suggestions on how to implement this include:
* Rely on the default behavior of the Spring `BeanFactory`, which is that beans are initialized in registration order.
You can easily arrange that by adopting the common practice of a set of `<import/>` elements in XML configuration that order your application modules and ensuring that the database and database initialization are listed first.
* Separate the `SessionFactory` and the business components that use it and control their startup order by putting them in separate `ApplicationContext` instances (for example, the parent context contains the `SessionFactory`, and the child context contains the business components).
This structure is common in Spring web applications but can be more generally applied.
* Use the Schema management for xref:cassandra/schema-management.adoc#cassandra.schema-management.tables[Tables and User-defined Types] to initialize the keyspace using Spring Data Cassandra's built-in schema generator.
[[cassandra.schema-management.tables]]
== Tables and User-defined Types
Spring Data for Apache Cassandra approaches data access with mapped entity classes that fit your data model.
You can use these entity classes to create Cassandra table specifications and user type definitions.
Schema creation is tied to `CqlSession` initialization by `SchemaAction`.
The following actions are supported:
* `SchemaAction.NONE`: No tables or types are created or dropped.
This is the default setting.
* `SchemaAction.CREATE`: Create tables, indexes, and user-defined types from entities annotated with `@Table` and types annotated with `@UserDefinedType`.
Existing tables or types cause an error if you tried to create the type.
* `SchemaAction.CREATE_IF_NOT_EXISTS`: Like `SchemaAction.CREATE` but with `IF NOT EXISTS` applied.
Existing tables or types do not cause any errors but may remain stale.
* `SchemaAction.RECREATE`: Drops and recreates existing tables and types that are known to be used.
Tables and types that are not configured in the application are not dropped.
* `SchemaAction.RECREATE_DROP_UNUSED`: Drops all tables and types and recreates only known tables and types.
NOTE: `SchemaAction.RECREATE` and `SchemaAction.RECREATE_DROP_UNUSED` drop your tables and lose all data.
`RECREATE_DROP_UNUSED` also drops tables and types that are not known to the application.
[[enabling-tables-and-user-defined-types-for-schema-management]]
=== Enabling Tables and User-Defined Types for Schema Management
xref:object-mapping.adoc#mapping.usage[Metadata-based Mapping] explains object mapping with conventions and annotations.
To prevent unwanted classes from being created as a table or a type, schema management is only active for entities annotated with `@Table` and user-defined types annotated with `@UserDefinedType`.
Entities are discovered by scanning the classpath.
Entity scanning requires one or more base packages.
Tuple-typed columns that use `TupleValue` do not provide any typing details.
Consequently, you must annotate such column properties with `@CassandraType(type = TUPLE, typeArguments = …)`
to specify the desired column type.
The following example shows how to specify entity base packages in XML configuration:
.Specifying entity base packages
====
.Java
[source,java,role="primary"]
----
include::example$EntityBasePackagesConfiguration.java[tags=class]
----
.XML
[source,xml,role="secondary"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra
https://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<cassandra:mapping entity-base-packages="com.foo,com.bar"/>
</beans>
----
====

View File

@@ -0,0 +1,567 @@
[[cassandra.template]]
= Persisting Entities
The `CassandraTemplate` class (and its reactive variant `ReactiveCassandraTemplate`), located in the `org.springframework.data.cassandra` package, is the central class in Spring's Cassandra support and provides a rich feature set to interact with the database.
The template offers convenience operations to create, update, delete, and query Cassandra, and provides a mapping between your domain objects and rows in Cassandra tables.
NOTE: Once configured, a template instance is thread-safe and can be reused across multiple instances.
The mapping between rows in Cassandra and application domain classes is done by delegating to an implementation of the `CassandraConverter` interface.
Spring provides a default implementation, `MappingCassandraConverter`, but you can also write your own custom converter.
See the section on
xref:object-mapping.adoc[Cassandra conversion] for more detailed information.
The `CassandraTemplate` class implements the `CassandraOperations` interface and its reactive variant `ReactiveCassandraTemplate` implements `ReactiveCassandraOperations`.
In as much as possible, the methods on `[Reactive]CassandraOperations` are named after methods available in Cassandra to make the API familiar to developers who are already familiar with Cassandra.
For example, you can find methods such as `select`, `insert`, `delete`, and `update`.
The design goal was to make it as easy as possible to transition between the use of the base Cassandra driver and `[Reactive]CassandraOperations`.
A major difference between the two APIs is that `CassandraOperations` can be passed domain objects instead of CQL and query objects.
NOTE: The preferred way to reference operations on a `[Reactive]CassandraTemplate` instance is through the
`[Reactive]CassandraOperations` interface.
The default converter implementation used by `[Reactive]CassandraTemplate` is `MappingCassandraConverter`.
While `MappingCassandraConverter` can use additional metadata to specify the mapping of objects to rows, it can also convert objects that contain no additional metadata by using some conventions for the mapping of fields and table names.
These conventions, as well as the use of mapping annotations, are explained in the xref:object-mapping.adoc["`Mapping`" chapter].
Another central feature of `[Reactive]CassandraTemplate` is exception translation of exceptions thrown in the Cassandra Java driver into Spring's portable Data Access Exception hierarchy.
See the section on
xref:cassandra/cql-template.adoc#exception-translation[exception translation] for more information.
NOTE: The Template API has different execution model flavors.
The basic `CassandraTemplate` uses a blocking (imperative-synchronous) execution model.
You can use `AsyncCassandraTemplate` for asynchronous execution and synchronization with `ListenableFuture` instances or `ReactiveCassandraTemplate` for reactive execution.
[[cassandra.template.instantiating]]
== Instantiating `CassandraTemplate`
`CassandraTemplate` should always be configured as a Spring bean, although we show an example earlier where you can instantiate it directly.
However, because we are assuming the context of making a Spring module, we assume the presence of the Spring container.
There are two ways to get a `CassandraTemplate`, depending on how you load you Spring `ApplicationContext`:
* xref:cassandra/template.adoc#cassandra-template-autowiring[Autowiring]
* xref:cassandra/template.adoc#cassandra-template-bean-lookup-applicationcontext[Bean Lookup with `ApplicationContext`]
[float]
[[cassandra-template-autowiring]]
=== Autowiring
You can autowire a `[Reactive]CassandraOperations` into your project, as the following example shows:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Autowired
private CassandraOperations cassandraOperations;
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Autowired
private ReactiveCassandraOperations reactiveCassandraOperations;
----
======
As with all Spring autowiring, this assumes there is only one bean of type `[Reactive]CassandraOperations` in the `ApplicationContext`.
If you have multiple `[Reactive]CassandraTemplate` beans (which is the case if you work with multiple keyspaces in the same project), then you can use the `@Qualifier` annotation to designate the bean you want to autowire.
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Autowired
@Qualifier("keyspaceOneTemplateBeanId")
private CassandraOperations cassandraOperations;
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Autowired
@Qualifier("keyspaceOneTemplateBeanId")
private ReactiveCassandraOperations reactiveCassandraOperations;
----
======
[float]
[[cassandra-template-bean-lookup-applicationcontext]]
=== Bean Lookup with `ApplicationContext`
You can also look up the `[Reactive]CassandraTemplate` bean from the `ApplicationContext`, as shown in the following example:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
CassandraOperations cassandraOperations = applicationContext.getBean("cassandraTemplate", CassandraOperations.class);
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
ReactiveCassandraOperations cassandraOperations = applicationContext.getBean("ReactiveCassandraOperations", ReactiveCassandraOperations.class);
----
======
[[cassandra.template.query]]
== Querying Rows
You can express your queries by using the `Query` and `Criteria` classes, which have method names that reflect the native Cassandra predicate operator names, such as `lt`, `lte`, `is`, and others.
The `Query` and `Criteria` classes follow a fluent API style so that you can easily chain together multiple method criteria and queries while having easy-to-understand code.
Static imports are used in Java when creating `Query`
and `Criteria` instances to improve readability.
[[cassandra.template.query.table]]
=== Querying Rows in a Table
In earlier sections, we saw how to retrieve a single object by using the `selectOneById` method on `[Reactive]CassandraTemplate`.
Doing so returns a single domain object.
We can also query for a collection of rows to be returned as a list of domain objects.
Assuming we have a number of `Person` objects with name and age values stored as rows in a table and that each person has an account balance, we can now run a query by using the following code:
.Querying for rows using `[Reactive]CassandraTemplate`
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
List<Person> result = cassandraTemplate.select(query(where("age").is(50))
.and(where("balance").gt(1000.00d)).withAllowFiltering(), Person.class);
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
Flux<Person> result = reactiveCassandraTemplate.select(query(where("age").is(50))
.and(where("balance").gt(1000.00d)).withAllowFiltering(), Person.class);
----
======
The `select`, `selectOne`, and `stream` methods take a `Query` object as a parameter.
This object defines the criteria and options used to perform the query.
The criteria is specified by using a `Criteria` object that has a static factory method named `where` that instantiates a new `Criteria` object.
We recommend using a static import for `org.springframework.data.cassandra.core.query.Criteria.where` and `Query.query`, to make the query more readable.
This query should return a list of `Person` objects that meet the specified criteria.
The `Criteria` class has the following methods that correspond to the operators provided in Apache Cassandra:
[[cassandra.template.query.criteria]]
==== Methods for the Criteria class
* `CriteriaDefinition` *gt* `(Object value)`: Creates a criterion by using the `>` operator.
* `CriteriaDefinition` *gte* `(Object value)`: Creates a criterion by using the `>=` operator.
* `CriteriaDefinition` *in* `(Object... values)`: Creates a criterion by using the `IN` operator for a varargs argument.
* `CriteriaDefinition` *in* `(Collection<?> collection)`: Creates a criterion by using the `IN` operator using a collection.
* `CriteriaDefinition` *is* `(Object value)`: Creates a criterion by using field matching (`column = value`).
* `CriteriaDefinition` *lt* `(Object value)`: Creates a criterion by using the `<` operator.
* `CriteriaDefinition` *lte* `(Object value)`: Creates a criterion by using the `<=` operator.
* `CriteriaDefinition` *like* `(Object value)`: Creates a criterion by using the `LIKE` operator.
* `CriteriaDefinition` *contains* `(Object value)`: Creates a criterion by using the `CONTAINS` operator.
* `CriteriaDefinition` *containsKey* `(Object key)`: Creates a criterion by using the `CONTAINS KEY` operator.
`Criteria` is immutable once created.
[[cassandra.template.query.query-class]]
=== Methods for the Query class
The `Query` class has some additional methods that you can use to provide options for the query:
* `Query` *by* `(CriteriaDefinition... criteria)`: Used to create a `Query` object.
* `Query` *and* `(CriteriaDefinition criteria)`: Used to add additional criteria to the query.
* `Query` *columns* `(Columns columns)`: Used to define columns to be included in the query results.
* `Query` *limit* `(Limit limit)`: Used to limit the size of the returned results to the provided limit (used `SELECT` limiting).
* `Query` *limit* `(long limit)`: Used to limit the size of the returned results to the provided limit (used `SELECT` limiting).
* `Query` *pageRequest* `(Pageable pageRequest)`: Used to associate `Sort`, `PagingState`, and `fetchSize` with the query (used for paging).
* `Query` *pagingState* `(ByteBuffer pagingState)`: Used to associate a `ByteBuffer` with the query (used for paging).
* `Query` *queryOptions* `(QueryOptions queryOptions)`: Used to associate `QueryOptions` with the query.
* `Query` *sort* `(Sort sort)`: Used to provide a sort definition for the results.
* `Query` *withAllowFiltering* `()`: Used to render `ALLOW FILTERING` queries.
`Query` is immutable once created.
Invoking methods creates new immutable (intermediate) `Query` objects.
[[cassandra.template.query.rows]]
=== Methods for Querying for Rows
The `Query` class has the following methods that return rows:
* `List<T>` *select* `(Query query, Class<T> entityClass)`: Query for a list of objects of type `T` from the table.
* `T` *selectOne* `(Query query, Class<T> entityClass)`: Query for a single object of type `T` from the table.
* `Slice<T>` *slice* `(Query query, Class<T> entityClass)`: Starts or continues paging by querying for a `Slice` of objects of type `T` from the table.
* `Stream<T>` *stream* `(Query query, Class<T> entityClass)`: Query for a stream of objects of type `T` from the table.
* `List<T>` *select* `(String cql, Class<T> entityClass)`: Ad-hoc query for a list of objects of type `T` from the table by providing a CQL statement.
* `T` *selectOne* `(String cql, Class<T> entityClass)`: Ad-hoc query for a single object of type `T` from the table by providing a CQL statement.
* `Stream<T>` *stream* `(String cql, Class<T> entityClass)`: Ad-hoc query for a stream of objects of type `T` from the table by providing a CQL statement.
The query methods must specify the target type `T` that is returned.
[[cassandra.template.query.fluent-template-api]]
=== Fluent Template API
The `[Reactive]CassandraOperations` interface is one of the central components when it comes to more low-level interaction with Apache Cassandra.
It offers a wide range of methods.
You can find multiple overloads for every method.
Most of them cover optional (nullable) parts of the API.
`FluentCassandraOperations` and its reactive variant `ReactiveFluentCassandraOperations` provide a more narrow interface for common methods of `[Reactive]CassandraOperations`
providing a more readable, fluent API.
The entry points (`query(…)`, `insert(…)`, `update(…)`, and `delete(…)`) follow a natural naming scheme based on the operation to execute.
Moving on from the entry point, the API is designed to offer only context-dependent methods that guide the developer towards a terminating method that invokes the actual `[Reactive]CassandraOperations`.
The following example shows the fluent API:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
List<SWCharacter> all = ops.query(SWCharacter.class)
.inTable("star_wars") <1>
.all();
----
<1> Skip this step if `SWCharacter` defines the table name with `@Table` or if using the class name as the table name is not a problem
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
Flux<SWCharacter> all = ops.query(SWCharacter.class)
.inTable("star_wars") <1>
.all();
----
<1> Skip this step if `SWCharacter` defines the table name with `@Table` or if using the class name as the table name is not a problem
======
If a table in Cassandra holds entities of different types, such as a `Jedi` within a Table of `SWCharacters`, you can use different types to map the query result.
You can use `as(Class<?> targetType)` to map results to a different target type, while `query(Class<?> entityType)` still applies to the query and table name.
The following example uses the `query` and `as` methods:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
List<Jedi> all = ops.query(SWCharacter.class) <1>
.as(Jedi.class) <2>
.matching(query(where("jedi").is(true)))
.all();
----
<1> The query fields are mapped against the `SWCharacter` type.
<2> Resulting rows are mapped into `Jedi`.
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
Flux<Jedi> all = ops.query(SWCharacter.class) <1>
.as(Jedi.class) <2>
.matching(query(where("jedi").is(true)))
.all();
----
<1> The query fields are mapped against the `SWCharacter` type.
<2> Resulting rows are mapped into `Jedi`.
======
TIP: You can directly apply xref:repositories/projections.adoc[] to resulting documents by providing only the `interface` type through `as(Class<?>)`.
The terminating methods (`first()`, `one()`, `all()`, and `stream()`) handle switching between retrieving a single entity and retrieving multiple entities as `List` or `Stream` and similar operations.
WARNING: The new fluent template API methods (that is, `query(..)`, `insert(..)`, `update(..)`, and `delete(..)`) use effectively thread-safe supporting objects to compose the CQL statement.
However, it comes at the added cost of additional young-gen JVM heap overhead, since the design is based on final fields for the various CQL statement components and construction on mutation.
You should be careful when possibly inserting or deleting a large number of objects (such as inside of a loop, for instance).
[[cassandra-template.save-update-remove]]
== Saving, Updating, and Removing Rows
`[Reactive]CassandraTemplate` provides a simple way for you to save, update, and delete your domain objects and map those objects to tables managed in Cassandra.
[[cassandra.template.type-mapping]]
=== Type Mapping
Spring Data for Apache Cassandra relies on the DataStax Java driver's `CodecRegistry` to ensure type support.
As types are added or changed, the Spring Data for Apache Cassandra module continues to function without requiring changes.
See https://docs.datastax.com/en/cql/3.3/cql/cql_reference/cql_data_types_c.html[CQL data types]
and "`xref:object-mapping.adoc#mapping-conversion[Data Mapping and Type Conversion]`" for the current type mapping matrix.
[[cassandra.template.insert-update]]
=== Methods for Inserting and Updating rows
`[Reactive]CassandraTemplate` has several convenient methods for saving and inserting your objects.
To have more fine-grained control over the conversion process, you can register Spring `Converter` instances with the `MappingCassandraConverter`
(for example, `Converter<Row, Person>`).
NOTE: The difference between insert and update operations is that `INSERT` operations do not insert `null` values.
The simple case of using the `INSERT` operation is to save a POJO.
In this case, the table name is determined by the simple class name (not the fully qualified class name).
The table to store the object can be overridden by using mapping metadata.
When inserting or updating, the `id` property must be set.
Apache Cassandra has no means to generate an ID.
The following example uses the save operation and retrieves its contents:
.Inserting and retrieving objects by using the `[Reactive]CassandraTemplate`
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Person queriedBob = cassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Mono<Person> queriedBob = reactiveCassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
----
======
You can use the following operations to insert and save:
* `void` *insert* `(Object objectToSave)`: Inserts the object in an Apache Cassandra table.
* `WriteResult` *insert* `(Object objectToSave, InsertOptions options)`: Inserts the object in an Apache Cassandra table and applies `InsertOptions`.
You can use the following update operations:
* `void` *update* `(Object objectToSave)`: Updates the object in an Apache Cassandra table.
* `WriteResult` *update* `(Object objectToSave, UpdateOptions options)`: Updates the object in an Apache Cassandra table and applies `UpdateOptions`.
You can also use the old fashioned way and write your own CQL statements, as the following example shows:
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
cassandraTemplate().getCqlOperations().execute(cql);
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
Mono<Boolean> applied = reactiveCassandraTemplate.getReactiveCqlOperations().execute(cql);
----
======
You can also configure additional options such as TTL, consistency level, and lightweight transactions when using `InsertOptions` and `UpdateOptions`.
[[cassandra.template.insert-update.table]]
==== Which Table Are My Rows Inserted into?
You can manage the table name that is used for operating on the tables in two ways.
The default table name is the simple class name changed to start with a lower-case letter.
So, an instance of the `com.example.Person` class would be stored in the `person` table.
The second way is to specify a table name in the `@Table` annotation.
[[cassandra.template.batch]]
==== Inserting, Updating, and Deleting Individual Objects in a Batch
The Cassandra protocol supports inserting a collection of rows in one operation by using a batch.
The following methods in the `[Reactive]CassandraTemplate` interface support this functionality:
* `batchOps`: Creates a new `[Reactive]CassandraBatchOperations` to populate the batch.
`[Reactive]CassandraBatchOperations`
* `insert`: Takes a single object, an array (var-args), or an `Iterable` of objects to insert.
* `update`: Takes a single object, an array (var-args), or an `Iterable` of objects to update.
* `delete`: Takes a single object, an array (var-args), or an `Iterable` of objects to delete.
* `withTimestamp`: Applies a TTL to the batch.
* `execute`: Executes the batch.
[[cassandra.template.update]]
=== Updating Rows in a Table
For updates, you can select to update a number of rows.
The following example shows updating a single account object by adding a one-time $50.00 bonus to the balance with the `+` assignment:
.Updating rows using `[Reactive]CasandraTemplate`
[tabs]
======
Imperative::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
boolean applied = cassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
----
Reactive::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
Mono<Boolean> wasApplied = reactiveCassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
----
======
In addition to the `Query` discussed earlier, we provide the update definition by using an `Update` object.
The `Update` class has methods that match the update assignments available for Apache Cassandra.
Most methods return the `Update` object to provide a fluent API for code styling purposes.
[[cassandra.template.update.methods]]
==== Methods for Executing Updates for Rows
The update method can update rows, as follows:
* `boolean` *update* `(Query query, Update update, Class<?> entityClass)`: Updates a selection of objects in the Apache Cassandra table.
[[cassandra.template.update.update]]
==== Methods for the Update class
The `Update` class can be used with a little 'syntax sugar', as its methods are meant to be chained together.
Also, you can kick-start the creation of a new `Update` instance with the static method `public static Update update(String key, Object value)` and by using static imports.
The `Update` class has the following methods:
* `AddToBuilder` *addTo* `(String columnName)` `AddToBuilder` entry-point:
** Update `prepend(Object value)`: Prepends a collection value to the existing collection by using the `+` update assignment.
** Update `prependAll(Object... values)`: Prepends all collection values to the existing collection by using the `+` update assignment.
** Update `append(Object value)`: Appends a collection value to the existing collection by using the `+` update assignment.
** Update `append(Object... values)`: Appends all collection values to the existing collection by using the `+` update assignment.
** Update `entry(Object key, Object value)`: Adds a map entry by using the `+` update assignment.
** Update `addAll(Map<? extends Object, ? extends Object> map)`: Adds all map entries to the map by using the `+` update assignment.
* `Update` *remove* `(String columnName, Object value)`: Removes the value from the collection by using the `-` update assignment.
* `Update` *clear* `(String columnName)`: Clears the collection.
* `Update` *increment* `(String columnName, Number delta)`: Updates by using the `+` update assignment.
* `Update` *decrement* `(String columnName, Number delta)`: Updates by using the `-` update assignment.
* `Update` *set* `(String columnName, Object value)`: Updates by using the `=` update assignment.
* `SetBuilder` *set* `(String columnName)` `SetBuilder` entry-point:
** Update `atIndex(int index).to(Object value)`: Sets a collection at the given index to a value using the `=` update assignment.
** Update `atKey(String object).to(Object value)`: Sets a map entry at the given key to a value the `=` update assignment.
The following listing shows a few update examples:
====
[source]
----
// UPDATE … SET key = 'Spring Data';
Update.update("key", "Spring Data")
// UPDATE … SET key[5] = 'Spring Data';
Update.empty().set("key").atIndex(5).to("Spring Data");
// UPDATE … SET key = key + ['Spring', 'DATA'];
Update.empty().addTo("key").appendAll("Spring", "Data");
----
====
Note that `Update` is immutable once created.
Invoking methods creates new immutable (intermediate) `Update` objects.
[[cassandra.template.delete]]
=== Methods for Removing Rows
You can use the following overloaded methods to remove an object from the database:
* `boolean` *delete* `(Query query, Class<?> entityClass)`: Deletes the objects selected by `Query`.
* `T` *delete* `(T entity)`: Deletes the given object.
* `T` *delete* `(T entity, QueryOptions queryOptions)`: Deletes the given object applying `QueryOptions`.
* `boolean` *deleteById* `(Object id, Class<?> entityClass)`: Deletes the object using the given Id.
[[cassandra.template.optimistic-locking]]
=== Optimistic Locking
The `@Version` annotation provides syntax similar to that of JPA in the context of Cassandra and makes sure updates are only applied to rows with a matching version.
Optimistic Locking leverages Cassandra's lightweight transactions to conditionally insert, update and delete rows.
Therefore, `INSERT` statements are executed with the `IF NOT EXISTS` condition.
For updates and deletes, the actual value of the version property is added to the `UPDATE` condition in such a way that the modification does not have any effect if another operation altered the row in the meantime.
In that case, an `OptimisticLockingFailureException` is thrown.
The following example shows these features:
====
[source,java]
----
@Table
class Person {
@Id String id;
String firstname;
String lastname;
@Version Long version;
}
Person daenerys = template.insert(new Person("Daenerys")); <1>
Person tmp = template.findOne(query(where("id").is(daenerys.getId())), Person.class); <2>
daenerys.setLastname("Targaryen");
template.save(daenerys); <3>
template.save(tmp); // throws OptimisticLockingFailureException <4>
----
<1> Intially insert document. `version` is set to `0`.
<2> Load the just inserted document. `version` is still `0`.
<3> Update the document with `version = 0`.
Set the `lastname` and bump `version` to `1`.
<4> Try to update the previously loaded document that still has `version = 0`.
The operation fails with an `OptimisticLockingFailureException`, as the current `version` is `1`.
====
NOTE: Optimistic Locking is only supported with single-entity operations and not for batch operations.

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$upgrade.adoc[]

View File

@@ -0,0 +1,22 @@
[[spring-data-cassandra-reference-documentation]]
= Spring Data Cassandra
:revnumber: {version}
:revdate: {localdate}
:feature-scroll: true
_Spring Data for Apache Cassandra provides repository support for the Apache Cassandra database.
It eases development of applications with a consistent programming model that need to access Cassandra data sources._
[horizontal]
xref:cassandra.adoc[Cassandra] :: Apache Cassandra support and connectivity
xref:repositories.adoc[Repositories] :: Apache Cassandra Repositories
xref:observability.adoc[Observability] :: Observability Integration
xref:kotlin.adoc[Kotlin] :: Kotlin support
xref:migration-guides.adoc[Migration] :: Migration Guides
https://github.com/spring-projects/spring-data-commons/wiki[Wiki] :: What's New, Upgrade Notes, Supported Versions, additional cross-version information.
David Webb, Matthew Adams, John Blum, Mark Paluch, Jay Bryant
(C) 2008-2023 VMware, Inc.
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$kotlin.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$kotlin/coroutines.adoc[]

View File

@@ -1,6 +1,4 @@
include::../{spring-data-commons-docs}/kotlin.adoc[]
include::../{spring-data-commons-docs}/kotlin-extensions.adoc[leveloffset=+1]
include::{commons}@data-commons::page$kotlin/extensions.adoc[]
To retrieve a list of `SWCharacter` objects in Java, you would normally write the following:
@@ -24,5 +22,3 @@ Spring Data for Apache Cassandra provides the following extensions:
* Reified generics support for `CassandraOperations` (including async and reactive variants), `CqlOperations` (including async and reactive variants)`FluentCassandraOperations`, `ReactiveFluentCassandraOperations`, `Criteria`, and `Query`.
* <<kotlin.coroutines>> extensions for `ReactiveFluentCassandraOperations`.
include::../{spring-data-commons-docs}/kotlin-coroutines.adoc[leveloffset=+1]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$kotlin/null-safety.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$kotlin/object-mapping.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$kotlin/requirements.adoc[]

View File

@@ -1,5 +1,5 @@
[[cassandra.migration.1.x-to-2.x]]
= Migration Guide from Spring Data Cassandra 1.x to 2.x
= Migration Guide from 1.x to 2.x
Spring Data for Apache Cassandra 2.0 introduces a set of breaking changes when upgrading from earlier versions:
@@ -14,6 +14,7 @@ into dedicated interfaces and templates.
* Refactored `QueryOptions` to be immutable objects.
* Refactored `CassandraPersistentProperty` to single-column.
[[deprecations]]
== Deprecations
* Deprecated `QueryOptionsBuilder.readTimeout(long, TimeUnit)` in favor of `QueryOptionsBuilder.readTimeout(Duration)`.
@@ -25,6 +26,7 @@ into dedicated interfaces and templates.
* Deprecated constructors of `QueryOptions` in favor of their builders.
* Deprecated `TypedIdCassandraRepository` in favor of `CassandraRepository`
[[merged-spring-cql-and-spring-data-cassandra-modules]]
== Merged Spring CQL and Spring Data Cassandra Modules
Spring CQL and Spring Data Cassandra are now merged into a single module.
@@ -57,6 +59,7 @@ With the merge, we merged all CQL packages into Spring Data Cassandra:
* Moved `o.s.d.c.mapping` to `o.s.d.c.core.mapping` (affects mapping annotations).
* Moved `MapId` from `o.s.d.c.repository` to `o.s.d.c.core.mapping`.
[[revised-cqltemplate/cassandratemplate]]
== Revised `CqlTemplate`/`CassandraTemplate`
We split `CqlTemplate` and `CassandraTemplate` in three ways:
@@ -70,6 +73,7 @@ versus `execute(…)`) and the reduced method set is aligned with Spring Framewo
* Asynchronous methods are re-implemented on `AsyncCqlTemplate` and `AsyncCassandraTemplate` by using `ListenableFuture`.
We removed `Cancellable` and the various async callback listeners. `ListenableFuture` is a flexible approach and allows transition into a `CompletableFuture`.
[[removed-cassandraoperations-selectbysimpleids]]
== Removed `CassandraOperations.selectBySimpleIds()`
The method was removed because it did not support complex IDs.
@@ -82,6 +86,7 @@ cassandraTemplate.select(Query.query(Criteria.where("id").in(…)), Person.class
----
====
[[better-names-for-cassandrarepository]]
== Better names for `CassandraRepository`
We renamed `CassandraRepository` and `TypedIdCassandraRepository` to align Spring Data Cassandra naming with other Spring Data modules:
@@ -90,6 +95,7 @@ We renamed `CassandraRepository` and `TypedIdCassandraRepository` to align Sprin
* Renamed `TypedIdCassandraRepository` to `CassandraRepository`
* Introduced `TypedIdCassandraRepository`, extending `CassandraRepository` as a deprecated type to ease migration
[[removed-sd-cassandra-consistencylevel-and-retrypolicy-types-in-favor-of-datastax-consistencylevel-and-retrypolicy-types]]
== Removed SD Cassandra `ConsistencyLevel` and `RetryPolicy` types in favor of DataStax `ConsistencyLevel` and `RetryPolicy` types
Spring Data Cassandra `ConsistencyLevel` and `RetryPolicy` have been removed.
@@ -98,12 +104,14 @@ Please use the types provided by the DataStax driver.
The Spring Data Cassandra types restricted usage of available features provided in and allowed by the Cassandra native driver.
As a result, the Spring Data Cassandra's types required an update each time newer functionality was introduced by the driver.
[[refactored-cql-specifications-to-value-objects-and-configurators]]
== Refactored CQL Specifications to Value Objects and Configurators
As much as possible, CQL specification types are now value types (such as `FieldSpecification`, `AlterColumnSpecification`), and objects are constructed by static factory methods.
This allows immutability for simple value objects.
Configurator objects (such as `AlterTableSpecification`) that operate on mandatory properties (such as a table name or keyspace name) are initially constructed through a a static factory method and allow further configuration until the desired state is created.
[[refactored-queryoptions-to-be-immutable-objects]]
== Refactored `QueryOptions` to be Immutable Objects
`QueryOptions` and `WriteOptions` are now immutable and can be created through builders.
@@ -122,6 +130,7 @@ QueryOptions queryOptions = QueryOptions.builder()
.build();
----
[[refactored-cassandrapersistentproperty-to-single-column]]
== Refactored `CassandraPersistentProperty` to Single-column
This change affects You only if you operate directly on the mapping model.

View File

@@ -1,13 +1,15 @@
[[cassandra.migration.2.x-to-3.x]]
= Migration Guide from Spring Data Cassandra 2.x to 3.x
= Migration Guide from 2.x to 3.x
Spring Data for Apache Cassandra 3.0 introduces a set of breaking changes when upgrading from earlier versions.
[[review-dependencies]]
== Review dependencies
Upgrading to Spring Data Cassandra requires an upgrade to the DataStax Driver version 4. Upgrading to the new driver comes with transitive dependency changes, most notably, Google Guava is bundled and shaded by the driver.
Check out the https://docs.datastax.com/en/developer/java-driver/4.3/upgrade_guide/[DataStax Java Driver for Apache Cassandra 4 Upgrade Guide] for details on the Driver-related changes.
[[adapt-configuration]]
== Adapt Configuration
DataStax Java Driver 4 merges `Cluster` and `Session` objects into a single `CqlSession` object, therefore, all `Cluster`-related API was removed.
@@ -19,6 +21,7 @@ If you're using XML-based configuration, make sure to migrate all configuration
To reflect the change in configuration builders, `ClusterBuilderConfigurer` was renamed to `SessionBuilderConfigurer` accepting now `CqlSessionBuilder` instead of the `Cluster.Builder`.
Make sure to also provide the local data center in your configuration as it is required to properly configure load balancing.
[[connectivity]]
=== Connectivity
The configuration elements for `Cluster` (`cassandra:cluster`) and `Session` (`cassandra:session`) were merged into a single `CqlSession` (`cassandra:session`) element that configures both, the keyspace and endpoints.
@@ -56,6 +59,7 @@ With the upgrade, schema support was moved to a new namespace element: `cassandr
NOTE: Spring Data Cassandra 3.0 no longer registers default Mapping Context, Context and Template API beans when using XML namespace configuration.
The defaulting should be applied on application or Spring Boot level.
[[template-api]]
== Template API
Spring Data for Apache Cassandra encapsulates most of the changes that come with the driver upgrade as the Template API and repository support if your application mainly interacts with mapped entities or primitive Java types.
@@ -94,6 +98,7 @@ Typical cases include:
* Calls to `CqlTemplate.queryForResultSet(…)`
* Calling methods that accept `Statement`
[[changes-in-asynccqltemplate]]
=== Changes in `AsyncCqlTemplate`
DataStax driver 4 has changed the result type of queries that are run asynchronously.
@@ -106,6 +111,7 @@ Result set extraction requires a new interface for DataStax' `AsyncResultSet`.
`AsyncCqlTemplate` now uses `AsyncResultSetExtractor` in places where it used previously `ResultSetExtractor`.
Note that `AsyncResultSetExtractor.extractData(…)` returns a `Future` instead of a scalar object so a migration of code comes with the possibility to use fully non-blocking code in the extractor.
[[data-model-migrations]]
== Data model migrations
Your data model may require updates if you use the following features:
@@ -115,23 +121,27 @@ Your data model may require updates if you use the following features:
* Properties using `java.lang.Date`
* Properties using `UDTValue` or `TupleValue`
[[cassandratype]]
=== `@CassandraType`
DataStax driver 4 no longer ships with a `Name` enumeration to describe the Cassandra type.
We decided to re-introduce the enumeration with `CassandraType.Name`.
Make sure to update your imports to use the newly introduced replacement type.
[[force-quote]]
=== Force Quote
This flag is now deprecated, and we recommend not to use it any longer.
Spring Data for Apache Cassandra internally uses the driver's `CqlIdentifier` that ensures quoting where it's required.
[[property-types]]
=== Property Types
DataStax driver 4 no longer uses `java.lang.Date`.
Please upgrade your data model to use `java.time.LocalDateTime`.
Please also migrate raw UDT and tuple types to the new driver types `UdtValue` respective `TupleValue`.
[[other-changes]]
== Other changes
* Driver's `ConsistencyLevel` constant class was removed and reintroduced as `DefaultConsistencyLevel`. `@Consistency` was adapted to `DefaultConsistencyLevel`.
@@ -148,6 +158,7 @@ Previously it returned just `ReactiveSession`.
* Data type resolution was moved into `ColumnTypeResolver` so all `DataType`-related methods were moved from `CassandraPersistentEntity`/`CassandraPersistentProperty` into `ColumnTypeResolver` (affected methods are `MappingContext.getDataType(…)`, `CassandraPersistentProperty.getDataType()`, `CassandraPersistentEntity.getUserType()`, and `CassandraPersistentEntity.getTupleType()`).
* Schema creation was moved from `MappingContext` to `SchemaFactory` (affected methods are `CassandraMappingContext.getCreateTableSpecificationFor(…)`, `CassandraMappingContext.getCreateIndexSpecificationsFor(…)`, and `CassandraMappingContext.getCreateUserTypeSpecificationFor(…)`).
[[deprecations]]
== Deprecations
* `CassandraCqlSessionFactoryBean`, use `CqlSessionFactoryBean` instead.
@@ -164,8 +175,10 @@ Previously it returned just `ReactiveSession`.
* Schema creation via `CqlSessionFactoryBean` (`cassandra:session`) is deprecated.
Keyspace creation via `CqlSessionFactoryBean` (`cassandra:session`) is not affected.
[[removals]]
== Removals
[[removal.configuration-api]]
=== Configuration API
* `PoolingOptionsFactoryBean`
@@ -178,6 +191,7 @@ Keyspace creation via `CqlSessionFactoryBean` (`cassandra:session`) is not affec
* `AbstractClusterConfiguration`
* `ClusterBuilderConfigurer` (use `SessionBuilderConfigurer` instead
[[utilities]]
=== Utilities
* `GuavaListenableFutureAdapter`
@@ -186,6 +200,7 @@ Use the builder in conjunction of execution profiles as replacement.
* `CassandraAccessor.setRetryPolicy(…)` and `ReactiveCqlTemplate.setRetryPolicy(…)` methods.
Use execution profiles as replacement.
[[removal.namespace-support]]
=== Namespace support
* `cql` namespace (`http://www.springframework.org/schema/cql`, use `http://www.springframework.org/schema/data/cassandra` instead)
@@ -194,8 +209,10 @@ Use execution profiles as replacement.
* Removed implicit bean registrations Mapping Context, Context and Template API beans.
These must be declared explicitly.
[[additions]]
== Additions
[[add.configuration-api]]
=== Configuration API
* `CqlSessionFactoryBean`
@@ -203,6 +220,7 @@ These must be declared explicitly.
* `SessionFactoryFactoryBean` including schema creation via `KeyspacePopulator`
* `KeyspacePopulator` and `SessionFactoryInitializer` to initialize a keyspace
[[add.namespace-support]]
=== Namespace support
* `cassandra:cluster` (endpoint properties merged to `cassandra:session`)

View File

@@ -1,8 +1,9 @@
[[cassandra.migration.3.x-to-4.x]]
= Migration Guide from Spring Data Cassandra 3.x to 4.x
= Migration Guide from 3.x to 4.x
Spring Data for Apache Cassandra 4.0 introduces a set of breaking changes when upgrading from earlier versions.
[[asynchronous-template-api]]
== Asynchronous Template API
With the deprecation of `ListenableFuture`, `AsyncCqlOperations` and `AsyncCassandraOperations` and their dependant classes were migrated to `CompletableFuture`.

View File

@@ -0,0 +1,8 @@
[[cassandra.migration]]
= Migration Guides
:page-section-summary-toc: 1
This section contains version-specific migration guides explaining how to upgrade between two versions.

View File

@@ -9,7 +9,7 @@ The `MappingCassandraConverter` also lets you map domain objects to tables witho
In this chapter, we describe the features of the `MappingCassandraConverter`, how to use conventions for mapping domain objects to tables, and how to override those conventions with annotation-based mapping metadata.
include::../{spring-data-commons-docs}/object-mapping.adoc[leveloffset=+1]
include::{commons}@data-commons::page$object-mapping.adoc[leveloffset=+1]
[[mapping-conversion]]
== Data Mapping and Type Conversion
@@ -19,7 +19,7 @@ This section explains how types are mapped to and from an Apache Cassandra repre
Spring Data for Apache Cassandra supports several types that are provided by Apache Cassandra.
In addition to these types, Spring Data for Apache Cassandra provides a set of built-in converters to map additional types.
You can provide your own custom converters to adjust type conversion.
See "`<<cassandra.custom-converters>>`" for further details.
See "`xref:cassandra/converters.adoc[Overriding Default Mapping with Custom Converters]`" for further details.
The following table maps Spring Data types to Cassandra types:
[cols="3,2",options="header"]
@@ -150,7 +150,7 @@ The following example shows how to configure a `NamingStrategy`:
====
[source,java]
----
include::../{example-root}/NamingStrategyConfiguration.java[tags=method]
include::example$NamingStrategyConfiguration.java[tags=method]
----
====
@@ -167,7 +167,7 @@ The following example configuration class sets up Cassandra mapping support:
====
[source,java]
----
include::../{example-root}/SchemaConfiguration.java[tags=class]
include::example$SchemaConfiguration.java[tags=class]
----
====
@@ -308,7 +308,7 @@ The following example shows a class with a flat composite primary key:
====
[source,java]
----
include::../{example-root}/LoginEvent.java[tags=class]
include::example$LoginEvent.java[tags=class]
----
====
@@ -325,7 +325,7 @@ The following example shows a composite primary key class:
====
[source,java]
----
include::../{example-root}/LoginEventKey.java[tags=class]
include::example$LoginEventKey.java[tags=class]
----
====
@@ -465,7 +465,7 @@ The following example shows a more complex mapping:
====
[source,java]
----
include::../{example-root}/mapping/Person.java[tags=class]
include::example$mapping/Person.java[tags=class]
----
====
@@ -475,12 +475,12 @@ The following example shows how to map a UDT `Address`:
====
[source,java]
----
include::../{example-root}/mapping/Address.java[tags=class]
include::example$mapping/Address.java[tags=class]
----
====
NOTE: Working with User-Defined Types requires a `UserTypeResolver` that is configured with the mapping context.
See the <<cassandra.connectors,configuration chapter>> for how to configure a `UserTypeResolver`.
See the xref:cassandra/configuration.adoc[configuration chapter] for how to configure a `UserTypeResolver`.
The following example shows how map a tuple:
@@ -488,7 +488,7 @@ The following example shows how map a tuple:
====
[source,java]
----
include::../{example-root}/mapping/Coordinates.java[tags=class]
include::example$mapping/Coordinates.java[tags=class]
----
====
@@ -509,7 +509,7 @@ The following example shows a number of ways to create an index:
====
[source,java]
----
include::../{example-root}/mapping/PersonWithIndexes.java[tags=class]
include::example$mapping/PersonWithIndexes.java[tags=class]
----
====
@@ -520,47 +520,10 @@ The `@Indexed` annotation can be applied to single properties of embedded entiti
CAUTION: Index creation on session initialization may have a severe performance impact on application startup.
include::./converters.adoc[]
include::../{spring-data-commons-docs}/is-new-state-detection.adoc[leveloffset=+1]
[[cassandra.entity-persistence.state-detection-strategies]]
include::{commons}@data-commons::page$is-new-state-detection.adoc[leveloffset=+1]
NOTE: Cassandra provides no means to generate identifiers upon inserting data.
As consequence, entities must be associated with identifier values.
Spring Data defaults to identifier inspection to determine whether an entity is new.
If you want to use <<cassandra.auditing,auditing>> make sure to either use <<cassandra.template.optimistic-locking>> or implement `Persistable` for proper entity state detection.
[[cassandra.mapping-usage.events]]
== Lifecycle Events
The Cassandra mapping framework has several built-in `org.springframework.context.ApplicationEvent` events that your application can respond to by registering special beans in the `ApplicationContext`.
Being based on Spring's application context event infrastructure lets other products, such as Spring Integration, easily receive these events as they are a well known eventing mechanism in Spring-based applications.
To intercept an object before it goes into the database, you can register a subclass of `org.springframework.data.cassandra.core.mapping.event.AbstractCassandraEventListener` that overrides the `onBeforeSave(…)` method.
When the event is dispatched, your listener is called and passed the domain object (which is a Java entity).
Entity lifecycle events can be costly and you may notice a change in the performance profile when loading large result sets.
You can disable lifecycle events on the link:https://docs.spring.io/spring-data/cassandra/docs/{version}/api/org/springframework/data/cassandra/core/CassandraTemplate.html#setEntityLifecycleEventsEnabled(boolean)[Template API].
The following example uses the `onBeforeSave` method:
====
[source,java]
----
include::../{example-root}/mapping/BeforeSaveListener.java[tags=class]
----
====
Declaring these beans in your Spring `ApplicationContext` will cause them to be invoked whenever the event is dispatched.
The `AbstractCassandraEventListener` has the following callback methods:
* `onBeforeSave`: Called in `CassandraTemplate.insert(…)` and `.update(…)` operations before inserting or updating a row in the database.
* `onAfterSave`: Called in `CassandraTemplate…insert(…)` and `.update(…)` operations after inserting or updating a row in the database.
* `onBeforeDelete`: Called in `CassandraTemplate.delete(…)` operations before deleting row from the database.
* `onAfterDelete`: Called in `CassandraTemplate.delete(…)` operations after deleting row from the database.
* `onAfterLoad`: Called in the `CassandraTemplate.select(…)`, `.slice(…)`, and `.stream(…)` methods after each row is retrieved from the database.
* `onAfterConvert`: Called in the `CassandraTemplate.select(…)`, `.slice(…)`, and `.stream(…)` methods after converting a row retrieved from the database to a POJO.
NOTE: Lifecycle events are emitted only for root-level types.
Complex types used as properties within an aggregate root are not subject to event publication.
include::../{spring-data-commons-docs}/entity-callbacks.adoc[leveloffset=+1]
include::./cassandra-entity-callbacks.adoc[leveloffset=+2]
If you want to use xref:cassandra/auditing.adoc[auditing] make sure to either use xref:cassandra/template.adoc#cassandra.template.optimistic-locking[Optimistic Locking] or implement `Persistable` for proper entity state detection.

View File

@@ -1,5 +1,5 @@
[[cassandra.observability]]
== Observability
= Observability
Getting insights from an application component about its operations, timing and relation to application code is crucial to understand latency.
Spring Data Cassandra ships with a Micrometer instrumentation through the Cassandra driver to collect observations during Cassandra interaction.
@@ -32,10 +32,10 @@ Also, registers `ObservationRequestTracker.INSTANCE` with the `CqlSessionBuilder
<2> Wraps a CQL session object to observe reactive Cassandra statement execution.
====
include::../observability/_conventions.adoc[]
include::../observability/_metrics.adoc[]
include::../observability/_spans.adoc[]
See also https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/database/#cassandra[OpenTelemetry Semantic Conventions] for further reference.
include::observability/conventions.adoc[leveloffset=+1]
include::observability/metrics.adoc[leveloffset=+1]
include::observability/spans.adoc[leveloffset=+1]

View File

@@ -1,5 +1,5 @@
[[observability-conventions]]
=== Observability - Conventions
= Conventions
Below you can find a list of all `GlobalObservabilityConventions` and `ObservabilityConventions` declared by this project.

View File

@@ -1,10 +1,10 @@
[[observability-metrics]]
=== Observability - Metrics
= Metrics
Below you can find a list of all metrics declared by this project.
[[observability-metrics-cassandra-query-observation]]
==== Cassandra Query Observation
== Cassandra Query Observation
____
Create an `io.micrometer.observation.Observation` for Cassandra-based queries.

View File

@@ -1,10 +1,10 @@
[[observability-spans]]
=== Observability - Spans
= Spans
Below you can find a list of all spans declared by this project.
[[observability-spans-cassandra-query-observation]]
==== Cassandra Query Observation Span
== Cassandra Query Observation Span
> Create an `io.micrometer.observation.Observation` for Cassandra-based queries.

View File

@@ -62,6 +62,7 @@ Spring Data for Apache Cassandra 2.x binaries require JDK level 8.0 and later an
It requires https://cassandra.apache.org/[Cassandra] 2.0 or later and Datastax driver 4.x.
[[additional-help-resources]]
== Additional Help Resources
Learning a new framework is not always straight forward.

View File

@@ -0,0 +1,8 @@
[[cassandra.repositories]]
= Repositories
:page-section-summary-toc: 1
This chapter explains the basic foundations of Spring Data repositories and Cassandra specifics.
Before continuing to the Cassandra specifics, make sure you have a sound understanding of the basic concepts.
The goal of the Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/core-concepts.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/core-domain-events.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/core-extensions.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/create-instances.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/custom-implementations.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/definition.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/null-handling.adoc[]

View File

@@ -0,0 +1,4 @@
[[cassandra.projections]]
= Projections
include::{commons}@data-commons::page$repositories/projections.adoc[leveloffset=+1]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/query-keywords-reference.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/query-methods-details.adoc[]

View File

@@ -0,0 +1 @@
include::{commons}@data-commons::page$repositories/query-return-types-reference.adoc[]

View File

@@ -0,0 +1,20 @@
version: ${antora-component.version}
prerelease: ${antora-component.prerelease}
asciidoc:
attributes:
version: ${project.version}
springversionshort: ${spring.short}
springversion: ${spring}
attribute-missing: 'warn'
commons: ${springdata.commons.docs}
include-xml-namespaces: false
spring-data-commons-docs-url: https://docs.spring.io/spring-data-commons/reference
spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/${springdata.commons}/api/
springdocsurl: https://docs.spring.io/spring-framework/reference/{springversionshort}
springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api
spring-framework-docs: '{springdocsurl}'
spring-framework-javadoc: '{springjavadocurl}'
springhateoasversion: ${spring-hateoas}
releasetrainversion: ${releasetrain}
store: Cassandra

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,44 +0,0 @@
= Spring Data for Apache Cassandra - Reference Documentation
David Webb, Matthew Adams, John Blum, Mark Paluch, Jay Bryant
:revnumber: {version}
:revdate: {localdate}
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
:example-root: ../../../spring-data-cassandra/src/test/java/org/springframework/data/cassandra/example
:example-resources: ../../../spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/example
:tabsize: 2
(C) 2008-2023 The original author(s).
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
include::preface.adoc[]
include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1]
[[reference]]
= Reference Documentation
include::reference/introduction.adoc[leveloffset=+1]
include::reference/upgrade.adoc[leveloffset=+1]
include::reference/cassandra.adoc[leveloffset=+1]
include::reference/observability.adoc[leveloffset=+1]
include::reference/reactive-cassandra.adoc[leveloffset=+1]
include::reference/cassandra-repositories.adoc[leveloffset=+1]
include::reference/reactive-cassandra-repositories.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/auditing.adoc[leveloffset=+1]
include::reference/cassandra-auditing.adoc[leveloffset=+1]
include::reference/mapping.adoc[leveloffset=+1]
include::reference/kotlin.adoc[leveloffset=+1]
[[appendix]]
= Appendix
:numbered!:
include::{spring-data-commons-docs}/repository-namespace-reference.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/repository-populator-namespace-reference.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/repository-query-keywords-reference.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/repository-query-return-types-reference.adoc[leveloffset=+1]
include::reference/migration-guides.adoc[leveloffset=+1]

View File

@@ -1,30 +0,0 @@
[[cassandra.entity-callbacks]]
= Store specific EntityCallbacks
Spring Data for Apache Cassandra uses the `EntityCallback` API for its auditing support and reacts on the following callbacks.
.Supported Entity Callbacks
[%header,cols="4"]
|===
| Callback
| Method
| Description
| Order
| Reactive/BeforeConvertCallback
| `onBeforeConvert(T entity, CqlIdentifier tableName)`
| Invoked before a domain object is converted to `com.datastax.driver.core.Statement`.
| `Ordered.LOWEST_PRECEDENCE`
| Reactive/AuditingEntityCallback
| `onBeforeConvert(Object entity, CqlIdentifier tableName)`
| Marks an auditable entity _created_ or _modified_
| 100
| Reactive/BeforeSaveCallback
| `onBeforeSave(T entity, CqlIdentifier tableName, Statement statement)`
| Invoked before a domain object is saved. +
Can modify the target, to be persisted, `com.datastax.driver.core.Statement` containing all mapped entity information.
| `Ordered.LOWEST_PRECEDENCE`
|===

View File

@@ -1,368 +0,0 @@
[[cassandra.repositories]]
= Cassandra Repositories
This chapter covers the details of the Spring Data Repository support for Apache Cassandra.
Cassandra's repository support builds on the core repository support explained in "`<<repositories>>`".
Cassandra repositories use `CassandraTemplate` and its wired `CqlTemplate` as infrastructure beans.
You should understand the basic concepts explained there before proceeding.
[[cassandra-repo-usage]]
== Usage
To access domain entities stored in Apache Cassandra, you can use Spring Data's sophisticated repository support, which significantly eases implementing DAOs.
To do so, create an interface for your repository, as the following example shows:
.Sample Person entity
====
[source,java]
----
@Table
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
// … getters and setters omitted
}
----
====
Note that the entity has a property named `id` of type `String`.
The default serialization mechanism used in `CassandraTemplate` (which backs the repository support) regards properties named `id` as being the row ID.
The following example shows a repository definition to persist `Person` entities:
.Basic repository interface to persist `Person` entities
====
[source]
----
public interface PersonRepository extends CrudRepository<Person, String> {
// additional custom finder methods go here
}
----
====
Right now, the interface in the preceding example serves only typing purposes, but we add additional methods to it later.
Next, in your Spring configuration, add the following (if you use Java for configuration):
If you want to use Java configuration, use the `@EnableCassandraRepositories` annotation.
The annotation carries the same attributes as the namespace element.
If no base package is configured, the infrastructure scans the package of the annotated configuration class.
The following example shows how to use the `@EnableCassandraRepositories` annotation:
.Configuration for repositories
====
.Java
[source,java,role="primary"]
----
@Configuration
@EnableCassandraRepositories
class ApplicationConfig extends AbstractCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "keyspace";
}
public String[] getEntityBasePackages() {
return new String[] { "com.oreilly.springdata.cassandra" };
}
}
----
.XML
[source,xml,role="secondary"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra
https://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<cassandra:session port="9042" keyspace-name="keyspaceName"/>
<cassandra:mapping
entity-base-packages="com.acme.*.entities">
</cassandra:mapping>
<cassandra:converter/>
<cassandra:template/>
<cassandra:repositories base-package="com.acme.*.entities"/>
</beans>
----
====
The `cassandra:repositories` namespace element causes the base packages to be scanned for interfaces that extend `CrudRepository` and create Spring beans for each one found.
By default, the repositories are wired with a `CassandraTemplate` Spring bean called `cassandraTemplate`, so you only need to configure
`cassandra-template-ref` explicitly if you deviate from this convention.
Because our domain repository extends `CrudRepository`, it provides you with basic CRUD operations.
Working with the repository instance is a matter of injecting the repository as a dependency into a client, as the following example does by autowiring `PersonRepository`:
.Basic access to Person entities
====
[source,java]
----
@ExtendWith(SpringExtension.class)
class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
void readsPersonTableCorrectly() {
List<Person> persons = repository.findAll();
assertThat(persons.isEmpty()).isFalse();
}
}
----
====
Cassandra repositories support paging and sorting for paginated and sorted access to the entities.
Cassandra paging requires a paging state to forward-only navigate through pages.
A `Slice` keeps track of the current paging state and allows for creation of a `Pageable` to request the next page.
The following example shows how to set up paging access to `Person` entities:
.Paging access to `Person` entities
====
[source,java]
----
@ExtendWith(SpringExtension.class)
class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
void readsPagesCorrectly() {
Slice<Person> firstBatch = repository.findAll(CassandraPageRequest.first(10));
assertThat(firstBatch).hasSize(10);
Slice<Person> nextBatch = repository.findAll(firstBatch.nextPageable());
// …
}
}
----
====
NOTE: Cassandra repositories do not extend `PagingAndSortingRepository`, because classic paging patterns using limit/offset are not applicable to Cassandra.
The preceding example creates an application context with Spring's unit test support, which performs annotation-based dependency injection into the test class.
Inside the test cases (the test methods), we use the repository to query the data store.
We invoke the repository query method that requests all `Person` instances.
[[cassandra.repositories.queries]]
== Query Methods
Most of the data access operations you usually trigger on a repository result in a query being executed against the Apache Cassandra database.
Defining such a query is a matter of declaring a method on the repository interface.
The following example shows a number of such method declarations:
.PersonRepository with query methods
====
[source,java]
----
interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByLastname(String lastname); <1>
Slice<Person> findByFirstname(String firstname, Pageable pageRequest); <2>
Window<Person> findByFirstname(String firstname, CassandraScrollPosition pos, Limit limit); <3>
List<Person> findByFirstname(String firstname, QueryOptions opts); <4>
List<Person> findByFirstname(String firstname, Sort sort); <5>
List<Person> findByFirstname(String firstname, Limit limit); <6>
Person findByShippingAddress(Address address); <7>
Person findFirstByShippingAddress(Address address); <8>
Stream<Person> findAllBy(); <9>
@AllowFiltering
List<Person> findAllByAge(int age); <10>
}
----
<1> The method shows a query for all people with the given `lastname`.
The query is derived from parsing the method name for constraints, which can be concatenated with `And`.
Thus, the method name results in a query expression of `SELECT * FROM person WHERE lastname = 'lastname'`.
<2> Applies pagination to a query.
You can equip your method signature with a `Pageable` parameter and let the method return a `Slice` instance, and we automatically page the query accordingly.
<3> Applies scrolling to a query.
Scrolling wraps Cassandra's `PagingState` into `CassandraScrollPosition` and allows dynamic limiting.
You can also use `findTop…` for a static limit.
<4> Passing a `QueryOptions` object applies the query options to the resulting query before its execution.
<5> Applies dynamic sorting to a query.
You can add a `Sort` parameter to your method signature, and Spring Data automatically applies ordering to the query.
<6> Applies dynamic result limiting to a query.
Query results can be limited using `SELECT … LIMIT`.
<7> Shows that you can query based on properties that are not a primitive type by using `Converter` instances registered in `CustomConversions`.
Throws `IncorrectResultSizeDataAccessException` if more than one match is found.
<8> Uses the `First` keyword to restrict the query to only the first result.
Unlike the preceding method, this method does not throw an exception if more than one match is found.
<9> Uses a Java 8 `Stream` to read and convert individual elements while iterating the stream.
<10> Shows a query method annotated with `@AllowFiltering`, to allow server-side filtering.
====
NOTE: Querying non-primary key properties requires secondary indexes.
The following table shows short examples of the keywords that you can use in query methods:
[cols="1,2,3",options="header"]
.Supported keywords for query methods
|===
| Keyword
| Sample
| Logical result
| `After`
| `findByBirthdateAfter(Date date)`
| `birthdate > date`
| `GreaterThan`
| `findByAgeGreaterThan(int age)`
| `age > age`
| `GreaterThanEqual`
| `findByAgeGreaterThanEqual(int age)`
| `age >= age`
| `Before`
| `findByBirthdateBefore(Date date)`
| `birthdate < date`
| `LessThan`
| `findByAgeLessThan(int age)`
| `age < age`
| `LessThanEqual`
| `findByAgeLessThanEqual(int age)`
| `age <= age`
| `Between`
| `findByAgeBetween(int from, int to)` and `findByAgeBetween(Range<Integer> range)`
| ``age > from AND age < to`` and
lower / upper bounds (`>` / `>=` & `<` / `<=`) according to `Range`
| `In`
| `findByAgeIn(Collection ages)`
| `age IN (ages...)`
| `Like`, `StartingWith`, `EndingWith`
| `findByFirstnameLike(String name)`
| `firstname LIKE (name as like expression)`
| `Containing` on String
| `findByFirstnameContaining(String name)`
| `firstname LIKE (name as like expression)`
| `Containing` on Collection
| `findByAddressesContaining(Address address)`
| `addresses CONTAINING address`
| `(No keyword)`
| `findByFirstname(String name)`
| `firstname = name`
| `IsTrue`, `True`
| `findByActiveIsTrue()`
| `active = true`
| `IsFalse`, `False`
| `findByActiveIsFalse()`
| `active = false`
|===
[[cassandra.repositories.queries.delete]]
== Repository Delete Queries
The keywords in the preceding table can be used in conjunction with `delete…By` to create queries that delete matching documents.
====
[source,java]
----
interface PersonRepository extends Repository<Person, String> {
void deleteWithoutResultByLastname(String lastname);
boolean deleteByLastname(String lastname);
}
----
====
Delete queries return whether the query was applied or terminate without returning a value using `void`.
include::../{spring-data-commons-docs}/repository-projections.adoc[leveloffset=+2]
[[cassandra.repositories.queries.options]]
=== Query Options
You can specify query options for query methods by passing a `QueryOptions` object.
The options apply to the query before the actual query execution.
`QueryOptions` is treated as a non-query parameter and is not considered to be a query parameter value.
Query options apply to derived and string `@Query` repository methods.
To statically set the consistency level, use the `@Consistency` annotation on query methods.
The declared consistency level is applied to the query each time it is executed.
The following example sets the consistency level to `ConsistencyLevel.LOCAL_ONE`:
====
[source,java]
----
interface PersonRepository extends CrudRepository<Person, String> {
@Consistency(ConsistencyLevel.LOCAL_ONE)
List<Person> findByLastname(String lastname);
List<Person> findByFirstname(String firstname, QueryOptions options);
}
----
====
The DataStax Cassandra documentation includes https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html[a good discussion of the available consistency levels].
NOTE: You can control fetch size, consistency level, and retry policy defaults by configuring the following parameters on the CQL API instances: `CqlTemplate`, `AsyncCqlTemplate`, and `ReactiveCqlTemplate`.
Defaults apply if the particular query option is not set.
[[cassandra.repositories.misc.cdi-integration]]
=== CDI Integration
Instances of the repository interfaces are usually created by a container, and the Spring container is the most natural choice when working with Spring Data.
Spring Data for Apache Cassandra ships with a custom CDI extension that allows using the repository abstraction in CDI environments.
The extension is part of the JAR.To activate it, drop the Spring Data for Apache Cassandra JAR into your classpath.
You can now set up the infrastructure by implementing a CDI Producer for the
`CassandraTemplate`, as the following examlpe shows:
====
[source,java]
----
include::../{example-root}/CassandraTemplateProducer.java[tags=class]
----
====
The Spring Data for Apache Cassandra CDI extension picks up `CassandraOperations` as a CDI bean and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container.
Thus, obtaining an instance of a Spring Data repository is a matter of declaring an injected property, as the following example shows:
====
[source,java]
----
include::../{example-root}/RepositoryClient.java[tags=class]
----
====

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
== Exception Translation
The Spring Framework provides exception translation for a wide variety of database and mapping technologies.
This has traditionally been for JDBC and JPA.
Spring Data for Apache Cassandra extends this feature to Apache Cassandra by providing an implementation of the `org.springframework.dao.support.PersistenceExceptionTranslator` interface.
The motivation behind mapping to Spring's {springDocsUrl}html/dao.html#dao-exceptions[consistent data access exception hierarchy]
is to let you write portable and descriptive exception handling code without resorting to coding against and handling specific Cassandra exceptions.
All of Spring's data access exceptions are inherited from the
`DataAccessException` class, so you can be sure that you can catch all database-related exceptions within a single try-catch block.

View File

@@ -1,8 +0,0 @@
[[cassandra.migration]]
= Appendix E: Migration Guides
include::migration-guide-1.5-to-2.0.adoc[leveloffset=+1]
include::migration-guide-2.2-to-3.0.adoc[leveloffset=+1]
include::migration-guide-3.0-to-4.0.adoc[leveloffset=+1]

View File

@@ -1,181 +0,0 @@
[[cassandra.reactive.repositories]]
= Reactive Cassandra Repositories
This chapter outlines the specialties handled by the reactive repository support for Apache Cassandra.
It builds on the core repository infrastructure explained in <<cassandra.repositories>>, so you should have a good understanding of the basic concepts explained there.
Cassandra repositories use `ReactiveCassandraTemplate` and its wired `ReactiveCqlTemplate` as infrastructure beans.
Reactive usage is broken up into two phases: Composition and Execution.
Calling repository methods lets you compose a reactive sequence by obtaining `Publisher` instances and applying operators.
No I/O happens until you subscribe.
Passing the reactive sequence to a reactive execution infrastructure, such as {springDocsUrl}web.html#web-reactive[Spring WebFlux]
or https://vertx.io/docs/vertx-reactive-streams/java/[Vert.x]), subscribes to the publisher and initiate the actual execution.
See https://projectreactor.io/docs/core/release/reference/#reactive.subscribe[the Project reactor documentation] for more detail.
[[cassandra.reactive.repositories.libraries]]
== Reactive Composition Libraries
The reactive space offers various reactive composition libraries.
The most common libraries are
https://github.com/ReactiveX/RxJava[RxJava] and https://projectreactor.io/[Project Reactor].
Spring Data for Apache Cassandra is built on top of the https://github.com/datastax/java-driver[DataStax Cassandra Driver].
The driver is not reactive but the asynchronous capabilities allow us to adopt and expose the `Publisher` APIs to provide maximum interoperability by relying on the https://www.reactive-streams.org/[Reactive Streams] initiative.
Static APIs, such as `ReactiveCassandraOperations`, are provided by using Project Reactor's `Flux` and `Mono` types.
Project Reactor offers various adapters to convert reactive wrapper types (`Flux` to `Observable` and back), but conversion can easily clutter your code.
Spring Data's repository abstraction is a dynamic API that is mostly defined by you and your requirements as you declare query methods.
Reactive Cassandra repositories can be implemented by using either RxJava or Project Reactor wrapper types by extending from one of the library-specific repository interfaces:
* `ReactiveCrudRepository`
* `ReactiveSortingRepository`
* `RxJava2CrudRepository`
* `RxJava2SortingRepository`
Spring Data converts reactive wrapper types behind the scenes so that you can stick to your favorite composition library.
[[cassandra.reactive.repositories.usage]]
== Usage
To access domain entities stored in Apache Cassandra, you can use Spring Data's sophisticated repository support, which significantly eases implementing DAOs.
To do so, create an interface for your repository, as the following example shows:
.Sample Person entity
====
[source,java]
----
@Table
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
// … getters and setters omitted
}
----
====
Note that the entity has a property named `id` of type `String`.
The default serialization mechanism used in `CassandraTemplate` (which backs the repository support) regards properties named `id` as being the row ID.
The following example shows a repository definition to persist `Person` entities:
.Basic repository interface to persist `Person` entities
====
[source]
----
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {
Flux<Person> findByFirstname(String firstname); <1>
Flux<Person> findByFirstname(Publisher<String> firstname); <2>
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); <3>
Mono<Person> findFirstByFirstname(String firstname); <4>
@AllowFiltering
Flux<Person> findByAge(int age); <5>
}
----
<1> A query for all people with the given `firstname`.
The query is derived by parsing the method name for constraints, which can be concatenated with `And` and `Or`.
Thus, the method name results in a query expression of `SELECT * FROM person WHERE firstname = :firstname`.
<2> A query for all people with the given `firstname` once the `firstname` is emitted from the given `Publisher`.
<3> Find a single entity for the given criteria.
Completes with `IncorrectResultSizeDataAccessException` on non-unique results.
<4> Unlike the preceding query, the first entity is always emitted even if the query yields more result rows.
<5> A query method annotated with `@AllowFiltering`, which allows server-side filtering.
====
For Java configuration, use the `@EnableReactiveCassandraRepositories` annotation.
The annotation carries the same attributes as the corresponding XML namespace element.
If no base package is configured, the infrastructure scans the package of the annotated configuration class.
The following example uses the `@EnableReactiveCassandraRepositories` annotation:
.Java configuration for repositories
====
[source,java]
----
@Configuration
@EnableReactiveCassandraRepositories
class ApplicationConfig extends AbstractReactiveCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "keyspace";
}
public String[] getEntityBasePackages() {
return new String[] { "com.oreilly.springdata.cassandra" };
}
}
----
====
Since our domain repository extends `ReactiveSortingRepository`, it provides you with CRUD operations as well as methods for sorted access to the entities.
Working with the repository instance is a matter of dependency injecting it into a client, as the following example shows:
.Sorted access to Person entities
====
[source,java]
----
public class PersonRepositoryTests {
@Autowired ReactivePersonRepository repository;
@Test
public void sortsElementsCorrectly() {
Flux<Person> people = repository.findAll(Sort.by(new Order(ASC, "lastname")));
}
}
----
====
Cassandra repositories support paging and sorting for paginated and sorted access to the entities.
Cassandra paging requires a paging state to forward-only navigate through pages.
A `Slice` keeps track of the current paging state and allows for creation of a `Pageable` to request the next page.
The following example shows how to set up paging access to `Person` entities:
.Paging access to `Person` entities
====
[source,java]
----
@ExtendWith(SpringExtension.class)
class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
void readsPagesCorrectly() {
Mono<Slice<Person>> firstBatch = repository.findAll(CassandraPageRequest.first(10));
Mono<Slice<Person>> nextBatch = firstBatch.flatMap(it -> repository.findAll(it.nextPageable()));
// …
}
}
----
====
The preceding example creates an application context with Spring's unit test support, which performs annotation-based dependency injection into the test class.
Inside the test cases (the test methods), we use the repository to query the data store.
We invoke the repository query method that requests all `Person` instances.
[[cassandra.reactive.repositories.features]]
== Features
Spring Data's Reactive Cassandra support comes with the same set of features as the support for <<cassandra.repositories,imperative repositories>>.
It supports the following features:
* Query Methods that use <<cassandra.repositories.queries,String queries and Query Derivation>>
* <<projections>>
NOTE: Query methods must return a reactive type.
Resolved types (`User` versus `Mono<User>`) are not supported.

View File

@@ -1,441 +0,0 @@
[[cassandra.reactive]]
= Reactive Cassandra Support
The reactive Cassandra support contains a wide range of features:
* Spring configuration support using Java-based `@Configuration` classes.
* `ReactiveCqlTemplate` helper class that increases productivity by properly handling common Cassandra data access operations.
* `ReactiveCassandraTemplate` helper class that increases productivity by using `ReactiveCassandraOperations` in a reactive manner.
It includes integrated object mapping between tables and POJOs.
* Exception translation into Spring's portable {springDocsUrl}data-access.html#dao-exceptions[Data Access Exception Hierarchy].
* Feature rich object mapping integrated with Spring's {springDocsUrl}core.html#core-convert[Conversion Service].
* Java-based Query, Criteria, and Update DSLs.
* Automatic implementation of `Repository` interfaces, including support for custom finder methods.
For most data-oriented tasks, you can use the `ReactiveCassandraTemplate` or the repository support, which use the rich object mapping functionality. `ReactiveCqlTemplate` is commonly used to increment counters or perform ad-hoc CRUD operations. `ReactiveCqlTemplate` also provides callback methods that make it easy to get low-level API objects, such as `com.datastax.oss.driver.api.core.CqlSession`, which let you communicate directly with Cassandra.
Spring Data for Apache Cassandra uses consistent naming conventions on objects in various APIs to those found in the DataStax Java Driver so that they are immediately familiar and so that you can map your existing knowledge onto the Spring APIs.
[[cassandra.reactive.getting-started]]
== Getting Started
Spring Data for Apache Cassandra requires Apache Cassandra 2.1 or later and Datastax Java Driver 4.0 or later.
An easy way to quickly set up and bootstrap a working environment is to create a Spring-based project in https://spring.io/tools[Spring Tools] or use https://start.spring.io/[Spring Initializer].
First, you need to set up a running Apache Cassandra server.
See the
https://cassandra.apache.org/doc/latest/getting_started/index.html[Apache Cassandra Quick Start Guide]
for an explanation on how to start Apache Cassandra.
Once installed, starting Cassandra is typically a matter of running the following command: `CASSANDRA_HOME/bin/cassandra -f`.
To create a Spring project in STS, go to File -> New -> Spring Template Project -> Simple Spring Utility Project and press Yes when prompted.
Then enter a project and a package name, such as `org.spring.data.cassandra.example`.
Then you can add the following dependency declaration to your pom.xml file's `dependencies` section.
====
[source,xml,subs="verbatim,attributes"]
----
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
<version>{version}</version>
</dependency>
</dependencies>
----
====
Also, you should change the version of Spring in the pom.xml file to be as follows:
====
[source,xml,subs="verbatim,attributes"]
----
<spring.framework.version>{springVersion}</spring.framework.version>
----
====
If using a milestone release instead of a GA release, you also need to add the location of the Spring Milestone repository for Maven to your pom.xml file so that it is at the same level of your `<dependencies/>` element, as follows:
[source,xml]
----
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
----
The repository is also https://repo.spring.io/milestone/org/springframework/data/[browseable here].
You can also browse all Spring repositories https://repo.spring.io/webapp/#/home[here].
Now you can create a simple Java application that stores and reads a domain object to and from Cassandra.
To do so, first create a simple domain object class to persist, as the following example shows:
====
[source,java]
----
include::../{example-root}/Person.java[tags=file]
----
====
Next, create the main application to run, as the following example shows:
====
[source,java]
----
include::../{example-root}/ReactiveCassandraApplication.java[tags=file]
----
====
Even in this simple example, there are a few notable things to point out:
* A fully synchronous flow does not benefit from a reactive infrastructure, because a reactive programming model requires synchronization.
* You can create an instance of `ReactiveCassandraTemplate` with a Cassandra `CqlSession`.
* You must annotate your POJO as a Cassandra `@Table` and annotate the `@PrimaryKey`.
Optionally, you can override these mapping names to match your Cassandra database table and column names.
* You can either use raw CQL or the DataStax `QueryBuilder` API to construct your queries.
[[cassandra.reactive.examples-repo]]
== Examples Repository
A https://github.com/spring-projects/spring-data-examples[Github repository] contains several examples that you can download and play around with to get a feel for how the library works.
[[cassandra.reactive.connectors]]
== Connecting to Cassandra with Spring
One of the first tasks when using Apache Cassandra with Spring is to create a `com.datastax.oss.driver.api.core.CqlSession` object by using the Spring IoC container.
You can do so either by using Java-based bean metadata or by using XML-based bean metadata.
These are discussed in the following sections.
NOTE: For those not familiar with how to configure the Spring container using Java-based bean metadata instead of XML-based metadata, see the high-level introduction in the reference docs
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-java[here]
as well as the detailed documentation {springDocsUrl}core.html#beans-java-instantiating-container[here].
[[reactive.cassandra.java-config]]
=== Registering a Session instance using Java-based metadata
You can configure Reactive Cassandra support by using <<cassandra.cassandra-java-config,Java Configuration classes>>.
Reactive Cassandra support adapts a `CqlSession` to provide a reactive processing model on top of an asynchronous driver.
A reactive `CqlSession` is configured similarly to an imperative `CqlSession`.
We provide supporting configuration classes that come with predefined defaults and require only environment-specific information to configure Spring Data for Apache Cassandra.
The base class for reactive support is `AbstractReactiveCassandraConfiguration`.
This configuration class extends the imperative `AbstractCassandraConfiguration`, so the reactive support also configures the imperative API support.
The following example shows how to register Apache Cassandra beans in a configuration class:
ReactiveAppCassandraConfiguration .Registering Spring Data for Apache Cassandra beans using `AbstractReactiveCassandraConfiguration`
====
[source,java]
----
include::../{example-root}/ReactiveCassandraConfiguration.java[tags=class]
----
====
The configuration class in the preceding example is schema-management-enabled to create CQL objects during startup.
See <<cassandra.schema-management>> for further details.
[[cassandra.reactive.cql-template]]
== `ReactiveCqlTemplate`
The `ReactiveCqlTemplate` class is the central class in the core CQL package.
It handles the creation and release of resources.
It performs the basic tasks of the core CQL workflow, such as creating and running statements, leaving application code to provide CQL and extract results.
The `ReactiveCqlTemplate` class runs CQL queries and update statements and performs iteration over `ResultSet` instances and extraction of returned parameter values.
It also catches CQL exceptions and translates them into the generic, more informative, exception hierarchy defined in the `org.springframework.dao` package.
When you use the `ReactiveCqlTemplate` in your code, you need only implement callback interfaces, which have a clearly defined contract.
Given a `Connection`, the `ReactivePreparedStatementCreator` callback interface creates a <<cassandra.template.prepared-statements.cql,prepared statement>> with the provided CQL and any necessary parameter arguments.
The `RowCallbackHandler`
interface extracts values from each row of a `ReactiveResultSet`.
The `ReactiveCqlTemplate` can be used within a DAO implementation through direct instantiation with a `ReactiveSessionFactory`
reference or be configured in the Spring container and given to DAOs as a bean reference. `ReactiveCqlTemplate` is a foundational building block for <<cassandra.reactive.template,`ReactiveCassandraTemplate`>>.
All CQL issued by this class is logged at the `DEBUG` level under the category corresponding to the fully-qualified class name of the template instance (typically `ReactiveCqlTemplate`, but it may be different if you use a custom subclass of the `ReactiveCqlTemplate` class).
[[cassandra.reactive.cql-template.examples]]
=== Examples of `ReactiveCqlTemplate` Class Usage
This section provides some examples of `ReactiveCqlTemplate` class usage.
These examples are not an exhaustive list of all of the functionality exposed by the `ReactiveCqlTemplate`.
See the attendant https://docs.spring.io/spring-data/cassandra/docs/{version}/api/org/springframework/data/cassandra/core/cql/ReactiveCqlTemplate.html[Javadocs] for that.
[[cql-template.examples.query]]
==== Querying (SELECT) with `ReactiveCqlTemplate`
The following query gets the number of rows in a relation:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=rowCount]
----
====
The following query uses a bind variable:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=countOfActorsNamedJoe]
----
====
The following example queries for a `String`:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=lastName]
----
====
The following example queries and populates a single domain object:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=rowMapper]
----
====
The following example queries and populates a number of domain objects:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=listOfRowMapper]
----
====
If the last two snippets of code actually existed in the same application, it would make sense to remove the duplication present in the two `RowMapper` anonymous inner classes and extract them into a single class (typically a `static` nested class) that can then be referenced by DAO methods as needed.
For example, it might be better to write the last code snippet as follows:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=findAllActors]
----
====
[[cassandra.reactive.cql-template.examples.update]]
==== `INSERT`, `UPDATE`, and `DELETE` with `ReactiveCqlTemplate`
You can use the `execute(…)` method to perform `INSERT`, `UPDATE`, and `DELETE` operations.
Parameter values are usually provided as variable arguments or, alternatively, as an object array.
The following example shows how to perform an `INSERT` operation with `ReactiveCqlTemplate`:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=insert]
----
====
The following example shows how to perform an `UPDATE` operation with `ReactiveCqlTemplate`:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=update]
----
====
The following example shows how to perform an `DELETE` operation with `ReactiveCqlTemplate`:
====
[source,java,indent=0]
----
include::../{example-root}/ReactiveCqlTemplateExamples.java[tags=delete]
----
====
[[cassandra.reactive.exception]]
include::exception-translation.adoc[]
`ReactiveCqlTemplate` and `ReactiveCassandraTemplate` propagate exceptions as early as possible.
Exceptions that occur during the processing of the reactive sequence are emitted as error signals.
[[cassandra.reactive.template]]
== Introduction to `ReactiveCassandraTemplate`
The `ReactiveCassandraTemplate` class, located in the `org.springframework.data.cassandra` package, is the central class in Spring Data's Cassandra support.
It provides a rich feature set to interact with the database.
The template offers convenience data access operations to create, update, delete, and query Cassandra and provides a mapping between your domain objects and Cassandra table rows.
NOTE: Once configured, `ReactiveCassandraTemplate` is thread-safe and can be reused across multiple instances.
The mapping between rows in a Cassandra table and domain classes is done by delegating to an implementation of the `CassandraConverter` interface.
Spring provides a default implementation, `MappingCassandraConverter`, but you can also write your own custom converter.
See "`<<mapping.chapter>>`" for more detailed information.
The `ReactiveCassandraTemplate` class implements the `ReactiveCassandraOperations` interface.
As often as possible, the methods names `ReactiveCassandraOperations` match names in Cassandra to make the API familiar to developers who are familiar with Cassandra.
For example, you can find methods such as `select`, `insert`, `delete`, and `update`.
The design goal was to make it as easy as possible to transition between the use of the base Cassandra driver and `ReactiveCassandraOperations`.
A major difference between the two APIs is that `ReactiveCassandraOperations` can be passed domain objects instead of CQL and query objects.
NOTE: The preferred way to reference operations on a `ReactiveCassandraTemplate` instance is through its interface,
`ReactiveCassandraOperations`.
The default converter implementation for `ReactiveCassandraTemplate` is `MappingCassandraConverter`.
While the `MappingCassandraConverter` can make use of additional metadata to specify the mapping of objects to rows, it can also convert objects that contain no additional metadata by using conventions for the mapping of fields and table names.
These conventions, as well as the use of mapping annotations, are explained in "`<<mapping.chapter>>`".
Another central feature of `CassandraTemplate` is exception translation.
Exceptions thrown by the Cassandra Java driver are translated into Spring's portable Data Access Exception hierarchy.
See "`<<cassandra.exception>>`" for more information.
[[cassandra.reactive.template.instantiating]]
=== Instantiating `ReactiveCassandraTemplate`
`ReactiveCassandraTemplate` should always be configured as a Spring bean, although an earlier example showed how to instantiate it directly.
However, this section assumes that the template is used in a Spring module, so it also assumes that the Spring container is being used.
There are two ways to get a `ReactiveCassandraTemplate`, depending on how you load you Spring `ApplicationContext`:
* <<reactive.cassandra.template.autowiring>>
* <<reactive.cassandra.template.application-context>>
[float]
[[reactive.cassandra.template.autowiring]]
==== Autowiring
You can autowire a `ReactiveCassandraTemplate` into your project, as the following example shows:
====
[source,java]
----
@Autowired
private ReactiveCassandraOperations reactiveCassandraOperations;
----
====
Like all Spring autowiring, this assumes there is only one bean of type `ReactiveCassandraOperations` in the `ApplicationContext`.
If you have multiple `ReactiveCassandraTemplate` beans (which can be the case if you are working with multiple keyspaces in the same project), then you can use the `@Qualifier` annotation to designate which bean you want to autowire.
====
[source,java]
----
@Autowired
@Qualifier("keyspaceTwoTemplateBeanId")
private ReactiveCassandraOperations reactiveCassandraOperations;
----
====
[float]
[[reactive.cassandra.template.application-context]]
==== Bean Lookup with `ApplicationContext`
You can also look up the `ReactiveCassandraTemplate` bean from the `ApplicationContext`, as shown in the following example:
====
[source,java]
----
ReactiveCassandraOperations reactiveCassandraOperations = applicationContext.getBean("reactiveCassandraOperations", ReactiveCassandraOperations.class);
----
====
[[cassandra.reactive.template.save-update-remove]]
== Saving, Updating, and Removing Rows
`ReactiveCassandraTemplate` provides a simple way for you to save, update, and delete your domain objects and map those objects to tables managed in Cassandra.
[[cassandra.reactive.template.insert-update]]
=== Methods for Inserting and Updating rows
`CassandraTemplate` has several convenient methods for saving and inserting your objects.
To have more fine-grained control over the conversion process, you can register Spring `Converter` instances with the `MappingCassandraConverter`
(for example, `Converter<Row, Person>`).
NOTE: The difference between insert and update operations is that `INSERT` operations do not insert `null` values.
The simple case of using the `INSERT` operation is to save a POJO. In this case, the table name is determined by the simple class name (not the fully qualified class name).
The table to store the object can be overridden by using mapping metadata.
When inserting or updating, the `id` property must be set.
Apache Cassandra has no means to generate an ID.
The following example uses the save operation and retrieves its contents:
.Inserting and retrieving objects by using the `CassandraTemplate`
====
[source,java]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import static org.springframework.data.cassandra.core.query.Query.query;
Person bob = new Person("Bob", 33);
cassandraTemplate.insert(bob);
Mono<Person> queriedBob = reactiveCassandraTemplate.selectOneById(query(where("age").is(33)), Person.class);
----
====
You can use the following operations to insert and save:
* `void` *insert* `(Object objectToSave)`: Inserts the object in an Apache Cassandra table.
* `WriteResult` *insert* `(Object objectToSave, InsertOptions options)`: Inserts the object in an Apache Cassandra table and applies `InsertOptions`.
You can use the following update operations:
* `void` *update* `(Object objectToSave)`: Updates the object in an Apache Cassandra table.
* `WriteResult` *update* `(Object objectToSave, UpdateOptions options)`: Updates the object in an Apache Cassandra table and applies `UpdateOptions`.
You can also use the old fashioned way and write your own CQL statements, as the following example shows:
[source,java]
----
String cql = "INSERT INTO person (age, name) VALUES (39, 'Bob')";
Mono<Boolean> applied = reactiveCassandraTemplate.getReactiveCqlOperations().execute(cql);
----
You can also configure additional options such as TTL, consistency level, and lightweight transactions when using `InsertOptions` and `UpdateOptions`.
[[cassandra.reactive.template.insert-update.table]]
==== Which Table Are My Rows Inserted into?
You can manage the table name that is used for operating on the tables in two ways.
The default table name is the simple class name changed to start with a lower-case letter.
So, an instance of the `com.example.Person` class would be stored in the `person` table.
The second way is to specify a table name in the `@Table` annotation.
[[cassandra.reactive.template.update]]
=== Updating Rows in a Table
For updates, you can select to update a number of rows.
The following example shows updating a single account object by adding a one-time $50.00 bonus to the balance with the `+` assignment:
.Updating rows using `ReactiveCasandraTemplate`
====
[source,java]
----
import static org.springframework.data.cassandra.core.query.Criteria.where;
import org.springframework.data.cassandra.core.query.Query;
import org.springframework.data.cassandra.core.query.Update;
Mono<Boolean> wasApplied = reactiveCassandraTemplate.update(Query.query(where("id").is("foo")),
Update.create().increment("balance", 50.00), Account.class);
----
====
In addition to the `Query` discussed earlier, we provide the update definition by using an `Update` object.
The `Update` class has methods that match the update assignments available for Apache Cassandra.
Most methods return the `Update` object to provide a fluent API for code styling purposes.
For more detail, see "`<<cassandra.template.update.methods>>`".

View File

@@ -1,9 +0,0 @@
include::../{spring-data-commons-docs}/upgrade.adoc[]
== What to Read Next
Once youve decided to upgrade your application, you can find detailed information regarding specific features in the rest of the document.
You can find <<cassandra.migration,migration guides>> specific to major version migrations at the end of this document.
Spring Data's documentation is specific to that version, so any information that you find in here will contain the most up-to-date changes that are in that version.