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() { Listemployees = 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(Listemployees) { 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() { FluxdeleteAndInsert = 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 .