Skip to main content

mongodb-crud

Description

This Example is Spring WebFlux non-blocking, asynchronous, and event-driven application. We are going to build REST APIs using Spring Boot WebFlux and MongoDB. Using an API call, we will perform a Create, Read, Update, and Delete (CRUD) operation.

Requirements

  • Java 17+
  • Spring Boot 3.2.3
  • MongoDB 7

Project Structure

spring-webflux-mongodb-crud-1-proj-struct.png

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://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>3.2.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>zjc.examples</groupId>
<artifactId>spring-webflux-mongodb-crud-02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springmongodb</name>

<description>Demo project for Spring Boot MongoDB CRUD API</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

application.properties

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=mydb

Entity

Product.java class which will be the MongoDB document.

package zjc.examples.spring.webflux.mongodb.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "products")
public class Product {
// The ID of the product
@Id
private String id;

// The name of the product
private String name;

// The quantity of the product
private int qty;

// The price of the product
private double price;
}

ProductDto.java

package zjc.examples.spring.webflux.mongodb.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductDto {

// The ID of the product
private String id;

// The name of the product
private String name;

// The quantity of the product
private int qty;

// The price of the product
private double price;
}

ProductRepository.java

package zjc.examples.spring.webflux.mongodb.repository;

import zjc.examples.spring.webflux.mongodb.entity.Product;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends ReactiveMongoRepository<Product, String> {

}

ProductService.java

Mono and Flux types represent asynchronous sequence of elements. Where Mono represents a stream of 0 or 1 element and Flux is for a stream of 0 to N elements.

package zjc.examples.spring.webflux.mongodb.service;

import zjc.examples.spring.webflux.mongodb.dto.ProductDto;
import zjc.examples.spring.webflux.mongodb.repository.ProductRepository;
import zjc.examples.spring.webflux.mongodb.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class ProductService {

@Autowired
private ProductRepository repository;

// Get all products
public Flux<ProductDto> getProducts(){
return repository.findAll().map(AppUtils::entityToDto);
}

// Get product using Id
public Mono<ProductDto> getProduct(String id){
return repository.findById(id).map(AppUtils::entityToDto);
}

// Create Product
public Mono<ProductDto> saveProduct(Mono<ProductDto> productDtoMono){
return productDtoMono.map(AppUtils::dtoToEntity)
.flatMap(repository::insert)
.map(AppUtils::entityToDto);
}

// Update Product
public Mono<ProductDto> updateProduct(Mono<ProductDto> productDtoMono, String id){
return repository.findById(id)
.flatMap(p -> productDtoMono.map(AppUtils::dtoToEntity)
.doOnNext(e -> e.setId(id)))
.flatMap(repository::save)
.map(AppUtils::entityToDto);
}

// Delete Product
public Mono<Void> deleteProduct(String id){
return repository.deleteById(id);
}
}

AppUtils.java

package zjc.examples.spring.webflux.mongodb.utils;

import zjc.examples.spring.webflux.mongodb.dto.ProductDto;
import zjc.examples.spring.webflux.mongodb.entity.Product;
import org.springframework.beans.BeanUtils;

// Utility class for converting between Product and ProductDto objects
public class AppUtils {

// Convert Product entity to ProductDto
public static ProductDto entityToDto(Product product) {
ProductDto productDto = new ProductDto();
BeanUtils.copyProperties(product, productDto);
return productDto;
}

// Convert ProductDto to Product entity
public static Product dtoToEntity(ProductDto productDto) {
Product product = new Product();
BeanUtils.copyProperties(productDto, product);
return product;
}
}

ProductController.java

package zjc.examples.spring.webflux.mongodb.controller;

import zjc.examples.spring.webflux.mongodb.dto.ProductDto;
import zjc.examples.spring.webflux.mongodb.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

// Controller class for managing Product-related HTTP requests
@RestController
@RequestMapping("/products")
public class ProductController {

@Autowired
private ProductService service;

// Get all products
@GetMapping
public Flux<ProductDto> getProducts(){
return service.getProducts();
}

// GET product using Id
@GetMapping("/{id}")
public Mono<ProductDto> getProduct(@PathVariable String id){
return service.getProduct(id);
}

// Create new Product
@PostMapping
public Mono<ProductDto> saveProduct(@RequestBody Mono<ProductDto> productDtoMono){
return service.saveProduct(productDtoMono);
}

// Update product using Id
@PutMapping("/update/{id}")
public Mono<ProductDto> updateProduct(@RequestBody Mono<ProductDto> productDtoMono, @PathVariable String id){
return service.updateProduct(productDtoMono, id);
}

// Delete Product using Id
@DeleteMapping("/delete/{id}")
public Mono<Void> deleteProduct(@PathVariable String id){
return service.deleteProduct(id);
}
}

Running

mvn spring-boot:run

In Postman:

Create Operation

spring-webflux-mongodb-crud-1-create-product.png

mongodb:

~$ mongosh
Current Mongosh Log ID: 6676a8b203ca3bc446597192
Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.9
Using MongoDB: 7.0.11
Using Mongosh: 2.2.9

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

------
The server generated these startup warnings when booting
2024-06-22T09:15:08.912+02:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
2024-06-22T09:15:09.187+02:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
2024-06-22T09:15:09.187+02:00: vm.max_map_count is too low
------

test> use mydb
switched to db mydb
mydb> db.products.find()
[
{
_id: ObjectId('6676a8c7570db464a118377a'),
name: 'Java',
qty: 2,
price: 29,
_class: 'zjc.examples.spring.webflux.mongodb.entity.Product'
}
]
mydb>

Analogously:

  • Read Operation,
  • Update Operation
  • Delete Operation.

Source code:

https://github.com/ZbCiok/zjc-examples/tree/main/spring/webflux/spring-webflux-mongodb-crud-02