Spring Boot MongoDB CRUD example with Maven

In this tutorial, we’re gonna build a Spring Boot Rest API example that use Spring Data MongoDB & Maven to make CRUD operations with MongoDB database. You’ll know:

  • How to configure Spring Data to work with MongoDB Database
  • How to define MongoDB Data Models and Repository interfaces
  • Way to create Spring Rest Controller to process HTTP requests
  • Way to use Spring Data MongoDB to interact with MongoDB Database

More Practice:
Spring Boot, MongoDB, Reactive CRUD example
Spring Boot + GraphQL + MongoDB example with Spring Data & graphql-java
Spring Boot, MongoDB: JWT Authentication with Spring Security
Spring Boot MongoDB Pagination example with Spring Data

Exception Handling:
Spring Boot @ControllerAdvice & @ExceptionHandler example
@RestControllerAdvice example in Spring Boot

Unit Test: Spring Boot Unit Test for Rest Controller
Documentation: Spring Boot Swagger 3 example
Caching: Spring Boot Redis Cache example
Validation: Spring Boot Validate Request Body

Fullstack:
Angular 8 + Spring Boot + MongoDB example
Angular 10 + Spring Boot + MongoDB example
Angular 11 + Spring Boot + MongoDB example
Angular 12 + Spring Boot + MongoDB example
Angular 13 + Spring Boot + MongoDB example
Angular 14 + Spring Boot + MongoDB example
Angular 15 + Spring Boot + MongoDB example
React + Spring Boot + MongoDB example
Vue.js + Spring Boot + MongoDB example


Overview of Spring Boot MongoDB CRUD example

We will build a Spring Boot MongoDB Rest CRUD API for a Tutorial application in that:

  • Each Tutorial has id, title, description, published status.
  • Apis help to create, retrieve, update, delete Tutorials.
  • Apis also support custom finder methods such as find by published status or by title.

These are APIs that we need to provide:

Methods Urls Actions
POST /api/tutorials create new Tutorial
GET /api/tutorials retrieve all Tutorials
GET /api/tutorials/:id retrieve a Tutorial by :id
PUT /api/tutorials/:id update a Tutorial by :id
DELETE /api/tutorials/:id delete a Tutorial by :id
DELETE /api/tutorials delete all Tutorials
GET /api/tutorials/published find all published Tutorials
GET /api/tutorials?title=[keyword] find all Tutorials which title contains keyword

– We make CRUD operations & finder methods using Spring Data MongoDB.
– Rest Controller will be created with the help of Spring Web MVC.

Technology

  • Java 17 / 11 / 8
  • Spring Boot 3 / 2 (with Spring Web MVC, Spring Data MongoDB)
  • MongoDB
  • Maven

Project Structure

spring-boot-mongodb-crud-example-project-structure

  • Tutorial data model class corresponds to entity and table tutorials.
  • TutorialRepository is an interface that extends MongoRepository for CRUD methods and custom finder methods. It will be autowired in TutorialController.
  • TutorialController is a RestController which has request mapping methods for RESTful requests such as: getAllTutorials, createTutorial, updateTutorial, deleteTutorial, findByPublished
  • Configuration for Spring Data MongoDB is in application.properties.
  • pom.xml contains dependencies for Spring Boot Web MVC and Spring Data MongoDB.

Let’s implement this application right now.

Create & Setup Spring Boot project

Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.

Then open pom.xml and add these dependencies:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

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

Configure Spring Data MongoDB

Under src/main/resources folder, open application.properties and add following lines.

spring.data.mongodb.database=bezkoder_db
spring.data.mongodb.port=27017

Define Data Model

Our Data model is Tutorial with four fields: id, title, description, published.
In model package, we define Tutorial class.

model/Tutorial.java

package com.bezkoder.spring.data.mongodb.model;

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

@Document(collection = "tutorials")
public class Tutorial {
  @Id
  private String id;

  private String title;
  private String description;
  private boolean published;

  public Tutorial() {

  }

  public Tutorial(String title, String description, boolean published) {
    this.title = title;
    this.description = description;
    this.published = published;
  }

  public String getId() {
    return id;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getDescription() {
    return description;
  }

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

  public boolean isPublished() {
    return published;
  }

  public void setPublished(boolean isPublished) {
    this.published = isPublished;
  }

  @Override
  public String toString() {
    return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
  }
}

@Document annotation helps us override the collection name by “tutorials”.

Create Repository Interface

Let’s create a repository to interact with Tutorials from the database.
In repository package, create TutorialRepository interface that extends MongoRepository.

repository/TutorialRepository.java

package com.bezkoder.spring.data.mongodb.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.bezkoder.spring.data.mongodb.model.Tutorial;

public interface TutorialRepository extends MongoRepository<Tutorial, String> {
  List<Tutorial> findByTitleContaining(String title);
  List<Tutorial> findByPublished(boolean published);
}

Now we can use MongoRepository’s methods: save(), findOne(), findById(), findAll(), count(), delete(), deleteById()… without implementing these methods.

We also define custom finder methods:
findByTitleContaining(): returns all Tutorials which title contains input title.
findByPublished(): returns all Tutorials with published having value as input published.

The implementation is plugged in by Spring Data MongoDB automatically.

Create Spring Rest APIs Controller

Finally, we create a controller that provides APIs for creating, retrieving, updating, deleting and finding Tutorials.

controller/TutorialController.java

package com.bezkoder.spring.data.mongodb.controller;

...
import com.bezkoder.spring.data.mongodb.model.Tutorial;
import com.bezkoder.spring.data.mongodb.repository.TutorialRepository;

@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {

  @Autowired
  TutorialRepository tutorialRepository;

  @GetMapping("/tutorials")
  public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
    
  }

  @GetMapping("/tutorials/{id}")
  public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") String id) {
    
  }

  @PostMapping("/tutorials")
  public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
    
  }

  @PutMapping("/tutorials/{id}")
  public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") String id, @RequestBody Tutorial tutorial) {
    
  }

  @DeleteMapping("/tutorials/{id}")
  public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") String id) {
    
  }

  @DeleteMapping("/tutorials")
  public ResponseEntity<HttpStatus> deleteAllTutorials() {
    
  }

  @GetMapping("/tutorials/published")
  public ResponseEntity<List<Tutorial>> findByPublished() {
    
  }

}

@CrossOrigin is for configuring allowed origins.
@RestController annotation is used to define a controller and to indicate that the return value of the methods should be be bound to the web response body.
@RequestMapping("/api") declares that all Apis’ url in the controller will start with /api.
– We use @Autowired to inject TutorialRepository bean to local variable.

Now I will show you how to implement each controller’s CRUD methods.

Create Operation

We use @PostMapping annotation for handling POST HTTP requests.
A new Tutorial will be created by MongoRepository.save() method.

@PostMapping("/tutorials")
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
  try {
    Tutorial _tutorial = tutorialRepository.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
    return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
  } catch (Exception e) {
    return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

Retrieve Operations

We use @GetMapping annotation for handling GET HTTP requests, then Repository’s findAll(), findByTitleContaining(title), findByPublished() method to get the result.

  • getAllTutorials(): returns List of Tutorials, if there is title parameter, it returns a List in that each Tutorial contains the title
  • getTutorialById(): returns Tutorial by given id
  • findByPublished(): return published Tutorials
@GetMapping("/tutorials")
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
  try {
    List<Tutorial> tutorials = new ArrayList<Tutorial>();

    if (title == null)
      tutorialRepository.findAll().forEach(tutorials::add);
    else
      tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);

    if (tutorials.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    return new ResponseEntity<>(tutorials, HttpStatus.OK);
  } catch (Exception e) {
    return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

@GetMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") String id) {
  Optional<Tutorial> tutorialData = tutorialRepository.findById(id);

  if (tutorialData.isPresent()) {
    return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);
  } else {
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
  }
}

@GetMapping("/tutorials/published")
public ResponseEntity<List<Tutorial>> findByPublished() {
  try {
    List<Tutorial> tutorials = tutorialRepository.findByPublished(true);

    if (tutorials.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<>(tutorials, HttpStatus.OK);
  } catch (Exception e) {
    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

Update Operation

@PutMapping will help us handle PUT HTTP requests.
updateTutorial() receives id and a Tutorial payload.
– from the id, we get the Tutorial from database using findById() method.
– then we use the payload and save() method for updating the Tutorial.

@PutMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") String id, @RequestBody Tutorial tutorial) {
  Optional<Tutorial> tutorialData = tutorialRepository.findById(id);

  if (tutorialData.isPresent()) {
    Tutorial _tutorial = tutorialData.get();
    _tutorial.setTitle(tutorial.getTitle());
    _tutorial.setDescription(tutorial.getDescription());
    _tutorial.setPublished(tutorial.isPublished());
    return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
  } else {
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
  }
}

Delete Operation

We use @DeleteMapping for DELETE HTTP requests.
There are 2 methods:

  • deleteTutorial(): delete a Tutorial document with given id
  • deleteAllTutorials(): remove all documents in tutorials collection

The operations is done with the help of MongoRepository’s deleteById() and deleteAll() method.

@DeleteMapping("/tutorials/{id}")
public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") String id) {
  try {
    tutorialRepository.deleteById(id);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  } catch (Exception e) {
    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

@DeleteMapping("/tutorials")
public ResponseEntity<HttpStatus> deleteAllTutorials() {
  try {
    tutorialRepository.deleteAll();
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  } catch (Exception e) {
    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

Run & Test

Run Spring Boot application with Maven command: mvn spring-boot:run.

Create some Tutorials:

spring-boot-mongodb-crud-example-create

Check MongoDB database:

spring-boot-mongodb-crud-example-create-db

Update some Tutorials:

spring-boot-mongodb-crud-example-update

Check MongoDB database after updating:

spring-boot-mongodb-crud-example-update-db

Get all Tutorials:

spring-boot-mongodb-crud-example-retrieve-all

Get a Tutorial by Id:

spring-boot-mongodb-crud-example-retrieve-one

Find all published Tutorials:

spring-boot-mongodb-crud-example-find-published

Find all Tutorials which title contains ‘ring’:

spring-boot-mongodb-crud-example-find-by-title

Delete a Tutorial:

spring-boot-mongodb-crud-example-delete-one

Check MongoDB database after deleting the Tutorial:

spring-boot-mongodb-crud-example-delete-one-db

Delete all Tutorials:

spring-boot-mongodb-crud-example-delete-all

You can use the Simple HTTP Client using Axios to check it.

axios-request-example-get-post-put-delete

Or: Simple HTTP Client using Fetch API

Conclusion

Today we’ve built a Rest CRUD API using Spring Boot, Spring Data MongoDB & Maven to create, retrieve, update, delete documents in MongoDB database.

We also see that MongoRepository supports a great way to make CRUD operations and custom finder methods without need of boilerplate code.

This project can be used in the following full-stack:
Angular 8 + Spring Boot + MongoDB example
Angular 10 + Spring Boot + MongoDB example
Angular 11 + Spring Boot + MongoDB example
Angular 12 + Spring Boot + MongoDB example
Angular 13 + Spring Boot + MongoDB example
Angular 14 + Spring Boot + MongoDB example
Angular 15 + Spring Boot + MongoDB example
React + Spring Boot + MongoDB example
Vue.js + Spring Boot + MongoDB example

You may need to handle Exception with:
Spring Boot @ControllerAdvice & @ExceptionHandler example
@RestControllerAdvice example in Spring Boot

Or make pagination with structure of the response like this:

{
    "totalItems": 12,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

With the tutorial: Spring Boot MongoDB Pagination example with Spring Data

Happy learning! See you again.

Further Reading

Source Code

You can find the complete source code for this tutorial on Github.

Reactive: Spring Boot, MongoDB, Reactive CRUD example
Documentation: Spring Boot + Swagger 3 example (with OpenAPI 3)
Caching: Spring Boot Redis Cache example
Validation: Spring Boot Validate Request Body

18 thoughts to “Spring Boot MongoDB CRUD example with Maven”

  1. Sort of off topic, but I’m having trouble integrating this with Spring Graphql. Are there any tweaks I would need to make from this tutorial?

  2. Thanks for the clear cut explanation. This was the easiest, working and clear tutorial I could find for this topic.

  3. Is it just me or anyone else finds that mongo connection strings in application.properties are not put correctly. There is no connection string. I can understand if that was obvious but some reference to it for someone totally new would have been nice. Even the github doesn’t mention it.

  4. In this example, do we need to manually start a database named bezkoder_db, or does Spring application starts it automatically when we edit application.properties file and run Spring app ?

  5. I try to edit create function with @PostMapping as follows
    MentorApplication mentorApplication = MentorApplicationRepository.save(new MentorApplication(application.getId(),application.getTopic(), application.getSubtopic(),application.getDiscussion(), application.getCreationDate(), application.getStatus()));
    but in this line I get
    “not make a static reference to the non-static method save(MentorApplication) from the type CrudRepository” error

    my repository interface is as follows
    public interface MentorApplicationRepository extends MongoRepository {
    List findByIDContaining(String id);
    List findByTopic(String topic);
    List findByStatus(int status);
    }
    anybody got an error like this ? or knows how to solve it ?

    1. Hi, if you need only Rest API for testing, just remove it. But in practice, CORS is an important protocol for making cross-domain requests possible.
      For example, the front-end JavaScript code served from https://aaa.com makes a request for https://bbb.com/data.json.

  6. Very knowledge full Tutorial. Can you please implement this with UI along with TestCases and with more custom exception for understanding more.

  7. Thank you soo muchh I’ve been trying to find an example that works and as simple as possible this worked perfectly with my understanding

  8. Hi!
    Thanks for all your help with this.

    Im trying to run this sample but i have troubles whith Mongo.
    Maybe it’s a newbie question but i’ve never used Mongo.

    Cluster created with settings {hosts=[localhost:27017], mode=MULTIPLE, requiredClusterType=UNKNOWN, serverSelectionTimeout=’30000 ms’, maxWaitQueueSize=500}

    Adding discovered server localhost:27017 to client view of cluster

    Exception in monitor thread while connecting to server localhost:27017

    com.mongodb.MongoSocketOpenException: Exception opening socket
    at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:70) ~[mongodb-driver-core-3.11.2.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:128) ~[mongodb-driver-core-3.11.2.jar:na]
    at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:117) ~[mongodb-driver-core-3.11.2.jar:na]
    at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na]
    Caused by: java.net.ConnectException: Connection refused
    at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
    at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:579) ~[na:na]
    at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
    at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597) ~[na:na]
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:339) ~[na:na]
    at java.base/java.net.Socket.connect(Socket.java:603) ~[na:na]
    at com.mongodb.internal.connection.SocketStreamHelper.initialize(SocketStreamHelper.java:64) ~[mongodb-driver-core-3.11.2.jar:na]
    at com.mongodb.internal.connection.SocketStream.initializeSocket(SocketStream.java:79) ~[mongodb-driver-core-3.11.2.jar:na]
    at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:65) ~[mongodb-driver-core-3.11.2.jar:na]
    … 3 common frames omitted

    Spring boot finally starts but mongo never replies received requests.

Comments are closed to reduce spam. If you have any question, please send me an email.