1. Обзор
В этом руководстве мы узнаем, как настраивать и реализовывать операции с базой данных реактивным способом на Couchbase с использованием репозиториев данных Spring.
Мы рассмотрим основные способы использования ReactiveCrudRepository
и ReactiveSortingRepository
. Кроме того, мы настроим наше тестовое приложение с помощью AbstractReactiveCouchbaseConfiguration
.
2. Зависимости Maven
Во-первых, добавим необходимые зависимости:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-couchbase-reactive</artifactId>
</dependency>
Зависимость spring-boot-starter-data-couchbase-reactive содержит все, что нам нужно для работы с Couchbase с использованием реактивного API.
Мы также добавим зависимость реактора от ядра для использования Project Reactor API.
3. Конфигурация
Далее давайте определим настройки соединения между Couchbase и нашим приложением.
Давайте начнем с создания класса, который будет содержать наши свойства:
@Configuration
public class CouchbaseProperties {
private List<String> bootstrapHosts;
private String bucketName;
private String bucketPassword;
private int port;
public CouchbaseProperties(
@Value("${spring.couchbase.bootstrap-hosts}") List<String> bootstrapHosts,
@Value("${spring.couchbase.bucket.name}") String bucketName,
@Value("${spring.couchbase.bucket.password}") String bucketPassword,
@Value("${spring.couchbase.port}") int port) {
this.bootstrapHosts = Collections.unmodifiableList(bootstrapHosts);
this.bucketName = bucketName;
this.bucketPassword = bucketPassword;
this.port = port;
}
// getters
}
Чтобы использовать реактивную поддержку, мы должны создать класс конфигурации, который будет расширять AbstractReactiveCouchbaseConfiguration
:
@Configuration
@EnableReactiveCouchbaseRepositories("com.foreach.couchbase.domain.repository")
public class ReactiveCouchbaseConfiguration extends AbstractReactiveCouchbaseConfiguration {
private CouchbaseProperties couchbaseProperties;
public ReactiveCouchbaseConfiguration(CouchbaseProperties couchbaseProperties) {
this.couchbaseProperties = couchbaseProperties;
}
@Override
protected List<String> getBootstrapHosts() {
return couchbaseProperties.getBootstrapHosts();
}
@Override
protected String getBucketName() {
return couchbaseProperties.getBucketName();
}
@Override
protected String getBucketPassword() {
return couchbaseProperties.getBucketPassword();
}
@Override
public CouchbaseEnvironment couchbaseEnvironment() {
return DefaultCouchbaseEnvironment
.builder()
.bootstrapHttpDirectPort(couchbaseProperties.getPort())
.build();
}
}
Кроме того, мы использовали @EnableReactiveCouchbaseRepositories
, чтобы включить наши реактивные репозитории, которые будут находиться в указанном пакете.
Кроме того, мы переопределили CouchbaseEnvironment()
, чтобы передать порт подключения Couchbase.
4. Репозитории
В этом разделе мы узнаем, как создать и использовать реактивный репозиторий. По умолчанию представление «все» поддерживает большинство операций CRUD. Пользовательские методы репозитория поддерживаются N1QL . Если кластер не поддерживает N1QL, во время инициализации будет выдано исключение UnsupportedCouchbaseFeatureException .
Во-первых, давайте создадим класс POJO, с которым будут работать наши репозитории:
@Document
public class Person {
@Id private UUID id;
private String firstName;
//getters and setters
}
4.1. Репозиторий на основе представлений
Теперь мы создадим репозиторий для Person
:
@Repository
@ViewIndexed(designDoc = ViewPersonRepository.DESIGN_DOCUMENT)
public interface ViewPersonRepository extends ReactiveCrudRepository<Person, UUID> {
String DESIGN_DOCUMENT = "person";
}
Репозиторий расширяет интерфейс ReactiveCrudRepository
, чтобы использовать Reactor API для взаимодействия с Couchbase.
Кроме того, мы можем добавить пользовательский метод и использовать аннотацию @View
, чтобы сделать его основанным на представлении:
@View(designDocument = ViewPersonRepository.DESIGN_DOCUMENT)
Flux<Person> findByFirstName(String firstName);
По умолчанию запрос будет искать представление с именем byFirstName
. Если мы хотим предоставить пользовательское имя представления, нам придется использовать аргумент viewName
.
Наконец, давайте создадим простой CRUD-тест с помощью тестового подписчика:
@Test
public void shouldSavePerson_findById_thenDeleteIt() {
final UUID id = UUID.randomUUID();
final Person person = new Person(id, "John");
personRepository
.save(person)
.subscribe();
final Mono<Person> byId = personRepository.findById(id);
StepVerifier
.create(byId)
.expectNextMatches(result -> result
.getId()
.equals(id))
.expectComplete()
.verify();
personRepository
.delete(person)
.subscribe();
}
4.2. N1QL/репозиторий на основе представлений
Теперь мы создадим реактивный репозиторий для Person
, который будет использовать запросы N1QL:
@Repository
@N1qlPrimaryIndexed
public interface N1QLPersonRepository extends ReactiveCrudRepository<Person, UUID> {
Flux<Person> findAllByFirstName(String firstName);
}
Репозиторий расширяет ReactiveCrudRepository
, чтобы также использовать Reactor API. Кроме того, мы добавили собственный метод findAllByFirstName
, который создает запрос, поддерживаемый N1QL.
После этого добавим тест для метода findAllByFirstName
:
@Test
public void shouldFindAll_byLastName() {
final String firstName = "John";
final Person matchingPerson = new Person(UUID.randomUUID(), firstName);
final Person nonMatchingPerson = new Person(UUID.randomUUID(), "NotJohn");
personRepository
.save(matchingPerson)
.subscribe();
personRepository
.save(nonMatchingPerson)
.subscribe();
final Flux<Person> allByFirstName = personRepository.findAllByFirstName(firstName);
StepVerifier
.create(allByFirstName)
.expectNext(matchingPerson)
.verifyComplete();
}
Кроме того, мы создадим репозиторий, который позволит нам извлекать людей с помощью абстракции сортировки:
@Repository
public interface N1QLSortingPersonRepository extends ReactiveSortingRepository<Person, UUID> {
Flux<Person> findAllByFirstName(String firstName, Sort sort);
}
Наконец, давайте напишем тест, чтобы проверить, действительно ли данные отсортированы:
@Test
public void shouldFindAll_sortedByFirstName() {
final Person firstPerson = new Person(UUID.randomUUID(), "John");
final Person secondPerson = new Person(UUID.randomUUID(), "Mikki");
personRepository
.save(firstPerson)
.subscribe();
personRepository
.save(secondPerson)
.subscribe();
final Flux<Person> allByFirstName = personRepository
.findAll(Sort.by(Sort.Direction.DESC, "firstName"));
StepVerifier
.create(allByFirstName)
.expectNextMatches(person -> person
.getFirstName()
.equals(secondPerson.getFirstName()))
.expectNextMatches(person -> person
.getFirstName()
.equals(firstPerson.getFirstName()))
.verifyComplete();
}
5. Вывод
В этой статье мы узнали, как использовать репозитории с помощью реактивного программирования с Couchbase и Spring Data Reactive framework.
Как всегда, код этих примеров доступен на Github .