Реактивные хранилища данных Spring с MongoDB
1. Вступление
В этом руководстве мы увидим, как настроить и реализовать операции с базой данных с помощью реактивного программирования через Spring Data Reactive Repositories с MongoDB.
Мы рассмотрим основные способы использованияReactiveCrudRepository,ReactiveMongoRepository,, а такжеReactiveMongoTemplate.
Несмотря на то, что в этих реализациях используетсяreactive programming, это не является основной темой данного руководства.
2. Среда
Чтобы использовать Reactive MongoDB, нам нужно добавить зависимость к нашемуpom.xml.
Мы также добавим встроенный MongoDB для тестирования:
// ...
org.springframework.boot
spring-boot-starter-data-mongodb-reactive
de.flapdoodle.embed
de.flapdoodle.embed.mongo
test
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. СозданиеDocument
Для примеров ниже давайте создадим классAccount и аннотируем его@Document, чтобы использовать его в операциях с базой данных:
@Document
public class Account {
@Id
private String id;
private String owner;
private Double value;
// getters and setters
}
5. Использование реактивных репозиториев
Мы уже знакомы сrepositories programming model, с уже определенными методами CRUD, а также с поддержкой некоторых других общих вещей.
Теперь с реактивной моделью мы получаем тот же набор методов и спецификаций, за исключением того, что мы будем реагировать на результаты и параметры.
5.1. ReactiveCrudRepositoryс
Мы можем использовать этот репозиторий так же, как блокирующийCrudRepository:
@Repository
public interface AccountCrudRepository
extends ReactiveCrudRepository {
Flux findAllByValue(String value);
Mono findFirstByOwner(Mono owner);
}
Мы можем передавать различные типы аргументов, такие как простой (String), завернутый (Optional,Stream) или реактивный (Mono,Flux), как мы можем см. в методеfindFirstByOwner().
5.2. ReactiveMongoRepositoryс
Также есть интерфейсReactiveMongoRepository, который наследуется отReactiveCrudRepository и добавляет несколько новых методов запроса:
@Repository
public interface AccountReactiveRepository
extends ReactiveMongoRepository { }
ИспользуяReactiveMongoRepository, мы можем запросить на примере:
Flux accountFlux = repository
.findAll(Example.of(new Account(null, "owner", null)));
В результате мы получим каждыеAccount, которые совпадают с прошедшим примером.
После создания наших репозиториев в них уже определены методы для выполнения некоторых операций с базой данных, которые нам не нужно реализовывать:
Mono accountMono
= repository.save(new Account(null, "owner", 12.3));
Mono accountMono2 = repository
.findById("123456");
5.3. RxJava2CrudRepositoryс
СRxJava2CrudRepository, мы имеем то же поведение, что иReactiveCrudRepository,, но с результатами и типами параметров изRxJava:
@Repository
public interface AccountRxJavaRepository
extends RxJava2CrudRepository {
Observable findAllByValue(Double value);
Single findFirstByOwner(Single owner);
}
5.4. Тестирование наших основных операций
Чтобы протестировать наши методы репозитория, мы будем использовать тестового подписчика:
@Test
public void givenValue_whenFindAllByValue_thenFindAccount() {
repository.save(new Account(null, "Bill", 12.3)).block();
Flux 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 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 accountMono = repository.save(new Account(null, "Bill", 12.3));
StepVerifier
.create(accountMono)
.assertNext(account -> assertNotNull(account.getId()))
.expectComplete()
.verify();
}
6. ReactiveMongoTemplateс
Помимо подхода к репозиториям, у нас есть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 findById(String id) {
return template.findById(id, Account.class);
}
public Flux findAll() {
return template.findAll(Account.class);
}
public Mono save(Mono account) {
return template.save(account);
}
}
ReactiveMongoTemplate также имеет ряд методов, которые не относятся к нашему домену, вы можете проверить их вdocumentation.
7. Заключение
В этом кратком руководстве мы рассмотрели использование репозиториев и шаблонов с использованием реактивного программирования с использованием MongoDB и среды Spring Data Reactive Repositories.
Полный исходный код примеров доступенover on GitHub.