Spring Boot 2.x实战88 - 响应式编程3 - Reactive NoSQL之Reactive ElasticSearch

3. Reactive NoSQL

上面我们的Repository是非响应式的,而响应式编程要求全栈技术都是响应式,所以这节我们讨论响应式的Spring Data。目前Spring Data支持的响应式的NoSQL有:

  • MongoDB:使用spring-boot-starter-data-mongodb-reactive依赖;
  • Redis:使用spring-boot-starter-data-redis-reactive依赖;
  • Cassandra:使用spring-boot-starter-data-cassandra-reactive依赖;
  • Couchbase:使用spring-boot-starter-data-couchbase-reactive依赖;
  • Elasticsearch:使用spring-boot-starter-data-elasticsearch依赖;

3.1 响应式Elasticsearch

本节依然使用Elasticsearch作为演示:

新建应用,信息如下:

Group:top.wisely

Artifact:learning-reactive-nosql

Dependencies:Spring Reactive WebSpring Data ElasticsearchLombok

build.gradle文件中的依赖如下:

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
   implementation 'org.springframework.boot:spring-boot-starter-webflux'
   compileOnly 'org.projectlombok:lombok'
   annotationProcessor 'org.projectlombok:lombok'
	//...
}
3.1.1 Spring Boot的自动配置

Spring Boot自2.2.0版本开始支持响应式Elasticsearch的自动配置,自动配置文件为:

  • ReactiveElasticsearchRepositoriesAutoConfiguration:通过@EnableReactiveElasticsearchRepositories开启响应式Elasticsearch Repository的支持;

  • ElasticsearchDataAutoConfiguration:通过导入lasticsearchDataConfiguration.ReactiveRestClientConfiguration来配置响应式操作模板ReactiveElasticsearchTemplate的Bean;

  • ReactiveRestClientAutoConfiguration:通过ReactiveRestClientProperties,以spring.data.elasticsearch.client.reactive.*开头来配置响应式Elasticsearch客户端ReactiveElasticsearchClient的Bean。

3.1.2 示例

首先我们配置连接Elasticsearch:

spring:
  data:
    elasticsearch:
      client:
        reactive:
          endpoints: localhost:9200

下面我们的工作和Spring Data的其它项目是类似的,我们先定义领域模型:

//...
import org.springframework.data.elasticsearch.annotations.Document;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "person")
public class Person {
    @Id
    private String id;
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

然后我们再进行Repository的定义,此处我们继承的是ReactiveElasticsearchRepository

//...
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;

public interface PersonRepository extends ReactiveElasticsearchRepository<Person, String> {
}

我们看一下ReactiveElasticsearchRepository的父接口的定义:

@NoRepositoryBean
public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> {
   <S extends T> Mono<S> save(S entity);
   <S extends T> Flux<S> saveAll(Iterable<S> entities);
   <S extends T> Flux<S> saveAll(Publisher<S> entityStream);
   Mono<T> findById(ID id);
   Mono<T> findById(Publisher<ID> id);
   Mono<Boolean> existsById(ID id);
   Mono<Boolean> existsById(Publisher<ID> id);
   Flux<T> findAll();
   Flux<T> findAllById(Iterable<ID> ids);
   Flux<T> findAllById(Publisher<ID> idStream);
   Mono<Long> count();
   Mono<Void> deleteById(ID id);
   Mono<Void> deleteById(Publisher<ID> id);
   Mono<Void> delete(T entity);
   Mono<Void> deleteAll(Iterable<? extends T> entities);
   Mono<Void> deleteAll(Publisher<? extends T> entityStream);
   Mono<Void> deleteAll();
}

从父级Repository接口的方法声明可以看出,所有的返回值都是响应式类型,也支持参数是响应式的。

下面我们用响应式控制器来调用Repository:

@RestController
@RequestMapping("/people")
public class PersonController {

    PersonRepository personRepository;

    public PersonController(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<Person> add(@RequestBody Person person){
        return personRepository.save(person);
    }

    @GetMapping("/{id}")
    public Mono<Person> getById(@PathVariable String id) {
        return personRepository.findById(id);
    }
    @GetMapping
    public Flux<Person> list(){
        return personRepository.findAll();
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public Mono<Void> delete(@PathVariable("id") String id) {
        return personRepository.deleteById(id);
    }
}

代码逻辑和上例是一样的,下面我们依然使用上例的WebClient代码验证开发的结果,我们对ControllerClient的代码做了一些修改:

public String add1(){
    System.out.println("添加第一条数据");
    Mono<Person> mono = webClient
            .post()
            .uri("http://localhost:8080/people")
            .body(Mono.just(new Person("wyf", 35)), Person.class) //使用2个参数的构造器
            .retrieve()
            .bodyToMono(Person.class);
    Person person = mono.block(); //使用block()方法订阅Mono并将其变为同步
    System.out.println(person);
    return person.getId(); //获取id给删除使用
}
    public void delete(String id){
        System.out.println("删除一条数据");
        Mono<Void> mono = webClient
                .delete()
                .uri("http://localhost:8080/people/{id}", id) //使用外部传递的id来删除
                .retrieve()
                .bodyToMono(Void.class);
        mono.subscribe(System.out::println);
    }

CommandLineRunner的代码修改为:

@Bean
CommandLineRunner webClientClr(ControllerClient controllerClient){
   return args -> {
      String id = controllerClient.add1();
      controllerClient.add2();
      Thread.sleep(1000);
      controllerClient.list();
      Thread.sleep(1000);
      controllerClient.delete(id);
      Thread.sleep(1000);
      controllerClient.list();
   };
}

我们先删除上一章中生成的Elasticsearch的person索引,在Postman中用DELETE方法调用http://localhost:9200/person:
在这里插入图片描述

最终运行应用的效果为:
在这里插入图片描述

新书推荐:

我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html
在这里插入图片描述

主要包含目录有:

第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和批处理(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。

猜你喜欢

转载自blog.csdn.net/wiselyman/article/details/107098901