Рубрики
Без рубрики

Весенние данные с реактивной Кассандрой

Узнайте о подключении к Cassandra с помощью реактивного стека данных Spring.

Автор оригинала: baeldung.

1. Введение

В этом уроке мы узнаем, как использовать функции реактивного доступа к данным Spring Data Cassandra.

В частности, это третья статья из серии статей Spring Data Cassandra. В этой статье мы представим базу данных Cassandra с использованием REST API.

Подробнее о Spring Data Cassandra мы можем прочитать в первой и второй статьях серии.

2. Зависимости Maven

На самом деле, Spring Data Cassandra поддерживает реактивные типы Project Reactor и RxJava. Для демонстрации мы будем использовать реактивные типы реактора проекта Flux и Mono в этом уроке.

Для начала давайте добавим зависимости, необходимые для нашего урока:


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


    io.projectreactor
    reactor-core

Последнюю версию spring-data-cassandra можно найти здесь .

Теперь мы собираемся предоставить SELECT операции из базы данных через REST API. Итак, давайте также добавим зависимость для RestController :


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

3. Реализация Вашего Приложения

Поскольку мы будем сохранять данные, давайте сначала определим наш объект сущности :

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

Затем пришло время создать EmployeeRepository , который простирается от ReactiveCassandraRepository. Важно отметить, что этот интерфейс обеспечивает поддержку реактивного типа s:

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

3.1. Контроллер Rest для операций CRUD

Для иллюстрации мы представим некоторые основные операции SELECT с помощью простого контроллера Rest:

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

Наконец, давайте добавим простую Службу сотрудников :

@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. Конфигурация базы данных

Затем давайте укажем пространство ключей и порт для подключения к Cassandra в application.properties :

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

4. Тестирование конечных точек

Наконец, пришло время протестировать наши конечные точки API.

4.1. Ручное тестирование

Для начала давайте извлекем записи сотрудников из базы данных:

curl localhost:8080/employee/list

В результате мы получаем всех сотрудников:

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

Двигаясь дальше, давайте попробуем найти конкретного сотрудника по его идентификатору:

curl localhost:8080/employee/643

В результате мы возвращаем мистера Майка Лорена:

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

Наконец, давайте посмотрим, работает ли наш возрастной фильтр:

curl localhost:8080/employee/filterByAge/35

И, как и ожидалось, мы получаем всех сотрудников, чей возраст превышает 35 лет:

[
    {
        "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. Интеграционное тестирование

Кроме того, давайте протестируем ту же функциональность, написав тестовый случай:

@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. Заключение

Таким образом, мы узнали, как использовать реактивные типы с помощью Spring Data Cassandra для создания неблокирующего приложения.

Как всегда, проверьте исходный код этого учебника на GitHub .