带有响应式MongoDB的Spring Data MongoDB

经过优锐课的java学习分享中,讨论随着NoSQL数据库的普及,MongoDB迅速普及。咱们能够看到,码了不少专业的相关知识, 分享给你们参考学习。 

Spring Data MongoDB已更新,以利用Spring Framework 5中引入的反应式编程模型。随后是对NoSQL数据库(例如MongoDB,Cassandra和Redis)的反应式数据访问的支持。java

随着NoSQL数据库的普及,MongoDB在企业和Spring社区中迅速普及。react

 

在本文中,咱们将介绍如何使用Spring Framework 5和Spring Data MongoDB中的反应式编程功能。spring

若是是反应式编程的新手,建议首先阅读Java中的反应式流是什么?帖子,而后再阅读Spring Web Reactive帖子。mongodb

Maven POM

对于这篇文章,我正在使用嵌入式MongoDB。 我想与在内存中加载的实例进行对话,该实例具备与个人生产环境相同的功能,从而受益不浅。 这使得开发和测试快速发展。数据库

能够在此处查看个人文章以在Spring Boot应用程序中配置和使用嵌入式MongoDB。apache

引入嵌入式MongoDB的依赖关系是:编程

1 <dependency>
2 
3    <groupId>de.flapdoodle.embed</groupId>
4 
5    <artifactId>de.flapdoodle.embed.mongo</artifactId>
6 
7    <scope>runtime</scope>
8 
9 </dependency>

 

Reactive MongoDB的所有功能取决于MongoDB驱动程序。 官方的MongoDB Reactive Streams Java驱动程序实现了Reactive Streams API,以与其余反应式流实现实现互操做性。 反应性驱动程序为MongoDB提供具备无阻塞背压的异步流处理。api

要使用驱动程序,请添加此依赖项。架构

1 <dependency>
2 
3    <groupId>org.mongodb</groupId>
4 
5    <artifactId>mongodb-driver-reactivestreams</artifactId>
6 
7    <version>1.5.0</version>
8 
9 </dependency>

 

这是完整的pom.xml:app

  1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2 
  3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4 
  5     <modelVersion>4.0.0</modelVersion>
  6 
  7     <parent>
  8 
  9         <groupId>org.springframework.boot</groupId>
 10 
 11         <artifactId>spring-boot-starter-parent</artifactId>
 12 
 13         <version>1.5.4.RELEASE</version>
 14 
 15     </parent>
 16 
 17     <artifactId>spring-boot-reactive-mongodb</artifactId>
 18 
 19     <name>SpringBoot Reactive MongoDB</name>
 20 
 21     <properties>
 22 
 23         <spring-data-releasetrain.version>Kay-M1</spring-data-releasetrain.version>
 24 
 25         <spring.version>5.0.0.M3</spring.version>
 26 
 27         <reactor.version>3.0.3.RELEASE</reactor.version>
 28 
 29         <mongodb-driver-reactivestreams.version>1.5.0</mongodb-driver-reactivestreams.version>
 30 
 31         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 32 
 33         <java.version>1.8</java.version>
 34 
 35     </properties>
 36 
 37     <dependencies>
 38 
 39         <dependency>
 40 
 41             <groupId>org.springframework.boot</groupId>
 42 
 43             <artifactId>spring-boot-starter</artifactId>
 44 
 45         </dependency>
 46 
 47         <dependency>
 48 
 49             <groupId>org.springframework.data</groupId>
 50 
 51             <artifactId>spring-data-mongodb</artifactId>
 52 
 53         </dependency>
 54 
 55         <dependency>
 56 
 57             <groupId>io.projectreactor</groupId>
 58 
 59             <artifactId>reactor-core</artifactId>
 60 
 61         </dependency>
 62 
 63         <dependency>
 64 
 65             <groupId>org.mongodb</groupId>
 66 
 67             <artifactId>mongodb-driver-reactivestreams</artifactId>
 68 
 69             <version>${mongodb-driver-reactivestreams.version}</version>
 70 
 71         </dependency>
 72 
 73         <dependency>
 74 
 75             <groupId>de.flapdoodle.embed</groupId>
 76 
 77             <artifactId>de.flapdoodle.embed.mongo</artifactId>
 78 
 79             <scope>runtime</scope>
 80 
 81         </dependency>
 82 
 83         <dependency>
 84 
 85             <groupId>org.springframework.boot</groupId>
 86 
 87             <artifactId>spring-boot-starter-test</artifactId>
 88 
 89             <scope>test</scope>
 90 
 91         </dependency>
 92 
 93     </dependencies>
 94 
 95     <repositories>
 96 
 97         <repository>
 98 
 99             <id>spring-libs-snapshot</id>
100 
101             <url>https://repo.spring.io/libs-snapshot</url>
102 
103         </repository>
104 
105     </repositories>
106 
107     <pluginRepositories>
108 
109         <pluginRepository>
110 
111             <id>spring-libs-snapshot</id>
112 
113             <url>https://repo.spring.io/libs-snapshot</url>
114 
115         </pluginRepository>
116 
117     </pluginRepositories>
118 
119 </project>

 

域对象

我已经为这篇文章写了一个产品领域对象。 产品具备名称,描述,价格和产品URL。

 1 Product.java:
 2 
 3 package guru.springframework.domain;
 4 
 5 import org.bson.types.ObjectId;
 6 
 7 import org.springframework.data.annotation.Id;
 8 
 9 import org.springframework.data.mongodb.core.mapping.Document;
10 
11 import java.math.BigDecimal;
12 
13 @Document
14 
15 public class Product {
16 
17     @Id
18 
19     private ObjectId _id;
20 
21     private String name;
22 
23     private String description;
24 
25     private BigDecimal price;
26 
27     private String imageUrl;
28 
29     public Product(String name, String description, BigDecimal price, String imageUrl) {
30 
31         this.name = name;
32 
33         this.description = description;
34 
35         this.price = price;
36 
37         this.imageUrl = imageUrl;
38 
39     }
40 
41     public ObjectId getId() {
42 
43         return _id;
44 
45     }
46 
47     public void setId(ObjectId id) {
48 
49         this._id = id;
50 
51     }
52 
53     public String getDescription() {
54 
55         return description;
56 
57     }
58 
59     public void setDescription(String description) {
60 
61         this.description = description;
62 
63     }
64 
65     public BigDecimal getPrice() {
66 
67         return price;
68 
69     }
70 
71     public void setPrice(BigDecimal price) {
72 
73         this.price = price;
74 
75     }
76 
77     public String getImageUrl() {
78 
79         return imageUrl;
80 
81     }
82 
83     public void setImageUrl(String imageUrl) {
84 
85         this.imageUrl = imageUrl;
86 
87     }
88 
89 }

 

Spring Data MongoDB反应式CRUD存储库

若是Spring Boot应用程序中使用过Spring Data,那么将熟悉存储库模式。 扩展了CrudRepository或其子接口,Spring Data MongoDB将为生成实现。

反应性存储库以相同的方式工做。 能够从ReactiveCrudRepository扩展存储库接口,指定特定于域的查询方法,并依靠Spring Data MongoDB提供实现。

ReactiveCrudRepository使用Spring Framework 5中引入的反应类型。它们是Mono和Flux,它们实现了反应流。

这是反应式存储库界面。

 1 ReactiveProductRepository.java:
 2 
 3 package guru.springframework.repositories;
 4 
 5 import guru.springframework.domain.Product;
 6 
 7 import reactor.core.publisher.Flux;
 8 
 9 import reactor.core.publisher.Mono;
10 
11 import org.springframework.data.mongodb.repository.Query;
12 
13 import org.springframework.data.repository.reactive.ReactiveCrudRepository;
14 
15 public interface ReactiveProductRepository extends ReactiveCrudRepository<Product, String> {
16 
17     Flux<Product> findByName(String name);
18 
19     Flux<Product> findByName(Mono<String> name);
20 
21     Mono<Product> findByNameAndImageUrl(Mono<String> name, String imageUrl);
22 
23     @Query("{ 'name': ?0, 'imageUrl': ?1}")
24 
25     Mono<Product> findByNameAndImageUrl(String name, String imageUrl);
26 
27 }

 

所见,在此ReactiveProductRepository接口中,存储库使用反应类型做为返回类型。

Spring Data MongoDB中的反应性存储库也可使用反应性类型做为参数。 重载的findByName()和findByNameAndImageUrl()方法就是这样的示例。

Spring Data MongoDB反应性存储库的配置

配置类相似于非反应性类。 除了一些基础设施设置以外,咱们还有@EnableReactiveMongoRepositories批注,用于激活对反应式Spring Data的支持。

ApplicationConfiguration类的代码是这里。

 1 ApplicationConfiguration.java:
 2 
 3 package guru.springframework;
 4 
 5 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 6 
 7 import org.springframework.boot.autoconfigure.SpringBootApplication;
 8 
 9 import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
10 
11 import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
12 
13 import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
14 
15 import org.springframework.context.annotation.Bean;
16 
17 import org.springframework.context.annotation.DependsOn;
18 
19 import org.springframework.core.env.Environment;
20 
21 import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
22 
23 import org.springframework.data.mongodb.core.mapping.event.LoggingEventListener;
24 
25 import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
26 
27 import com.mongodb.reactivestreams.client.MongoClient;
28 
29 import com.mongodb.reactivestreams.client.MongoClients;
30 
31 @SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
32 
33 @EnableReactiveMongoRepositories
34 
35 @AutoConfigureAfter(EmbeddedMongoAutoConfiguration.class)
36 
37 class ApplicationConfiguration extends AbstractReactiveMongoConfiguration {
38 
39     private final Environment environment;
40 
41     public ApplicationConfiguration(Environment environment) {
42 
43         this.environment = environment;
44 
45     }
46 
47     @Override
48 
49     @Bean
50 
51     @DependsOn("embeddedMongoServer")
52 
53     public MongoClient mongoClient() {
54 
55         int port = environment.getProperty("local.mongo.port", Integer.class);
56 
57         return MongoClients.create(String.format("mongodb://localhost:%d", port));
58 
59     }
60 
61     @Override
62 
63     protected String getDatabaseName() {
64 
65         return "reactive-mongo";
66 
67     }
68 
69 }

 

这个ApplicationConfiguration类扩展了AbstractReactiveMongoConfiguration,它是反应式Spring Data MongoDB配置的基类。 mongoClient()方法使用@Bean注释,以显式声明一个可配置的MongoClient bean,该bean表明MongoDB的链接池。

Spring Data MongoDB集成测试

让咱们为存储库层编写一些集成测试,以验证咱们的代码是否按预期使用了反应式MongoDB。

这是集成测试代码:

  1 ReactiveProductRepositoryIntegrationTest.java:
  2 
  3 package guru.springframework;
  4 
  5 import static org.assertj.core.api.Assertions.*;
  6 
  7 import guru.springframework.domain.Product;
  8 
  9 import guru.springframework.repositories.ReactiveProductRepository;
 10 
 11 import reactor.core.publisher.Flux;
 12 
 13 import reactor.core.publisher.Mono;
 14 
 15 import java.math.BigDecimal;
 16 
 17 import java.util.List;
 18 
 19 import org.junit.Before;
 20 
 21 import org.junit.Test;
 22 
 23 import org.junit.runner.RunWith;
 24 
 25 import org.springframework.beans.factory.annotation.Autowired;
 26 
 27 import org.springframework.boot.test.context.SpringBootTest;
 28 
 29 import org.springframework.data.mongodb.core.CollectionOptions;
 30 
 31 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
 32 
 33 import org.springframework.test.context.junit4.SpringRunner;
 34 
 35 @RunWith(SpringRunner.class)
 36 
 37 @SpringBootTest
 38 
 39 public class ReactiveProductRepositoryIntegrationTest {
 40 
 41     @Autowired
 42 
 43     ReactiveProductRepository repository;
 44 
 45     @Autowired
 46 
 47     ReactiveMongoOperations operations;
 48 
 49     @Before
 50 
 51     public void setUp() {
 52 
 53         operations.collectionExists(Product.class)
 54 
 55                 .flatMap(exists -> exists ? operations.dropCollection(Product.class) : Mono.just(exists))
 56 
 57                 .flatMap(o -> operations.createCollection(Product.class, new CollectionOptions(1024 * 1024, 100, true)))
 58 
 59                 .then()
 60 
 61                 .block();
 62 
 63         repository
 64 
 65                 .save(Flux.just(new Product("T Shirt", "Spring Guru printed T Shirt", new BigDecimal(125), "tshirt1.png"),
 66 
 67                         new Product("T Shirt", "Spring Guru plain T Shirt", new BigDecimal(115), "tshirt2.png"),
 68 
 69                         new Product("Mug", "Spring Guru printed Mug", new BigDecimal(39), "mug1.png"),
 70 
 71                         new Product("Cap", "Spring Guru printed Cap", new BigDecimal(66), "cap1.png")))
 72 
 73                 .then()
 74 
 75                 .block();
 76 
 77     }
 78 
 79     @Test
 80 
 81     public void findByNameAndImageUrlWithStringQueryTest() {
 82 
 83         Product mug = repository.findByNameAndImageUrl("Mug", "mug1.png")
 84 
 85                 .block();
 86 
 87         assertThat(mug).isNotNull();
 88 
 89     }
 90 
 91     @Test
 92 
 93     public void findByNameAndImageUrlWithMonoQueryTest() {
 94 
 95         Product cap = repository.findByNameAndImageUrl(Mono.just("Cap"), "cap1.png")
 96 
 97                 .block();
 98 
 99         assertThat(cap).isNotNull();
100 
101     }
102 
103     @Test
104 
105     public void findByNameWithStringQueryTest() {
106 
107         List<Product> tShirts = repository.findByName("T Shirt")
108 
109                 .collectList()
110 
111                 .block();
112 
113         assertThat(tShirts).hasSize(2);
114 
115     }
116 
117     @Test
118 
119     public void findByNameWithMonoQueryTest() {
120 
121         List<Product> tShirts = repository.findByName(Mono.just("T Shirt"))
122 
123                 .collectList()
124 
125                 .block();
126 
127         assertThat(tShirts).hasSize(2);
128 
129     }
130 
131 }

 

在测试类中,咱们自动链接了两个Spring Bean。

Spring Data MongoDB提供的咱们的ReactiveProductRepository实现和ReactiveMongoOperations实现。

ReactiveMongoOperations是主要的反应模板API类ReactiveMongoTemplate的接口。 该接口使用Project Reactor Mono和Flux反应类型定义了一组基本的反应数据访问操做。

ReactiveMongoOperations包含反应性对应项,可用于传统阻止模板API的MongoOperations接口中的大多数操做。

咱们的集成测试的设置部分将删除全部现有文档并从新建立产品集合。 而后,安装方法将四个新文档插入到咱们的MongoDB集合中。

咱们正在调用.block()方法以确保在执行下一条命令以前完成处理。

这是IntelliJ集成测试的输出:

 

 

 结论

文章写道这里,若有不足之处,欢迎补充评论。

抽丝剥茧,细说架构那些事!

相关文章
相关标签/搜索