DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Spring Data: Easy MongoDB Migration Using Mongock
  • Advanced Search and Filtering API Using Spring Data and MongoDB
  • Manage Hierarchical Data in MongoDB With Spring
  • Spring Data: Data Auditing Using JaVers and MongoDB

Trending

  • How Large Tech Companies Architect Resilient Systems for Millions of Users
  • AI’s Role in Everyday Development
  • Performing and Managing Incremental Backups Using pg_basebackup in PostgreSQL 17
  • AI-Based Threat Detection in Cloud Security
  1. DZone
  2. Data Engineering
  3. Databases
  4. Spring Data MongoDB With Reactive MongoDB

Spring Data MongoDB With Reactive MongoDB

With the rise in popularity of NoSQL databases, MongoDB has rapidly gained popularity. See reactive programming features in Spring Framework 5 and Spring Data MongoDB.

By 
John Thompson user avatar
John Thompson
·
Aug. 09, 17 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
27.2K Views

Join the DZone community and get the full member experience.

Join For Free

Spring Data MongoDB has been updated to leverage the reactive programming model introduced in Spring Framework 5. This was followed by support for reactive data access for NoSQL databases such as MongoDB, Cassandra, and Redis.

With the rise in popularity of NoSQL databases, MongoDB has rapidly gained popularity in the enterprise and the Spring community.

I have published both an article and a video for setting up MongoDB within a Spring Boot application.

In this post, we’ll take a look at using the reactive programming features in Spring Framework 5 and Spring Data MongoDB.

If you’re new to reactive programming, I’ll suggest you first go through What Are Reactive Streams in Java? post, followed by the Spring Web Reactive post.

The Maven POM

For this post, I’m using embedded MongoDB. I want the benefit of talking to an instance loaded in-memory with the same capabilities as my production environment. This makes development and testing blazing fast.

You can check my post to configure and use embedded MongoDB in a Spring Boot application here.

The dependency to bring in the Embedded MongoDB is:

<dependency>
   <groupId>de.flapdoodle.embed</groupId>
   <artifactId>de.flapdoodle.embed.mongo</artifactId>
   <scope>runtime</scope>
</dependency>

The whole capability of Reactive MongoDB lies on the MongoDB driver. The official MongoDB Reactive Streams Java Driver implements the Reactive Streams API for interoperability with other reactive stream implementations. The reactive driver provides asynchronous stream processing with non-blocking back pressure for MongoDB.

To use the driver, add this dependency.

<dependency>
   <groupId>org.mongodb</groupId>
   <artifactId>mongodb-driver-reactivestreams</artifactId>
   <version>1.5.0</version>
</dependency>

Here is the complete pom.xml: 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
    </parent>
    <artifactId>spring-boot-reactive-mongodb</artifactId>
    <name>SpringBoot Reactive MongoDB</name>
    <properties>
        <spring-data-releasetrain.version>Kay-M1</spring-data-releasetrain.version>
        <spring.version>5.0.0.M3</spring.version>
        <reactor.version>3.0.3.RELEASE</reactor.version>
        <mongodb-driver-reactivestreams.version>1.5.0</mongodb-driver-reactivestreams.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-reactivestreams</artifactId>
            <version>${mongodb-driver-reactivestreams.version}</version>
        </dependency>
        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>spring-libs-snapshot</id>
            <url>https://repo.spring.io/libs-snapshot</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-libs-snapshot</id>
            <url>https://repo.spring.io/libs-snapshot</url>
        </pluginRepository>
    </pluginRepositories>
</project>

The Domain Object

I have written a product domain object for this post. Products have a name, description, price, and product URL. 

Product.java:

package guru.springframework.domain;

import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.math.BigDecimal;

@Document
public class Product {

    @Id
    private ObjectId _id;
    private String name;
    private String description;
    private BigDecimal price;
    private String imageUrl;

    public Product(String name, String description, BigDecimal price, String imageUrl) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.imageUrl = imageUrl;
    }

    public ObjectId getId() {
        return _id;
    }

    public void setId(ObjectId id) {
        this._id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

Spring Data MongoDB Reactive CRUD Repository

If you have worked with Spring Data in a Spring Boot application, you are familiar with the repository pattern. You extend CrudRepository or its sub-interface and Spring Data MongoDB will generate the implementation for you.

Reactive repositories work the same way. You extend your repository interface from ReactiveCrudRepository, specify domain-specific query methods, and rely on Spring Data MongoDB to provide the implementations.

ReactiveCrudRepository uses reactive types introduced in Spring Framework 5. These are Mono and Flux, which implement Reactive Streams.

Here is reactive repository interface.

ReactiveProductRepository.java:

package guru.springframework.repositories;

import guru.springframework.domain.Product;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

public interface ReactiveProductRepository extends ReactiveCrudRepository<Product, String> {
    Flux<Product> findByName(String name);

    Flux<Product> findByName(Mono<String> name);

    Mono<Product> findByNameAndImageUrl(Mono<String> name, String imageUrl);

    @Query("{ 'name': ?0, 'imageUrl': ?1}")
    Mono<Product> findByNameAndImageUrl(String name, String imageUrl);
}

As you can see, in this ReactiveProductRepository interface, the repository uses reactive types as return types.

Reactive repositories in Spring Data MongoDB can also use reactive types for parameters. The overloaded findByName() and findByNameAndImageUrl() methods are examples of this.

Configuration for Spring Data MongoDB Reactive Repositories

The configuration class is similar to a nonreactive one. Along with some infrastructural setup, we have the @EnableReactiveMongoRepositories annotation that activates support for reactive Spring Data.

The code of the ApplicationConfiguration class is this.

ApplicationConfiguration.java:

package guru.springframework;


import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.core.mapping.event.LoggingEventListener;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;

@SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@EnableReactiveMongoRepositories
@AutoConfigureAfter(EmbeddedMongoAutoConfiguration.class)
class ApplicationConfiguration extends AbstractReactiveMongoConfiguration {
    private final Environment environment;

    public ApplicationConfiguration(Environment environment) {
        this.environment = environment;
    }

    @Override
    @Bean
    @DependsOn("embeddedMongoServer")
    public MongoClient mongoClient() {
        int port = environment.getProperty("local.mongo.port", Integer.class);
        return MongoClients.create(String.format("mongodb://localhost:%d", port));
    }

    @Override
    protected String getDatabaseName() {
        return "reactive-mongo";
    }
}

This ApplicationConfiguration class extends AbstractReactiveMongoConfiguration, the base class for reactive Spring Data MongoDB configuration. The mongoClient() method is annotated with @Bean to explicitly declare a configurable MongoClient bean that represents a pool of connections for MongoDB.

Spring Data MongoDB Integration Tests

Let’s write few integration tests for the repository layer to verify that our code is using reactive MongoDB as expected.

Here is the integration test code:

ReactiveProductRepositoryIntegrationTest.java:

package guru.springframework;

import static org.assertj.core.api.Assertions.*;

import guru.springframework.domain.Product;
import guru.springframework.repositories.ReactiveProductRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ReactiveProductRepositoryIntegrationTest {

    @Autowired
    ReactiveProductRepository repository;
    @Autowired
    ReactiveMongoOperations operations;

    @Before
    public void setUp() {
        operations.collectionExists(Product.class)
                .flatMap(exists -> exists ? operations.dropCollection(Product.class) : Mono.just(exists))
                .flatMap(o -> operations.createCollection(Product.class, new CollectionOptions(1024 * 1024, 100, true)))
                .then()
                .block();

        repository
                .save(Flux.just(new Product("T Shirt", "Spring Guru printed T Shirt", new BigDecimal(125), "tshirt1.png"),
                        new Product("T Shirt", "Spring Guru plain T Shirt", new BigDecimal(115), "tshirt2.png"),
                        new Product("Mug", "Spring Guru printed Mug", new BigDecimal(39), "mug1.png"),
                        new Product("Cap", "Spring Guru printed Cap", new BigDecimal(66), "cap1.png")))

                .then()
                .block();
    }

    @Test
    public void findByNameAndImageUrlWithStringQueryTest() {

        Product mug = repository.findByNameAndImageUrl("Mug", "mug1.png")
                .block();
        assertThat(mug).isNotNull();
    }

    @Test
    public void findByNameAndImageUrlWithMonoQueryTest() {
        Product cap = repository.findByNameAndImageUrl(Mono.just("Cap"), "cap1.png")
                .block();
        assertThat(cap).isNotNull();
    }

    @Test
    public void findByNameWithStringQueryTest() {
        List<Product> tShirts = repository.findByName("T Shirt")
                .collectList()
                .block();
        assertThat(tShirts).hasSize(2);
    }

    @Test
    public void findByNameWithMonoQueryTest() {
        List<Product> tShirts = repository.findByName(Mono.just("T Shirt"))
                .collectList()
                .block();
        assertThat(tShirts).hasSize(2);
    }


}

In the test class, we auto-wired in two Spring Beans.

Our ReactiveProductRepository implementation that Spring Data MongoDB provides and a ReactiveMongoOperations  implementation.

ReactiveMongoOperations is the interface for the main reactive Template API class, ReactiveMongoTemplate. This interface defines a basic set of reactive data access operations using Project Reactor Mono and Flux reactive types.

ReactiveMongoOperations contains reactive counterpart for most of the operations available in the MongoOperations interface of the traditional blocking template API.

The setup portion of our integration test will drop any existing documents and recreate the Product collection. The setup method then inserts four new documents into our MongoDB collection.

We’re calling the .block() method to ensure processing completes before the next command is executed.

Here is the output of the integration tests from IntelliJ:

Test Output from Spring Data MongoDB reactive tests

You can get the complete source code for this post here.

Spring Framework Spring Data MongoDB Data (computing)

Published at DZone with permission of John Thompson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Data: Easy MongoDB Migration Using Mongock
  • Advanced Search and Filtering API Using Spring Data and MongoDB
  • Manage Hierarchical Data in MongoDB With Spring
  • Spring Data: Data Auditing Using JaVers and MongoDB

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!