Перейти к основному содержимому

Реактивные репозитории Spring Data с MongoDB

· 4 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Введение

В этом руководстве мы увидим, как настроить и реализовать операции с базой данных с помощью реактивного программирования через репозитории Spring Data Reactive с MongoDB.

Мы рассмотрим основные способы использования ReactiveCrud Repository, ReactiveMongoRepository , а также ReactiveMongoTemplate.

Несмотря на то, что в этих реализациях используется реактивное программирование , основное внимание в этом руководстве уделяется не этому.

2. Окружающая среда

Чтобы использовать Reactive MongoDB, нам нужно добавить зависимость в наш pom.xml.

Мы также добавим встроенную MongoDB для тестирования:

<dependencies>
// ...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

3. Конфигурация

Чтобы активировать реактивную поддержку, нам нужно использовать @EnableReactiveMongoRepositories вместе с некоторой настройкой инфраструктуры:

@EnableReactiveMongoRepositories
public class MongoReactiveApplication
extends AbstractReactiveMongoConfiguration {

@Bean
public MongoClient mongoClient() {
return MongoClients.create();
}

@Override
protected String getDatabaseName() {
return "reactive";
}
}

Обратите внимание, что это было бы необходимо, если бы мы использовали автономную установку MongoDB. Но, поскольку в нашем примере мы используем Spring Boot со встроенной MongoDB, указанная выше конфигурация не требуется.

4. Создание документа

Для приведенных ниже примеров давайте создадим класс Account и аннотируем его @Document , чтобы использовать его в операциях с базой данных:

@Document
public class Account {

@Id
private String id;
private String owner;
private Double value;

// getters and setters
}

5. Использование реактивных репозиториев

Мы уже знакомы с моделью программирования репозиториев , с уже определенными методами CRUD, а также с поддержкой некоторых других общих вещей.

Теперь с реактивной моделью мы получаем тот же набор методов и спецификаций, за исключением того, что мы будем работать с результатами и параметрами реактивным способом.

5.1. ReactiveCrudRepository

Мы можем использовать этот репозиторий так же, как блокирующий CrudRepository :

@Repository
public interface AccountCrudRepository
extends ReactiveCrudRepository<Account, String> {

Flux<Account> findAllByValue(String value);
Mono<Account> findFirstByOwner(Mono<String> owner);
}

Мы можем передавать различные типы аргументов, такие как простые ( String ), обернутые ( Optional , Stream ) или реактивные ( Mono , Flux ), как мы видим в методе findFirstByOwner() .

5.2. Реактивмонгорепозиторий

Также есть интерфейс ReactiveMongoRepository , который наследуется от ReactiveCrudRepository и добавляет несколько новых методов запросов:

@Repository
public interface AccountReactiveRepository
extends ReactiveMongoRepository<Account, String> { }

Используя ReactiveMongoRepository , мы можем запросить на примере:

Flux<Account> accountFlux = repository
.findAll(Example.of(new Account(null, "owner", null)));

В результате мы получим каждый Аккаунт , который совпадает с пройденным примером.

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

Mono<Account> accountMono 
= repository.save(new Account(null, "owner", 12.3));
Mono<Account> accountMono2 = repository
.findById("123456");

5.3. RxJava2CrudRepository

С RxJava2CrudRepository у нас то же поведение, что и у ReactiveCrudRepository, но с результатами и типами параметров из RxJava :

@Repository
public interface AccountRxJavaRepository
extends RxJava2CrudRepository<Account, String> {

Observable<Account> findAllByValue(Double value);
Single<Account> findFirstByOwner(Single<String> owner);
}

5.4. Тестирование наших основных операций

Чтобы протестировать наши методы репозитория, мы будем использовать тестового подписчика:

@Test
public void givenValue_whenFindAllByValue_thenFindAccount() {
repository.save(new Account(null, "Bill", 12.3)).block();
Flux<Account> accountFlux = repository.findAllByValue(12.3);

StepVerifier
.create(accountFlux)
.assertNext(account -> {
assertEquals("Bill", account.getOwner());
assertEquals(Double.valueOf(12.3) , account.getValue());
assertNotNull(account.getId());
})
.expectComplete()
.verify();
}

@Test
public void givenOwner_whenFindFirstByOwner_thenFindAccount() {
repository.save(new Account(null, "Bill", 12.3)).block();
Mono<Account> accountMono = repository
.findFirstByOwner(Mono.just("Bill"));

StepVerifier
.create(accountMono)
.assertNext(account -> {
assertEquals("Bill", account.getOwner());
assertEquals(Double.valueOf(12.3) , account.getValue());
assertNotNull(account.getId());
})
.expectComplete()
.verify();
}

@Test
public void givenAccount_whenSave_thenSaveAccount() {
Mono<Account> accountMono = repository.save(new Account(null, "Bill", 12.3));

StepVerifier
.create(accountMono)
.assertNext(account -> assertNotNull(account.getId()))
.expectComplete()
.verify();
}

6. Реактивный шаблон Mongo

Помимо подхода с репозиториями, у нас есть ReactiveMongoTemplate .

Прежде всего, нам нужно зарегистрировать ReactiveMongoTemplate как bean-компонент:

@Configuration
public class ReactiveMongoConfig {

@Autowired
MongoClient mongoClient;

@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(mongoClient, "test");
}
}

И затем мы можем внедрить этот bean-компонент в нашу службу для выполнения операций с базой данных:

@Service
public class AccountTemplateOperations {

@Autowired
ReactiveMongoTemplate template;

public Mono<Account> findById(String id) {
return template.findById(id, Account.class);
}

public Flux<Account> findAll() {
return template.findAll(Account.class);
}
public Mono<Account> save(Mono<Account> account) {
return template.save(account);
}
}

ReactiveMongoTemplate также имеет ряд методов, не относящихся к нашему домену, вы можете ознакомиться с ними в документации .

7. Заключение

В этом кратком руководстве мы рассмотрели использование репозиториев и шаблонов с использованием реактивного программирования с MongoDB с платформой Spring Data Reactive Repositories.

Полный исходный код примеров доступен на GitHub .