Spring Data Cassandra
This document describes how to use Spring Data Cassandra and reactive Spring Data Cassandra with YCQL.
Spring Data Cassandra and YCQL
YCQL provides Cassandra wire-compatible query language for client applications to query the YugabyteDB database. YCQL is integrated with the Spring Data Cassandra project and supports POJO-based annotations, data templates, repositories, and so on.
The following is a non-exhaustive list of supported features:
- Yugabyte Java driver for YCQL 3.x and 4.x drivers.
- Automatic implementation of Repository interfaces including support for custom query methods.
- Build repositories based on common Spring Data interfaces.
- Synchronous, reactive, and asynchronous YCQL operations.
Project Dependencies
Spring Data Cassandra projects are bundled with the Apache Cassandra Java driver. To enhance performance, it is recommended to replace this driver with the Yugabyte Java driver for YCQL.
Yugabyte Java Driver YCQL 3.x
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
<version>2.2.12.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- YCQL Driver -->
<dependency>
<groupId>com.yugabyte</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>3.8.0-yb-6</version>
</dependency>
The following shows how to exclude and add dependencies via Gradle:
dependencies {
compile('org.springframework.data:spring-data-cassandra:2.2.12.RELEASE') {
exclude group: "com.datastax.cassandra", name: "cassandra-driver-core"
}
compile('com.yugabyte:cassandra-driver-core:3.8.0-yb-6')
}
Yugabyte Java Driver YCQL 4.x
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
<version>3.0.6.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- YCQL Driver -->
<dependency>
<groupId>com.yugabyte</groupId>
<artifactId>java-driver-core</artifactId>
<version>4.6.0-yb-6</version>
</dependency>
The following shows how to exclude and add dependencies via Gradle:
dependencies {
compile('org.springframework.data:spring-data-cassandra:3.0.6.RELEASE') {
exclude group: "com.datastax.oss", name: "java-driver-core"
}
compile('com.yugabyte:java-driver-core:4.6.0-yb-6')
}
Yugabyte Java driver for YCQL provides support for single hop fetch which enables topology awareness, shard awareness data access using Spring Data Cassandra templates and repositories. In addition, Yugabyte Java drivers for YCQL support distributed transactions in Spring Boot applications using custom query methods.
Sample Spring Boot Project
A sample Spring boot project is available at https://github.com/yugabyte/spring-ycql-demo. The following steps demonstrate how to incrementally build a Spring boot application using Spring Data Cassandra and YCQL.
Use Spring Initializer
-
Navigate to https://start.spring.io to create a new Spring boot project. Select the following dependencies for implementing the Spring boot application:
- Spring Boot Data Cassandra
- Java 8
-
Update the Maven dependencies to use Yugabyte Java driver for YCQL, as follows:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-cassandra</artifactId> <exclusions> <exclusion> <groupId>com.datastax.oss</groupId> <artifactId>java-driver-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.yugabyte</groupId> <artifactId>java-driver-core</artifactId> <version>4.6.0-yb-6</version> </dependency>
-
Configure the Spring boot application to connect to YugabyteDB cluster using the following properties in the
application.properties
file:Property Description spring.data.cassandra.keyspace-name YCQL keyspace spring.data.cassandra.contact-points YugabyteDB T-servers. Comma-separated list of IP addresses or DNS spring.data.cassandra.port YCQL port spring.data.cassandra.local-datacenter Datacenter that is considered "local". Contact points should be from this datacenter.
Set Up Domain Objects and Repositories for YCQL Tables
Create a Customer
object to provide data access to allow Spring boot applications to manage first and last names of customers in a YugabyteDB table. To represent the object-table mapping to this data at the application level, you need to create a Customer
Java class, as follows:
package com.yugabyte.example.ycqldataaccess;
public class Customer {
private long id;
private String firstName, lastName;
public Customer(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
// define getters and setters
// ...
}
Store and Retrieve Data
Spring Data Cassandra provides the CassandraRepositories
interface that removes all the boilerplate code and simplifies definition of CRUD operations against YugabyteDB tables. Most of the YugabyteDB connection handling, exception handling, and general error handling is performed by repositories, leaving you to implement the business logic.
Create the CustomerRepository
interface, as follows:
package com.yugabyte.example.ycqldataaccess;
import java.util.Optional;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CustomerRepository extends CassandraRepository<Customer, String> {
Optional<Customer> findByFirstName(String firstName);
}
The following YCQLDataAccessApplication.java
is a demonstration of a class that can store and retrieve data from YugabyteDB tables using Cassandra repositories:
package com.yugabyte.example.ycqldataaccess;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import com.datastax.oss.driver.api.core.CqlSession;
@SpringBootApplication
@EnableCassandraRepositories(basePackageClasses = Customer.class)
public class YcqlDataAccesssApplication implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger
(YcqlDataAccesssApplication.class);
@Autowired
CustomerRepository customerRepository;
@Autowired
CqlSession ycqlSession;
public static void main(String[] args) {
SpringApplication.run(YcqlDataAccesssApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info("Creating Keyspace");
ycqlSession.execute("CREATE KEYSPACE IF NOT EXISTS demo;");
log.info("Creating table");
ycqlSession.execute("CREATE TABLE demo.customer
(\n" + " id INT PRIMARY KEY,\n" + " firstName text,\n" \+
" lastName text\n" + ") WITH default_time_to_live = 0\n"
\+ " AND transactions = {'enabled': 'true'};");
// Split the array of whole names into an array of first-last names
List<Object[]> splitUpNames = Arrays.asList(
"Tim Bun", "Mike Dean", "Alan Row", "Josh Rambo")
.stream().map(name -> name.split(" ")).collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> {
int id = 1;
log.info(String.format("Inserting customer record for %s %s", name[0], name[1]));
customerRepository.save(new Customer(id, (String) name[0], (String) name[1]));
id++;
});
log.info("Querying for customer records where first_name = 'Josh':");
log.info(customerRepository.findByFirstName("Josh").toString());
}
}
The CustomerRepository
type is autowired in YcqlDataAccesssApplication
which allows the insert of new records into the customer
table using the customerRepository.save()
method.
You can retrieve the data using the customerRepository.findByFirstName()
method which returns records based on the firstName
column. The implementation of the method is supplied by Spring at runtime.
Build an Executable JAR
The next step is to run the Spring application from the command line using Maven, as follows:
./mvnw spring-boot:run
Alternatively, you can build an uber JAR file by using the following command:
./mvnw clean package
Then you run the JAR file using the following command:
java -jar target/ycqldataaccess-1.0.0.jar
Expect the following output:
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Started YcqlDataAccesssApplication in 1.7 seconds (JVM running for 2.139)
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Creating Keyspace
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Creating table
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Inserting customer record for Tim Bun
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Inserting customer record for Mike Dean
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Inserting customer record for Alan Row
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Inserting customer record for Josh Rambo
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Querying for customer records where first_name = 'Josh':
INFO 28010 --- [main] c.y.e.y.YcqlDataAccesssApplication : Optional[Customer[id=1, firstName='Josh', lastName='Rambo']]
Spring Data Reactive Repositories and YCQL
YCQL API is compatible with Spring Data reactive repositories for Apache Cassandra.
Using Spring WebFlux and Spring Data reactive repositories, you can implement fully reactive Spring Microservices with YCQL API.
Project Dependencies
Reactive Spring Data Cassandra projects are bundled with the Apache Cassandra Java driver. To enhance performance, it is recommended to replace this driver with Yugabyte Java driver for YCQL, as follows:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
<exclusions>
<exclusion>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.yugabyte</groupId>
<artifactId>java-driver-core</artifactId>
<version>4.6.0-yb-6</version>
</dependency>
Sample Spring Boot Project for Reactive
A sample Spring boot project is available at https://github.com/yugabyte/spring-reactive-ycql-client. The following steps show how to incrementally build a Spring boot application using Spring Data Cassandra and YCQL.
Use Spring Initializer
-
Navigate to https://start.spring.io to create a new Spring boot project. Select the following dependencies for implementing the Spring boot application:
- Spring Boot Data Cassandra Reactive
- Java 8
-
Update the Maven dependencies to use the Yugabyte Java driver for YCQL, as follows:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId> <exclusions> <exclusion> <groupId>com.datastax.oss</groupId> <artifactId>java-driver-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.yugabyte</groupId> <artifactId>java-driver-core</artifactId> <version>4.6.0-yb-6</version> </dependency>
-
Configure the Spring boot application to connect to YugabyteDB cluster using the following properties in the
application.properties
file:Property Description spring.data.cassandra.keyspace-name YCQL keyspace spring.data.cassandra.contact-points YugabyteDB T-servers. Comma-separated list of IP addresses or DNS spring.data.cassandra.port YCQL port spring.data.cassandra.local-datacenter Datacenter that is considered "local". Contact points should be from this datacenter.
Set Up Domain Objects and Repositories for YugabyteDB Tables
Create a Customer
object to provide data access to allow Spring boot applications to manage first and last names of customers in a YugabyteDB table. To represent the object-table mapping to this data at the application level, you need to create a Customer
Java class, as follows:
package com.yugabyte.example.ycqldataaccess;
public class Customer {
private long id;
private String firstName, lastName;
public Customer(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
// define getters and setters
// ...
}
Store and Retrieve Data
Spring Data Cassandra Reactive provides the ReactiveCassandraRepositories
interface that removes all the boilerplate code and simplifies definition of CRUD operations against YugabyteDB tables. Most of the YugabyteDB connection handling, exception handling, and general error handling is performed by repositories, leaving you to implement the business logic.
Create the CustomerReactiveRepository
interface, as follows:
package com.yugabyte.example.ycqldataaccess;
import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;
import reactor.core.publisher.Mono;
public interface CustomerReactiveRepository extends
ReactiveCassandraRepository<Customer, String> {
Mono<Customer> findByFirstName(String firstName);
}
The following YCQLReactiveDataAccessApplication.java
is a demonstration of a class that can store and retrieve data from YugabyteDB tables using Cassandra repositories:
package com.yugabyte.example.ycqldataaccess;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.cassandra.repository.config.EnableReactiveCassandraRepositories;
import com.datastax.oss.driver.api.core.CqlSession;
@SpringBootApplication
@EnableReactiveCassandraRepositories(
basePackageClasses = CustomerReactiveRepository.class)
public class YcqlReactiveDataAccessApplication implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(YcqlReactiveDataAccessApplication.class);
@Autowired
CustomerReactiveRepository customerReactiveRepository;
@Autowired
CqlSession ycqlSession;
public static void main(String[] args) {
SpringApplication.run(YcqlReactiveDataAccessApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info("Creating table");
ycqlSession.execute("CREATE TABLE IF NOT EXISTS demo.customer (\n" +
" id INT PRIMARY KEY,\n"+ " firstName text,\n" +
" lastName text\n" + ") WITH default_time_to_live = 0\n" +
" AND transactions = {'enabled': 'true'};");
// Split the array of whole names into an array of first/last names
List<Object[]> splitUpNames =
Arrays.asList("Tim Bun", "Mike Dean", "Alan Row", "Josh Rambo")
.stream().map(name -> name.split(" "))
.collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> {
int id = 1;
log.info(String.format("Inserting customer record for %s %s",
name[0], name[1]));
customerReactiveRepository.save(new Customer(id, (String) name[0],
(String) name[1]));
id++;
});
log.info("Querying for customer records where first_name = 'Josh':");
// Using Reactive Repository for Querying the data.
customerReactiveRepository.findByFirstName("Josh")
.doOnNext(customer -> log.info(customer.toString()))
.block();
}
}
Compatibility Matrix
YugabyteDB Java Driver | Spring Data Cassandra |
---|---|
3.8.0-yb-6 | 2.2.12.RELEASE and later |
4.6.0-yb-6 | 3.0.6.RELEASE and later |