带有响应式MongoDB的Spring Data MongoDB


通过优锐课的java学习分享中,讨论随着NoSQL数据库的普及,MongoDB迅速普及。我们可以看到,码了很多专业的相关知识, 分享给大家参考学习。 

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

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

 

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

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

Maven POM

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

可以在此处查看我的文章以在Spring Boot应用程序中配置和使用嵌入式MongoDB。

引入嵌入式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提供具有无阻塞背压的异步流处理。

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

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:

  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集成测试的输出:

 

 

 结论

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

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM