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
對於這篇文章,我正在使用嵌入式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()方法就是這樣的示例。
配置類相似於非反應性類。 除了一些基礎設施設置以外,咱們還有@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的鏈接池。
這是集成測試代碼:
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集成測試的輸出:
結論
文章寫道這裏,若有不足之處,歡迎補充評論。
抽絲剝繭,細說架構那些事!