带有响应式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 
2 
3    de.flapdoodle.embed
4 
5    de.flapdoodle.embed.mongo
6 
7    runtime
8 
9 

 

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

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

1 
2 
3    org.mongodb
4 
5    mongodb-driver-reactivestreams
6 
7    1.5.0
8 
9 

 

这是完整的pom.xml:

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

 

域对象

我已经为这篇文章写了一个产品领域对象。 产品具有名称,描述,价格和产品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 {
16 
17     Flux findByName(String name);
18 
19     Flux findByName(Mono name);
20 
21     Mono findByNameAndImageUrl(Mono name, String imageUrl);
22 
23     @Query("{ 'name': ?0, 'imageUrl': ?1}")
24 
25     Mono 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 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 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集成测试的输出:

带有响应式MongoDB的Spring Data MongoDB_第1张图片

 

 

 结论

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

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

你可能感兴趣的:(带有响应式MongoDB的Spring Data MongoDB)