帶有響應式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集成測試的輸出:

 

 

 結論

文章寫道這裏,若有不足之處,歡迎補充評論。

抽絲剝繭,細說架構那些事!

相關文章
相關標籤/搜索