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集成测试的输出:
结论
文章写道这里,若有不足之处,欢迎补充评论。
抽丝剥茧,细说架构那些事!