1. Introduktion
I denne øvelse skal vi se, hvordan du konfigurerer og implementerer databaseoperationer ved hjælp af Reactive Programming gennem Spring Data Reactive Repositories med MongoDB.
Vi gennemgår de grundlæggende anvendelser af ReactiveCrud Repository, ReactiveMongoRepository , samt ReactiveMongoTemplate.
Selvom disse implementeringer bruger reaktiv programmering, er det ikke det primære fokus i denne øvelse.
2. Miljø
For at bruge Reactive MongoDB skal vi tilføje afhængigheden til vores pom.xml.
Vi tilføjer også en indlejret MongoDB til test:
<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. Konfiguration
For at aktivere den reaktive support skal vi bruge @EnableReactiveMongoRepositories sammen med nogle infrastrukturopsætninger:
@EnableReactiveMongoRepositories
public class MongoReactiveApplication
extends AbstractReactiveMongoConfiguration {
@Bean
public MongoClient mongoClient() {
return MongoClients.create();
}
@Override
protected String getDatabaseName() {
return "reactive";
}
}
Bemærk, at ovenstående ville være nødvendigt, hvis vi brugte den selvstændige MongoDB-installation. Men da vi bruger Spring Boot med embedded MongoDB i vores eksempel, er ovenstående konfiguration ikke nødvendig.
4. Oprettelse af et dokument
For eksemplerne nedenunder, lad os oprette en konto klasse og annoter den med @Document for at bruge det i databasehandlingerne:
@Document
public class Account {
@Id
private String id;
private String owner;
private Double value;
// getters and setters
}
5. Brug af reaktive lagre
Vi er allerede bekendt med repositories-programmeringsmodellen, med CRUD-metoderne allerede defineret plus understøttelse af nogle andre almindelige ting også.
Nu med den reaktive model får vi det samme sæt metoder og specifikationer, bortset fra at vi vil håndtere resultaterne og parametrene på en reaktiv måde.
5.1. ReactiveCrudRepository
Vi kan bruge dette lager på samme måde som det blokerende CrudRepository :
@Repository
public interface AccountCrudRepository
extends ReactiveCrudRepository<Account, String> {
Flux<Account> findAllByValue(String value);
Mono<Account> findFirstByOwner(Mono<String> owner);
}
Vi kan sende forskellige typer argumenter som almindelig (String ), pakket ind (Valgfrit , Stream ), eller reaktiv (Mono , Flux ), som vi kan se i findFirstByOwner() metode.
5.2. ReactiveMongoRepository
Der er også ReactiveMongoRepository grænseflade, som arver fra ReactiveCrudRepository og tilføjer nogle nye forespørgselsmetoder:
@Repository
public interface AccountReactiveRepository
extends ReactiveMongoRepository<Account, String> { }
Brug af ReactiveMongoRepository , kan vi forespørge ved eksempel:
Flux<Account> accountFlux = repository
.findAll(Example.of(new Account(null, "owner", null)));
Som et resultat heraf får vi hver konto det er det samme som eksemplet.
Med vores arkiver oprettet, har de allerede defineret metoder til at udføre nogle databaseoperationer, som vi ikke behøver at implementere:
Mono<Account> accountMono
= repository.save(new Account(null, "owner", 12.3));
Mono<Account> accountMono2 = repository
.findById("123456");
5.3. RxJava2CrudRepository
Med RxJava2CrudRepository, vi har samme adfærd som ReactiveCrudRepository, men med resultaterne og parametertyperne fra RxJava :
@Repository
public interface AccountRxJavaRepository
extends RxJava2CrudRepository<Account, String> {
Observable<Account> findAllByValue(Double value);
Single<Account> findFirstByOwner(Single<String> owner);
}
5.4. Test af vores grundlæggende operationer
For at teste vores lagermetoder bruger vi testabonnenten:
@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. ReactiveMongoTemplate
Udover repositories-tilgangen har viReactiveMongoTemplate .
Først og fremmest skal vi registrere ReactiveMongoTemplate som en bønne:
@Configuration
public class ReactiveMongoConfig {
@Autowired
MongoClient mongoClient;
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(mongoClient, "test");
}
}
Og så kan vi injicere denne bønne i vores tjeneste for at udføre databasehandlingerne:
@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 har også en række metoder som ikke relaterer til det domæne vi har, du kan tjekke dem ud i dokumentationen.