Overview:
In this article, I would like to show how to perform various CRUD Operations with Spring Data Reactive MongoDB Repository.
Project Setup:
Lets first create a simple spring boot project with the required dependencies like Spring Data Reactive MongoDB.
Sample Application:
We are going to develop a simple spring boot application for freelancers in which the users can register themselves. So that people who want to hire freelancers can search with specific skill sets.
MongoDB Setup:
I use docker-compose to set up MongoDB.
version: "3"
services:
mongo:
image: mongo
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
mongo-express:
image: mongo-express
ports:
- 8081:8081
restart: always
depends_on:
- mongo
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: password
ME_CONFIG_MONGODB_SERVER: mongo
Entity:
The entity class is designed as below. I use lombok for the getters and setters.
@Data
@Document
@ToString
public class Freelancer {
@Id
private String id;
private String name;
private int age;
private List<String> skills;
}
Spring Data Reactive MongoDB Repository:
We create a repository to do basic CRUD Operations by extending ReactiveMongoRepository. We also add couple of methods to the interface for searching with specific skill sets.
- findBySkillsAll – We might want to search for people with all given skills.
- findBySkillsIn – We might want to search for people containing 1 of the given skills.
@Repository
public interface FreelancerRepository extends ReactiveMongoRepository<Freelancer, String> {
@Query("{ 'skills': { $all: ?0 } }")
Flux<Freelancer> findBySkillsAll(List<String> skills);
Flux<Freelancer> findBySkillsIn(List<String> skills);
}
Service:
Let’s create a service class for the CRUD operations and for the above search functionality.
@Service
public class FreelancerService {
@Autowired
private FreelancerRepository repository;
public Flux<Freelancer> findBySkillsOne(final List<String> skills){
return this.repository.findBySkillsIn(skills);
}
public Flux<Freelancer> findBySkillsAll(final List<String> skills){
return this.repository.findBySkillsAll(skills);
}
public Mono<Freelancer> getPerson(final String id){
return this.repository.findById(id);
}
public Mono<Freelancer> savePerson(final Freelancer person){
return this.repository.save(person);
}
public Mono<Freelancer> updatePerson(final Freelancer person){
return this.repository.findById(person.getId())
.map(p -> person)
.flatMap(this.repository::save);
}
public Mono<Void> deletePerson(final String id){
return this.repository.deleteById(id);
}
}
REST Controller:
Lest create the controller as shown here. Do note that when we use reactive driver nothing happens until we subscribe to that action. If you notice, even a delete method which could have been ‘void’ return type returns a Mono<Void>. If you want the return type to be void, ensure that you are subscribing to that Flux / Mono from the Spring Data Reactive MongoDB repository. Otherwise it will not work. I directly expose entity class for this demo. In real life you might want to use a DTO.
@RestController
public class FreelancerController {
@Autowired
private FreelancerService freelancerService;
@GetMapping("/person/skills-one/{skills}")
public Flux<Freelancer> findBySkills(@PathVariable List<String> skills){
return this.freelancerService.findBySkillsOne(skills);
}
@GetMapping("/person/skills-all/{skills}")
public Flux<Freelancer> findByAllSkills(@PathVariable List<String> skills){
return this.freelancerService.findBySkillsAll(skills);
}
@GetMapping("/person/{id}")
public Mono<Freelancer> getPerson(@PathVariable String id){
return this.freelancerService.getPerson(id);
}
@PostMapping("/person")
public Mono<Freelancer> createPerson(@RequestBody Freelancer person){
return this.freelancerService.savePerson(person);
}
@PutMapping("/person")
public Mono<Freelancer> updatePerson(@RequestBody Freelancer person){
return this.freelancerService.updatePerson(person);
}
@DeleteMapping("/person/{id}")
public Mono<Void> deletePerson(@PathVariable String id){
return this.freelancerService.deletePerson(id);
}
}
Application Properties:
spring.data.mongodb.database=admin
spring.data.mongodb.username=admin
spring.data.mongodb.password=password
CRUD Operations:
- Lets start the application to perform the various CRUD operations.
- I register few freelancers as shown here using the POST endpoint.
{
"name": "sam",
"age": 40,
"skills": [ "js", "react", "python"]
}
{
"name": "jack",
"age": 38,
"skills": [ "js", "angular", "postgres"]
}
{
"name": "james",
"age": 30,
"skills": [ "java", "reactor", "mongo"]
}
{
"name": "smith",
"age": 32,
"skills": [ "qa", "selenium"]
}
We should be able to view all the records via Mongo express at port 8081.
- GET
- PUT
- Search By All skills
- Search By one of the skills
- DELETE
Summary:
We were able to do simple CRUD operations with Spring Data Reactive MongoDB Repository easily as Spring Data does all the heavy lifting.
You can find the source code here.
Learn more about MongoDB + Reactive Spring Data here.
- MongoDB Aggregation Pipeline With Reactive Spring Data
- MongoDB Tailable Cursor With Reactive Spring Data
- MongoDB Change Streams With Reactive Spring Data
Happy learning 🙂