Données de printemps avec Cassandra réactive

Données de printemps avec Cassandra réactif

1. introduction

Dans ce didacticiel, nous allons apprendre à utiliser les fonctionnalités d’accès réactif aux données de Spring Data Cassandra.

En particulier, il s'agit du troisième article de la série d'articles Spring Data Cassandra. Dans celui-ci, nous exposerons une base de données Cassandra à l'aide d'une API REST.

Nous pouvons en savoir plus sur Spring Data Cassandra dans les articlesfirst etsecond de la série.

2. Dépendances Maven

En fait, Spring Data Cassandra prend en charge les types réactifs Project Reactor et RxJava. Pour le démontrer, nous utiliserons les types réactifsFlux etMono in du réacteur du projet dans ce didacticiel.

Pour commencer, ajoutons les dépendances nécessaires à notre didacticiel:


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


    io.projectreactor
    reactor-core

La dernière version de l'analysespring-data-cassandra est trouvéehere.

Nous allons maintenant exposer les opérationsSELECT de la base de données via une API REST. Ajoutons donc également la dépendance pourRestController:


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

3. Implémentation de notre application

Puisque nous allons conserver les données, définissons d'abord notre objet entité:

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

Ensuite, il est temps de créer unEmployeeRepository t qui s'étend à partir deReactiveCassandraRepository. Il est important de noter quethis interface enables the support for reactive types:

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

3.1. Contrôleur de repos pour les opérations CRUD

À des fins d'illustration, nous allons exposer quelques opérations de base deSELECT à l'aide d'un simple contrôleur de repos:

@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);
    }
}

Enfin, ajoutons un simpleEmployeeService:

@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. Configuration de la base de données

Ensuite, spécifions l'espace de clés et le port à utiliser pour se connecter à Cassandra dansapplication.properties:

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

4. Test des points de terminaison

Enfin, il est temps de tester nos points de terminaison d'API.

4.1. Test manuel

Pour commencer, récupérons les enregistrements des employés dans la base de données:

curl localhost:8080/employee/list

En conséquence, nous obtenons tous les employés:

[
    {
        "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
    }
]

Continuons, essayons de trouver un employé spécifique par son identifiant:

curl localhost:8080/employee/643

En conséquence, nous obtenons M. Mike Lauren de retour:

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

Enfin, voyons si notre filtre d'âge fonctionne:

curl localhost:8080/employee/filterByAge/35

Et comme prévu, nous avons tous les employés dont l’âge est supérieur à 35 ans:

[
    {
        "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. Test d'intégration

De plus, testons la même fonctionnalité en écrivant un scénario de test:

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

En résumé, nous avons appris à utiliser des types réactifs en utilisant Spring Data Cassandra pour créer une application non bloquante.

Comme toujours, consultez le code source de ce tutorielover on GitHub.