Spring Data Reactive Repositories with Couchbase – 使用Couchbase的Spring Data Reactive Repositories

最后修改: 2019年 9月 10日

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

1. Overview

1.概述

In this tutorial, we’ll learn how to configure and implement database operations in a reactive way on Couchbase using Spring Data Repositories.

在本教程中,我们将学习如何在Couchbase上使用Spring Data Repositories以反应式方式配置和实现数据库操作。

We’ll cover the basic usages of ReactiveCrudRepository and ReactiveSortingRepository. Additionally, we’ll configure our test application with AbstractReactiveCouchbaseConfiguration.

我们将介绍ReactiveCrudRepositoryReactiveSortingRepository的基本使用方法。此外,我们将用AbstractReactiveCouchbaseConfiguration来配置我们的测试应用程序。

2. Maven Dependencies

2.Maven的依赖性

Firstly, let’s add the necessary dependencies:

首先,让我们添加必要的依赖性。

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-couchbase-reactive</artifactId>
</dependency>

The spring-boot-starter-data-couchbase-reactive dependency contains everything that we need to operate on Couchbase using reactive API.

spring-boot-starter-data-couchbase-reactive依赖项包含了我们使用反应式API对Couchbase进行操作所需的一切。

We’ll also include the reactor-core dependency to use Project Reactor API.

我们还将包括reactor-core依赖项,以使用Project Reactor API。

3. Configuration

3.配置

Next, let’s define the connection settings between Couchbase and our application.

接下来,我们来定义Couchbase和我们的应用程序之间的连接设置。

Let’s begin by creating a class that will hold our properties:

让我们开始创建一个将容纳我们的属性的类。

@Configuration
public class CouchbaseProperties {

    private List<String> bootstrapHosts;
    private String bucketName;
    private String bucketPassword;
    private int port;

    public CouchbaseProperties(
      @Value("${spring.couchbase.bootstrap-hosts}") List<String> bootstrapHosts, 
      @Value("${spring.couchbase.bucket.name}") String bucketName, 
      @Value("${spring.couchbase.bucket.password}") String bucketPassword, 
      @Value("${spring.couchbase.port}") int port) {
        this.bootstrapHosts = Collections.unmodifiableList(bootstrapHosts);
        this.bucketName = bucketName;
        this.bucketPassword = bucketPassword;
        this.port = port;
    }

    // getters
}

In order to use reactive support, we should create the configuration class that’ll extend AbstractReactiveCouchbaseConfiguration:

为了使用反应式支持,我们应该创建一个配置类,它将扩展AbstractReactiveCouchbaseConfiguration

@Configuration
@EnableReactiveCouchbaseRepositories("com.baeldung.couchbase.domain.repository")
public class ReactiveCouchbaseConfiguration extends AbstractReactiveCouchbaseConfiguration {

    private CouchbaseProperties couchbaseProperties;

    public ReactiveCouchbaseConfiguration(CouchbaseProperties couchbaseProperties) {
        this.couchbaseProperties = couchbaseProperties;
    }

    @Override
    protected List<String> getBootstrapHosts() {
        return couchbaseProperties.getBootstrapHosts();
    }

    @Override
    protected String getBucketName() {
        return couchbaseProperties.getBucketName();
    }

    @Override
    protected String getBucketPassword() {
        return couchbaseProperties.getBucketPassword();
    }

    @Override
    public CouchbaseEnvironment couchbaseEnvironment() {
        return DefaultCouchbaseEnvironment
          .builder()
          .bootstrapHttpDirectPort(couchbaseProperties.getPort())
          .build();
    }
}

Additionally, we’ve used @EnableReactiveCouchbaseRepositories to enable our reactive repositories that’ll be under the specified package.

此外,我们使用@EnableReactiveCouchbaseRepositories来启用我们的反应式仓库,这些仓库将在指定的包下。

Furthermore, we’ve overridden couchbaseEnvironment() in order to pass the Couchbase connection port.

此外,我们重写了couchbaseEnvironment(),以便传递Couchbase连接端口。

4. Repositories

4.存储库

In this section, we’ll learn how to create and use the reactive repository. By default, the “all” view is backing most CRUD operations. The custom repository methods are backed by N1QL. If the cluster doesn’t support N1QL, the UnsupportedCouchbaseFeatureException will be thrown during initialization.

在本节中,我们将学习如何创建和使用反应式资源库。默认情况下,”所有”视图正在支持大多数CRUD操作。自定义存储库方法由N1QL支持。如果集群不支持N1QL,在初始化过程中就会抛出UnsupportedCouchbaseFeatureException

Firstly, let’s create the POJO class that our repositories will work with:

首先,让我们创建一个POJO类,我们的存储库将与之合作。

@Document
public class Person {
    @Id private UUID id;
    private String firstName;

   //getters and setters
}

4.1. View-Based Repository

4.1.基于视图的存储库

Now, we’ll create a repository for Person:

现在,我们将为Person创建一个资源库。

@Repository
@ViewIndexed(designDoc = ViewPersonRepository.DESIGN_DOCUMENT)
public interface ViewPersonRepository extends ReactiveCrudRepository<Person, UUID> {

    String DESIGN_DOCUMENT = "person";
}

The repository extends the ReactiveCrudRepository interface in order to use Reactor API to interact with Couchbase.

存储库扩展了ReactiveCrudRepository接口,以便使用Reactor API与Couchbase交互。

Additionally, we can add a custom method and use the @View annotation to make it view-based:

此外,我们可以添加一个自定义方法,并使用@View注解,使其基于视图。

@View(designDocument = ViewPersonRepository.DESIGN_DOCUMENT)
Flux<Person> findByFirstName(String firstName);

By default, the query will look for a view named byFirstName. If we want to provide a custom view name, we’ll have to use the viewName argument.

默认情况下,该查询将寻找一个名为byFirstName的视图。如果我们想提供一个自定义的视图名称,我们就必须使用viewName参数。

Lastly, let’s create a simple CRUD test with the help of a test subscriber:

最后,让我们在测试用户的帮助下创建一个简单的CRUD测试。

@Test
public void shouldSavePerson_findById_thenDeleteIt() {
    final UUID id = UUID.randomUUID();
    final Person person = new Person(id, "John");
    personRepository
      .save(person)
      .subscribe();
 
    final Mono<Person> byId = personRepository.findById(id);
 
    StepVerifier
      .create(byId)
      .expectNextMatches(result -> result
        .getId()
        .equals(id))
      .expectComplete()
      .verify();
 
    personRepository
      .delete(person)
      .subscribe();
}

4.2. N1QL/View-Based Repository

4.2.N1QL/基于视图的存储库

Now, we’ll create the reactive repository for Person that’ll use the N1QL queries:

现在,我们将为Person创建反应式资源库,它将使用N1QL查询。

@Repository
@N1qlPrimaryIndexed
public interface N1QLPersonRepository extends ReactiveCrudRepository<Person, UUID> {
    Flux<Person> findAllByFirstName(String firstName);
}

The repository extends the ReactiveCrudRepository in order to use Reactor API as well. In addition, we’ve added a custom findAllByFirstName method, which creates the N1QL backed query.

存储库扩展了ReactiveCrudRepository,以便也使用Reactor API。此外,我们还添加了一个自定义的findAllByFirstName方法,它创建了N1QL支持的查询。

After that, let’s add the test for the findAllByFirstName method:

之后,让我们添加findAllByFirstName方法的测试。

@Test
public void shouldFindAll_byLastName() {
    final String firstName = "John";
    final Person matchingPerson = new Person(UUID.randomUUID(), firstName);
    final Person nonMatchingPerson = new Person(UUID.randomUUID(), "NotJohn");
    personRepository
      .save(matchingPerson)
      .subscribe();
    personRepository
      .save(nonMatchingPerson)
      .subscribe();
 
    final Flux<Person> allByFirstName = personRepository.findAllByFirstName(firstName);
 
    StepVerifier
      .create(allByFirstName)
      .expectNext(matchingPerson)
      .verifyComplete();
}

Furthermore, we’ll create a repository that allows us to retrieve people using the sorting abstraction:

此外,我们将创建一个资源库,允许我们使用排序抽象来检索人。

@Repository
public interface N1QLSortingPersonRepository extends ReactiveSortingRepository<Person, UUID> {
    Flux<Person> findAllByFirstName(String firstName, Sort sort);
}

Lastly, let’s write a test to check if the data is actually sorted:

最后,让我们写一个测试来检查数据是否真的被排序了。

@Test
public void shouldFindAll_sortedByFirstName() {
    final Person firstPerson = new Person(UUID.randomUUID(), "John");
    final Person secondPerson = new Person(UUID.randomUUID(), "Mikki");
    personRepository
      .save(firstPerson)
      .subscribe();
    personRepository
      .save(secondPerson)
      .subscribe();
 
    final Flux<Person> allByFirstName = personRepository
      .findAll(Sort.by(Sort.Direction.DESC, "firstName"));
 
    StepVerifier
      .create(allByFirstName)
      .expectNextMatches(person -> person
        .getFirstName()
        .equals(secondPerson.getFirstName()))
      .expectNextMatches(person -> person
        .getFirstName()
        .equals(firstPerson.getFirstName()))
      .verifyComplete();
}

5. Conclusion

5.总结

In this article, we’ve learned how to use repositories using reactive programming with Couchbase and Spring Data Reactive framework.

在这篇文章中,我们已经学会了如何使用Couchbase和Spring Data Reactive框架的反应式编程来使用存储库。

As always, the code for these examples is available over on Github.

像往常一样,这些例子的代码可在Github上获得