#
Reactive Programming: Spring Boot & MongoDB
This tutorial explains you how you can access the MongoDB database in a reactive manner using Spring Boot WebFlux.
Reactive Streams gives us a common API for Reactive Programming in Java. For more information about Reactive Programming you can take a look at the Reactive manifesto as well.
Reactive programming is about building asynchronous, non-blocking and event-driven applications that can easily scale. Each event is published to subscribers while ensuring that the subscribers are never overwhelmed.
Spring 5 Framework introduced Reactor as an implementation for the Reactive Streams specification (by introducing a brand new reactive framework called Spring WebFlux). Reactor is a next-gen Reactive library for building non-blocking applications on the JVM. Reactor is a fourth-generation Reactive library for building non-blocking applications on the JVM based on the Reactive Streams Specification.
Reactor Provides two main types called Flux and Mono. Both of these types implement the Publisher interface provided by Reactive Streams. Flux is used to represent a stream of 0..N elements and Mono is used to represent a stream of 0..1 element.
In this tutorial I will explain how to access the MongoDB database in a Reactive (asynchronous manner) using the Spring Boot and WebFlux framework.
For creating an example of accessing the MongoDB database in a Reactive manner you have to create a simple Spring Boot application using Spring Initializr. As dependencies, you have to use "Reactive MongoDB" and "Reactive Web"
Open the unzipped application generated by Initializr with Spring Tool Suite (or Eclipse, etc).
Maven will download the needed dependencies automatically.
Run the WebFlux Application in order to see if all works good. If not it is possible not to have the Maven Repository OK and you must run the following commands:
cd <project_home>
mvn dependency:purge-local-repository
Once the project is running without errors add/modify the following classes in order to get you project running:
package com.example.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Employee {
@Id
private String id;
private String name;
private Long salary;
public Employee() {
}
public Employee(String id, String name, Long salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getSalary() {
return salary;
}
public void setSalary(Long salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
package com.example.model;
import java.util.Date;
public class EmployeeDate {
private Employee employee;
private Date date;
public EmployeeDate(Employee employee, Date date) {
this.employee = employee;
this.date = date;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
package com.example.repository;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import com.example.model.Employee;
public interface EmployeeRepository extends ReactiveMongoRepository<employee, string=""> {
}
package com.example.resources;
import java.time.Duration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.model.Employee;
import com.example.model.EmployeeDate;
import com.example.repository.EmployeeRepository;
import java.util.Date;
import java.util.stream.Stream;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@RestController
@RequestMapping("/rest/employee")
public class EmployeeResource {
private EmployeeRepository employeeRepository;
public EmployeeResource(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
@GetMapping("/all")
public Flux<employee> getAll() {
return employeeRepository.findAll();
}
@GetMapping("/{id}")
public Mono<employee> getId(@PathVariable("id") final String empId) {
return employeeRepository.findById(empId);
}
@GetMapping(value = "/{id}/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<employeedate> getEvents(@PathVariable("id")
final String empId) {
return employeeRepository.findById(empId)
.flatMapMany(employee -> {
Flux<long> interval = Flux.interval(Duration.ofSeconds(2));
Flux<employeedate> employeeEventFlux =
Flux.fromStream(
Stream.generate(() -> new EmployeeDate(employee,
new Date()))
);
return Flux.zip(interval, employeeEventFlux)
.map(Tuple2::getT2);
});
}
}
package com.example;
import java.util.UUID;
import java.util.stream.Stream;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.example.model.Employee;
import com.example.repository.EmployeeRepository;
@SpringBootApplication
public class ReactiveMongoExampleApplication {
@Bean
CommandLineRunner employees(EmployeeRepository employeeRepository) {
return args -> {
employeeRepository
.deleteAll()
.subscribe(null, null, () -> {
Stream.of(new Employee(UUID.randomUUID().toString(), "Samuel", 12000L),
new Employee(UUID.randomUUID().toString(), "Dana", 13000L),
new Employee(UUID.randomUUID().toString(), "Paul", 20000L),
new Employee(UUID.randomUUID().toString(), "Denis", 33000L)
)
.forEach(employee -> {
employeeRepository
.save(employee)
.subscribe(System.out::println);
});
});
};
}
public static void main(String[] args) {
SpringApplication.run(ReactiveMongoExampleApplication.class, args);
}
}
Run the Spring MonoWebFlux Application and you will see something like this:
You can open the MongoDB Compass to see the test1 database collections:
In our case, we have only one collection "employee".
This database was configured in the application.properties file, which contains:
spring.data.mongodb.uri=mongodb://localhost:27017/test1
Into the "employee" collection we have 4 documents:
You can get the information from the MongoDB database using the reactive way :
or
As you can see you can use the Reactive programming to interact with a reactive database (MongoDB, Redis, Cassandra, Couchbase).