1. Wprowadzenie
W tym samouczku zobaczymy, jak skonfigurować i zaimplementować operacje na bazie danych za pomocą programowania reaktywnego za pomocą repozytoriów reaktywnych Spring Data z MongoDB.
Omówimy podstawowe zastosowania ReactiveCrud Repozytorium, Reaktywne MongoRepository , a także ReactiveMongoTemplate.
Mimo że te implementacje wykorzystują programowanie reaktywne, nie jest to główny cel tego samouczka.
2. Środowisko
Aby używać Reactive MongoDB, musimy dodać zależność do naszego pom.xml.
Dodamy również do testów osadzoną bazę danych 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. Konfiguracja
Aby aktywować wsparcie reaktywne, musimy użyć @EnableReactiveMongoRepositories wraz z konfiguracją infrastruktury:
@EnableReactiveMongoRepositories
public class MongoReactiveApplication
extends AbstractReactiveMongoConfiguration {
@Bean
public MongoClient mongoClient() {
return MongoClients.create();
}
@Override
protected String getDatabaseName() {
return "reactive";
}
}
Zauważ, że powyższe byłoby konieczne, gdybyśmy korzystali z samodzielnej instalacji MongoDB. Ale ponieważ w naszym przykładzie używamy Spring Boot z osadzoną bazą danych MongoDB, powyższa konfiguracja nie jest konieczna.
4. Tworzenie Dokumentu
W poniższych przykładach utwórzmy konto klasę i dodaj do niej adnotację @Document aby użyć go w operacjach na bazie danych:
@Document
public class Account {
@Id
private String id;
private String owner;
private Double value;
// getters and setters
}
5. Korzystanie z repozytoriów reaktywnych
Znamy już model programowania repozytoriów, ze zdefiniowanymi już metodami CRUD oraz obsługą kilku innych typowych rzeczy.
Teraz dzięki modelowi reaktywnemu otrzymujemy ten sam zestaw metod i specyfikacji, z wyjątkiem tego, że z wynikami i parametrami zajmiemy się w sposób reaktywny.
5.1. ReactiveCrudRepository
Możemy używać tego repozytorium w taki sam sposób, jak blokującego CrudRepository :
@Repository
public interface AccountCrudRepository
extends ReactiveCrudRepository<Account, String> {
Flux<Account> findAllByValue(String value);
Mono<Account> findFirstByOwner(Mono<String> owner);
}
Możemy przekazywać różne typy argumentów, takie jak zwykły (Ciąg ), opakowane (Opcjonalne , Strumień ) lub reaktywny (Mono , strumień ), jak widzimy w findFirstByOwner() metoda.
5.2. Reaktywne MongoRepository
Jest też ReactiveMongoRepository interfejs, który dziedziczy z ReactiveCrudRepository i dodaje kilka nowych metod zapytań:
@Repository
public interface AccountReactiveRepository
extends ReactiveMongoRepository<Account, String> { }
Korzystanie z ReactiveMongoRepository , możemy zapytać na przykład:
Flux<Account> accountFlux = repository
.findAll(Example.of(new Account(null, "owner", null)));
W rezultacie otrzymamy każde konto to jest to samo, co podany przykład.
Po utworzeniu naszych repozytoriów mają już zdefiniowane metody wykonywania niektórych operacji na bazach danych, których nie musimy wdrażać:
Mono<Account> accountMono
= repository.save(new Account(null, "owner", 12.3));
Mono<Account> accountMono2 = repository
.findById("123456");
5.3. RxJava2CrudRepository
Dzięki RxJava2CrudRepository zachowujemy się tak samo jak ReactiveCrudRepository, ale z wynikami i typami parametrów z RxJava :
@Repository
public interface AccountRxJavaRepository
extends RxJava2CrudRepository<Account, String> {
Observable<Account> findAllByValue(Double value);
Single<Account> findFirstByOwner(Single<String> owner);
}
5.4. Testowanie naszych podstawowych operacji
Aby przetestować nasze metody repozytorium, użyjemy subskrybenta testowego:
@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. Reaktywny szablon Mongo
Oprócz podejścia opartego na repozytoriach, mamy ReactiveMongoTemplate .
Przede wszystkim musimy się zarejestrować ReactiveMongoTemplate jako fasola:
@Configuration
public class ReactiveMongoConfig {
@Autowired
MongoClient mongoClient;
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(mongoClient, "test");
}
}
A następnie możemy wstrzyknąć ten bean do naszej usługi, aby wykonać operacje na bazie danych:
@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);
}
}
Reaktywny szablon Mongo ma również wiele metod, które nie są związane z domeną, którą posiadamy, możesz je sprawdzić w dokumentacji.