Spring Data Reactive Repositories with MongoDB – 与MongoDB的Spring Data Reactive Repositories合作

最后修改: 2018年 6月 3日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we’re going to see how to configure and implement database operations using Reactive Programming through Spring Data Reactive Repositories with MongoDB.

在本教程中,我们将看到如何通过Spring Data Reactive Repositories与MongoDB一起使用反应式编程配置和实现数据库操作。

We’ll go over the basic usages of ReactiveCrudRepository, ReactiveMongoRepository, as well as ReactiveMongoTemplate.

我们将讨论ReactiveCrudRepository、ReactiveMongoRepository以及ReactiveMongoTemplate的基本使用方法。

Even though these implementations use reactive programming, that isn’t the primary focus of this tutorial.

尽管这些实现使用了反应式编程,但这并不是本教程的主要重点。

2. Environment

2.环境

In order to use Reactive MongoDB, we need to add the dependency to our pom.xml.

为了使用Reactive MongoDB,我们需要在我们的pom.xml.中添加依赖性。

We’ll also add an embedded MongoDB for testing:

我们还将添加一个嵌入式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. Configuration

3.配置

In order to activate the reactive support, we need to use the @EnableReactiveMongoRepositories alongside with some infrastructure setup:

为了激活反应式支持,我们需要使用@EnableReactiveMongoRepositories以及一些基础设施设置。

@EnableReactiveMongoRepositories
public class MongoReactiveApplication
  extends AbstractReactiveMongoConfiguration {

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

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

Note that the above would be necessary if we were using the standalone MongoDB installation. But, as we’re using Spring Boot with embedded MongoDB in our example, the above configuration is not necessary.

注意,如果我们使用的是独立的MongoDB安装,上述内容是必要的。但是,由于我们的例子中使用的是带有嵌入式MongoDB的Spring Boot,所以上述配置是没有必要的。

4. Creating a Document

4.创建一个文件

For the examples below, let’s create an Account class and annotate it with @Document to use it in the database operations:

对于下面的例子,让我们创建一个Account类,并用@Document对其进行注释,以便在数据库操作中使用它。

@Document
public class Account {
 
    @Id
    private String id;
    private String owner;
    private Double value;
 
    // getters and setters
}

5. Using Reactive Repositories

5.使用反应式存储库

We are already familiar with the repositories programming model, with the CRUD methods already defined plus support for some other common things as well.

我们已经熟悉了存储库编程模型,已经定义了CRUD方法,还支持其他一些常见的东西。

Now with the Reactive model, we get the same set of methods and specifications, except that we’ll deal with the results and parameters in a reactive way.

现在有了Reactive模型,我们得到了同样的一套方法和规范,只是我们将以一种反应式的方式来处理结果和参数。

5.1. ReactiveCrudRepository

5.1.ReactiveCrudRepository

We can use this repository the same way as the blocking CrudRepository:

我们可以用与阻塞CrudRepository相同的方式使用这个仓库。

@Repository
public interface AccountCrudRepository 
  extends ReactiveCrudRepository<Account, String> {
 
    Flux<Account> findAllByValue(String value);
    Mono<Account> findFirstByOwner(Mono<String> owner);
}

We can pass different types of arguments like plain (String), wrapped (Optional, Stream), or reactive (Mono, Flux) as we can see in the findFirstByOwner() method.

我们可以传递不同类型的参数,如普通参数(String)、包装参数(OptionalStream)或反应参数(MonoFlux),我们可以在findFirstByOwner()方法中看到。

5.2. ReactiveMongoRepository

5.2.ReactiveMongoRepository

There’s also the ReactiveMongoRepository interface, which inherits from ReactiveCrudRepository and adds some new query methods:

还有ReactiveMongoRepository接口,它继承自ReactiveCrudRepository并增加了一些新的查询方法。

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

Using the ReactiveMongoRepository, we can query by example:

使用ReactiveMongoRepository,我们可以通过实例进行查询。

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

As a result, we’ll get every Account that is the same as the example passed.

结果是,我们将得到每一个与所传递的例子相同的Account

With our repositories created, they already have defined methods to perform some database operations that we don’t need to implement:

在我们的存储库创建后,它们已经有定义好的方法来执行一些我们不需要实现的数据库操作。

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

5.3. RxJava2CrudRepository

5.3.RxJava2CrudRepository

With RxJava2CrudRepository, we have the same behavior as the ReactiveCrudRepository, but with the results and parameter types from RxJava:

使用RxJava2CrudRepository,我们有与ReactiveCrudRepository相同的行为,但有RxJava的结果和参数类型。

@Repository
public interface AccountRxJavaRepository 
  extends RxJava2CrudRepository<Account, String> {
 
    Observable<Account> findAllByValue(Double value);
    Single<Account> findFirstByOwner(Single<String> owner);
}

5.4. Testing Our Basic Operations

5.4.测试我们的基本操作

In order to test our repository methods, we’ll use the test subscriber:

为了测试我们的存储库方法,我们将使用测试订阅器。

@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

6.ReactiveMongoTemplate

Besides the repositories approach, we have the ReactiveMongoTemplate.

除了存储库方法,我们还有ReactiveMongoTemplate

First of all, we need to register ReactiveMongoTemplate as a bean:

首先,我们需要将ReactiveMongoTemplate注册为一个bean。

@Configuration
public class ReactiveMongoConfig {
 
    @Autowired
    MongoClient mongoClient;

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

And then, we can inject this bean into our service to perform the database operations:

然后,我们可以将这个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 also has a number of methods that do not relate to the domain we have, you can check them out in the documentation.

ReactiveMongoTemplate也有一些与我们的领域无关的方法,你可以在文档中查看它们。

7. Conclusion

7.结论

In this brief tutorial, we’ve covered the use of repositories and templates using reactive programming with MongoDB with Spring Data Reactive Repositories framework.

在这个简短的教程中,我们已经涵盖了利用Spring Data Reactive Repositories框架对MongoDB进行反应式编程的储存库和模板的使用。

The full source code for the examples is available over on GitHub.

例子的完整源代码可在GitHub上获得over