SpringData Reactive Cassandra Repositories | SpringBoot

Reactive programming is about non-blocking applications. With the previous post, we had learned how to use SpringBoot WebFlux. In the tutorial, JavaSampleApproach will explore more about SpringData Reactive Cassandra Repositories with a set of high-level abstraction CRUD methods.

Related posts:
SpringBoot WebFlux Annotation-based RestAPIs
SpringBoot WebFlux Functional RestAPIs
How to start Spring Data Cassandra with SpringBoot
Angular + Spring WebFlux + Spring Data Reactive Cassandra example | Full-Reactive Angular Http Client – Spring Boot RestApi Server

I. Technologies

– Java: 1.8
– Maven: 3.3.9
– Spring Tool Suite: Version 3.9.0.RELEASE
– Spring Boot: 2.0.0.M4
– Spring Boot Starter Webflux
– Spring Boot Starter Data Cassandra Reactive
– Cassandra version: 3.9.0

II. SpringData Reactive Cassandra

Cassandra Reactive SpringData provides repositories with a set of high-level abstraction CRUD methods or templates for working. In the tutorial, we focus on Reactive Cassandra repositories implementation.

For working with ReactiveCassandra, we need dependency:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>

How to work?

We use AbstractReactiveCassandraConfiguration to configure Reactive Cassandra database:


@Configuration
@EnableReactiveCassandraRepositories
public class CassandraReactiveConfig extends AbstractReactiveCassandraConfiguration {

	...

    @Override
    protected String getKeyspaceName() {
        return keySpace;
    }

    @Override
    protected String getContactPoints() {
        return contactPoints;
    }

    ...
}

We use ReactiveCrudRepository to define reactive-repositories:


...

public interface ReactiveCustomerRepository extends ReactiveCrudRepository {
	
	@Query("SELECT * FROM customer WHERE firstname = ?0 and lastname  = ?1 ALLOW FILTERING")
	Mono findByFirstnameAndLastname(String firstname, String lastname);
	
	Flux findByAge(Mono age);
}
We can pass parameters to query methods as plain (e.g. String), wrapped (Optional, Stream) or reactive wrapped arguments (Mono, Flux).
When using reactive wrappers (Mono, Flux) parameters, query creation and execution will be deferred until having an actual subscription.

We can combine Spring Web Reactive and SpringData Reactive in a reactive processing way:


...
 
@GetMapping
public Flux getAll() {
	
	// retrieve all
	return reactiveCustomerRepo.findAll();
}
 
@GetMapping("/{id}")
public Mono getCustomerById(@PathVariable Long id) {
	
	// find by id
	return reactiveCustomerRepo.findById(id);
}
 
...

III. Practice

We create a SpringBoot project as below:

springdata reactive cassandra repositories - project structure

Step to do:
– Create SpringBoot project
– Create data model
– Configure Cassandra Reactive
– Implement Reactive repository
– Implement WebFlux RestAPIs
– Implement initial client
– Run and check results

1. Create SpringBoot project

Using SpringToolSuite to create SpringBoot project, with needed dependencies:

springdata reactive cassandra repositories - add depedencies

Check pom.xml file after finishing:

...

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

...

2. Create data model


package com.javasampleapproach.cassandrareactive.model;

import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;

@Table("customer")
public class Customer {
	
	@PrimaryKey
	private Long id;
	private String firstname;
	private String lastname;
	private int age;
	
	public Customer(){}
	
	public Customer(Long id, String firstname, String lastname, int age){
		this.id = id;
		this.firstname = firstname;
		this.lastname = lastname;
		this.age = age;
	}
	
	public void setId(Long id){
		this.id = id;
	}
	
	public Long getId(){
		return this.id;
	}
	
	public void setFirstname(String firstname){
		this.firstname = firstname;
	}
	
	public String getFirstname(){
		return this.firstname;
	}
	
	public void setLastname(String lastname){
		this.lastname = lastname;
	}
	
	public String getLastname(){
		return this.lastname;
	}
	
	public void setAge(int age){
		this.age = age;
	}
	
	public int getAge(){
		return this.age;
	}
	
	@Override
	public String toString() {
		return String.format("Customer[id=%d, firstName='%s', lastName='%s', age=%d]", this.id,
				this.firstname, this.lastname, this.age);
	}
}

3. Configure Cassandra Reactive


package com.javasampleapproach.cassandrareactive.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractReactiveCassandraConfiguration;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.repository.config.EnableReactiveCassandraRepositories;

@Configuration
@EnableReactiveCassandraRepositories
public class CassandraReactiveConfig extends AbstractReactiveCassandraConfiguration {

    @Value("${jsa.cassandra.keyspace-name}")
    String keySpace;

    @Value("${jsa.cassandra.contact-points}")
    String contactPoints;

    @Override
    protected String getKeyspaceName() {
        return keySpace;
    }

    @Override
    protected String getContactPoints() {
        return contactPoints;
    }

    @Override
    public SchemaAction getSchemaAction() {
        return SchemaAction.CREATE;
    }
}

4. Implement Reactive repository


package com.javasampleapproach.cassandrareactive.repo;

import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

import com.javasampleapproach.cassandrareactive.model.Customer;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface ReactiveCustomerRepository extends ReactiveCrudRepository {
	
	@Query("SELECT * FROM customer WHERE firstname = ?0 and lastname  = ?1 ALLOW FILTERING")
	Mono findByFirstnameAndLastname(String firstname, String lastname);
	
	Flux findByAge(Mono age);
}

5. Implement WebFlux RestAPIs


package com.javasampleapproach.cassandrareactive.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.javasampleapproach.cassandrareactive.model.Customer;
import com.javasampleapproach.cassandrareactive.repo.ReactiveCustomerRepository;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping(value="/api/customer")
public class RestControllerAPIs {
	
	@Autowired
	ReactiveCustomerRepository reactiveCustomerRepo;

	@GetMapping
    public Flux getAll() {
		
		// retrieve all
    	return reactiveCustomerRepo.findAll();
    }
	
	@GetMapping("/{id}")
    public Mono getCustomerById(@PathVariable Long id) {
		
		// find by id
		return reactiveCustomerRepo.findById(id);
    }
	
	@GetMapping("/findbynames")
	public Mono findByFirstnameAndLastname(@RequestParam String firstname, @RequestParam String lastname){
		
		// find on MongoDB
		return reactiveCustomerRepo.findByFirstnameAndLastname(firstname, lastname);
	}
	
	@GetMapping("/findbyage")
	public Flux findByAge(@RequestParam int age){
		
		// find on MongoDB
		Flux customers = reactiveCustomerRepo.findByAge(Mono.just(age));
		
		// transform data
		return customers.map(it -> " - " + it.getFirstname() + " " + it.getLastname() + "
"); } }

6. Implement initial client


package com.javasampleapproach.cassandrareactive;

import java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.javasampleapproach.cassandrareactive.model.Customer;
import com.javasampleapproach.cassandrareactive.repo.ReactiveCustomerRepository;


@SpringBootApplication
public class SpringCassandraDbReactiveRepositoriesApplication implements CommandLineRunner{
	
	@Autowired
	ReactiveCustomerRepository reactiveCustomerRepo;
	
	public static void main(String[] args) {
		SpringApplication.run(SpringCassandraDbReactiveRepositoriesApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
	
		// save list Customers
		reactiveCustomerRepo.saveAll(Arrays.asList(new Customer(new Long(1), "Walter", "White", 29),
										  new Customer(new Long(2), "Skyler", "White", 24),
										  new Customer(new Long(3), "Saul", "Goodman", 27),
										  new Customer(new Long(4), "Jesse", "Pinkman", 24)
										)
									).subscribe(System.out::println);
		
	}
}

7. Run and check results

7.1 Set up Apache Cassandra

Download Apache Cassandra:
– For Linux, go to the site Downloading Cassandra.
– For Windows, go to the site DataStax Distribution.

In the tutorial, environment: {Windows 10, Apache Cassandra – v3.9.0} to demo. After downloading, we have a file: datastax-ddc-64bit-3.9.0.msi. Now just double click on datastax-ddc-64bit-3.9.0.msi then do step by step to install.

Now start Cassandra server:

springdata reactive cassandra repositories - start cassandra server

Open Cassandra CQL Shell to create Cassandra table:

– Create Cassandra keyspace with name javasampleapproach:


create keyspace javasampleapproach with replication={'class':'SimpleStrategy', 'replication_factor':1};

– Create customer table for javasampleapproach keyspace:


use javasampleapproach;
 
CREATE TABLE customer(
   id int PRIMARY KEY,
   firstname text,
   lastname text,
   age int
);

– Create an index on age column:


CREATE INDEX ON javasampleapproach.customer (age);

springdata reactive cassandra repositories - create javasampleapproach keyspace

7.2 Run and check results

– Build & Run the project with SpringBoot App mode: {mvn clean install, mvn spring-boot:run}.

-> Check Cassandra customer table:

springdata reactive cassandra repositories - select all

– Make a request to get all customers http://localhost:8080/api/customer

springdata reactive cassandra repositories - request select all

– Make a request to get a customer by id http://localhost:8080/api/customer/1

springdata reactive cassandra repositories - request select a customer by id

– Make a request to get list full name of customers by age http://localhost:8080/api/customer/findbyage?age=24

springdata reactive cassandra repositories - request find by age

– Make a request to get a Customer by full name http://localhost:8080/api/customer/findbynames?firstname=Skyler&lastname=White

springdata reactive cassandra repositories - find by name

IV. Sourcecode

SpringCassandraDbReactiveRepositories



By grokonez | September 25, 2017.

Last updated on May 3, 2021.



Related Posts


Got Something To Say:

Your email address will not be published. Required fields are marked *

*