Весенние данные с реактивной Кассандрой
1. Вступление
В этом руководстве мы узнаем, как использовать функции доступа к реактивным данным в Spring Data Cassandra.
В частности, это третья статья из серии статей Spring Data Cassandra. В этом мы представим базу данных Cassandra с помощью REST API.
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 быть найденаhere.
Теперь мы собираемся предоставить операции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 t, который начинается сReactiveCassandraRepository.. Важно отметить, чтоthis interface enables the support for reactive types:
public interface EmployeeRepository extends ReactiveCassandraRepository {
@AllowFiltering
Flux findByAgeGreaterThan(int age);
}
3.1. Контроллер отдыха для операций CRUD
В целях иллюстрации мы покажем некоторые базовые операцииSELECT с помощью простого контроллера отдыха:
@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);
}
}
Наконец, давайте добавим простойEmployeeService:
@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
}
]
В дальнейшем попробуем найти конкретного сотрудника по его id:
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 для создания неблокирующего приложения.
Как всегда, ознакомьтесь с исходным кодом этого руководстваover on GitHub.