[[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:

.Java configuration for repositories
====
[source,java]
----
@Configuration
@EnableCassandraRepositories
class ApplicationConfig extends AbstractCassandraConfiguration {

  @Override
  protected String getKeyspaceName() {
    return "keyspace";
  }

  public String[] getEntityBasePackages() {
    return new String[] { "com.oreilly.springdata.cassandra" };
  }
}
----
====

If you want to use XML configuration, then the following example shows a minimal configuration snippet:

.Cassandra repository Spring XML configuration
====
[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">

    <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]
----
@RunWith(SpringRunner.class)
@ContextConfiguration
public class PersonRepositoryTests {

    @Autowired PersonRepository repository;

    @Test
    public 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]
----
@RunWith(SpringRunner.class)
@ContextConfiguration
public class PersonRepositoryTests {

    @Autowired PersonRepository repository;

    @Test
    public void readsPagesCorrectly() {

      Slice<Person> firstBatch = repository.findAll(CassandraPageRequest.first(10));

      assertThat(firstBatch).hasSize(10);

      Page<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]
----
public interface PersonRepository extends CrudRepository<Person, String> {

    List<Person> findByLastname(String lastname);                           <1>

    Slice<Person> findByFirstname(String firstname, Pageable pageRequest);  <2>

    List<Person> findByFirstname(String firstname, QueryOptions opts);      <3>

    List<Person> findByFirstname(String firstname, Sort sort);              <4>

    Person findByShippingAddress(Address address);                          <5>

    Person findFirstByShippingAddress(Address address);                     <6>

    Stream<Person> findAllBy();                                             <7>

    @AllowFiltering
    List<Person> findAllByAge(int age);                                     <8>
}
----
<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> Passing a `QueryOptions` object applies the query options to the resulting query before its execution.
<4> 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.
<5> 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.
<6> 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.
<7> Uses a Java 8 `Stream` to read and convert individual elements while iterating the stream.
<8> 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]
----
public 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/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]
----
====
