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.
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.