Dados da Primavera com Cassandra Reativa

Dados da Primavera com Cassandra Reativa

1. Introdução

Neste tutorial, aprenderemos como usar os recursos de acesso a dados reativos do Spring Data Cassandra.

Particularmente, este é o terceiro artigo da série de artigos do Spring Data Cassandra. Neste, vamos expor um banco de dados Cassandra usando uma API REST.

Podemos ler mais sobre Spring Data Cassandra nos artigosfirstesecond da série.

2. Dependências do Maven

Por uma questão de fato, o Spring Data Cassandra suporta os tipos reativos do Project Reactor e RxJava. Para demonstrar, usaremos os tipos reativos do reator do projetoFluxeMono neste tutorial.

Para começar, vamos adicionar as dependências necessárias para nosso tutorial:


    org.springframework.data
    spring-data-cassandra
    2.1.2.RELEASE


    io.projectreactor
    reactor-core

A versão mais recente da varreduraspring-data-cassandra pode ser encontradahere.

Agora, vamos exporSELECT operações do banco de dados por meio de uma API REST. Então, vamos adicionar a dependência paraRestController também:


    org.springframework.boot
    spring-boot-starter-web

3. Implementando nosso aplicativo

Uma vez que iremos persistir dados, vamos primeiro definir nosso objeto de entidade:

@Table
public class Employee {
    @PrimaryKey
    private int id;
    private String name;
    private String address;
    private String email;
    private int age;
}

Em seguida, é hora de criar umEmployeeRepository tque se estende deReactiveCassandraRepository.. É importante observar quethis interface enables the support for reactive types:

public interface EmployeeRepository extends ReactiveCassandraRepository {
    @AllowFiltering
    Flux findByAgeGreaterThan(int age);
}

3.1. Controlador de descanso para operações CRUD

Para fins de ilustração, vamos expor algumas operações básicas deSELECT usando um controlador de descanso simples:

@RestController
@RequestMapping("employee")
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @PostConstruct
    public void saveEmployees() {
        List employees = new ArrayList<>();
        employees.add(new Employee(123, "John Doe", "Delaware", "[email protected]", 31));
        employees.add(new Employee(324, "Adam Smith", "North Carolina", "[email protected]", 43));
        employees.add(new Employee(355, "Kevin Dunner", "Virginia", "[email protected]", 24));
        employees.add(new Employee(643, "Mike Lauren", "New York", "[email protected]", 41));
        employeeService.initializeEmployees(employees);
    }

    @GetMapping("/list")
    public Flux getAllEmployees() {
        Flux employees = employeeService.getAllEmployees();
        return employees;
    }

    @GetMapping("/{id}")
    public Mono getEmployeeById(@PathVariable int id) {
        return employeeService.getEmployeeById(id);
    }

    @GetMapping("/filterByAge/{age}")
    public Flux getEmployeesFilterByAge(@PathVariable int age) {
        return employeeService.getEmployeesFilterByAge(age);
    }
}

Finalmente, vamos adicionar umEmployeeService simples:

@Service
public class EmployeeService {

    @Autowired
    EmployeeRepository employeeRepository;

    public void initializeEmployees(List employees) {
        Flux savedEmployees = employeeRepository.saveAll(employees);
        savedEmployees.subscribe();
    }

    public Flux getAllEmployees() {
        Flux employees =  employeeRepository.findAll();
        return employees;
    }

    public Flux getEmployeesFilterByAge(int age) {
        return employeeRepository.findByAgeGreaterThan(age);
    }

    public Mono getEmployeeById(int id) {
        return employeeRepository.findById(id);
    }
}

3.2. Configuração do banco de dados

Então, vamos especificar o keyspace e a porta a serem usados ​​para conexão com o Cassandra emapplication.properties:

spring.data.cassandra.keyspace-name=practice
spring.data.cassandra.port=9042

4. Testando os Endpoints

Finalmente, é hora de testar nossos endpoints da API.

4.1. Teste manual

Para começar, vamos buscar os registros do funcionário no banco de dados:

curl localhost:8080/employee/list

Como resultado, obtemos todos os funcionários:

[
    {
        "id": 324,
        "name": "Adam Smith",
        "address": "North Carolina",
        "email": "[email protected]",
        "age": 43
    },
    {
        "id": 123,
        "name": "John Doe",
        "address": "Delaware",
        "email": "[email protected]",
        "age": 31
    },
    {
        "id": 355,
        "name": "Kevin Dunner",
        "address": "Virginia",
        "email": "[email protected]",
        "age": 24
    },
    {
        "id": 643,
        "name": "Mike Lauren",
        "address": "New York",
        "email": "[email protected]",
       "age": 41
    }
]

Continuando, vamos tentar encontrar um funcionário específico por sua id:

curl localhost:8080/employee/643

Como resultado, temos o Sr. Mike Lauren de volta:

{
    "id": 643,
    "name": "Mike Lauren",
    "address": "New York",
    "email": "[email protected]",
    "age": 41
}

Finalmente, vamos ver se nosso filtro de idade funciona:

curl localhost:8080/employee/filterByAge/35

E, como esperado, temos todos os funcionários com idade superior a 35 anos:

[
    {
        "id": 324,
        "name": "Adam Smith",
        "address": "North Carolina",
        "email": "[email protected]",
        "age": 43
    },
    {
        "id": 643,
        "name": "Mike Lauren",
        "address": "New York",
        "email": "[email protected]",
        "age": 41
    }
]

4.2. Teste de integração

Além disso, vamos testar a mesma funcionalidade escrevendo um caso de teste:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ReactiveEmployeeRepositoryIntegrationTest {

    @Autowired
    EmployeeRepository repository;

    @Before
    public void setUp() {
        Flux deleteAndInsert = repository.deleteAll()
          .thenMany(repository.saveAll(Flux.just(
            new Employee(111, "John Doe", "Delaware", "[email protected]", 31),
            new Employee(222, "Adam Smith", "North Carolina", "[email protected]", 43),
            new Employee(333, "Kevin Dunner", "Virginia", "[email protected]", 24),
            new Employee(444, "Mike Lauren", "New York", "[email protected]", 41))));

        StepVerifier
          .create(deleteAndInsert)
          .expectNextCount(4)
          .verifyComplete();
    }

    @Test
    public void givenRecordsAreInserted_whenDbIsQueried_thenShouldIncludeNewRecords() {
        Mono saveAndCount = repository.count()
          .doOnNext(System.out::println)
          .thenMany(repository
            .saveAll(Flux.just(
            new Employee(325, "Kim Jones", "Florida", "[email protected]", 42),
            new Employee(654, "Tom Moody", "New Hampshire", "[email protected]", 44))))
          .last()
          .flatMap(v -> repository.count())
          .doOnNext(System.out::println);

        StepVerifier
          .create(saveAndCount)
          .expectNext(6L)
          .verifyComplete();
    }

    @Test
    public void givenAgeForFilter_whenDbIsQueried_thenShouldReturnFilteredRecords() {
        StepVerifier
          .create(repository.findByAgeGreaterThan(35))
          .expectNextCount(2)
          .verifyComplete();
    }
}

5. Conclusão

Em resumo, aprendemos como usar tipos reativos usando o Spring Data Cassandra para criar um aplicativo sem bloqueio.

Como sempre, verifique o código-fonte deste tutorialover on GitHub.