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 + GraphQL + MongoDB example with Spring Data & graphql-java
Spring Boot, MongoDB: JWT Authentication with Spring Security

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

Fullstack:
Angular + 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 Tutotial 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:

MethodsUrlsActions
POST/api/tutorialscreate new Tutorial
GET/api/tutorialsretrieve all Tutorials
GET/api/tutorials/:idretrieve a Tutorial by :id
PUT/api/tutorials/:idupdate a Tutorial by :id
DELETE/api/tutorials/:iddelete a Tutorial by :id
DELETE/api/tutorialsdelete all Tutorials
GET/api/tutorials/publishedfind 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 8
  • Spring Boot 2.2.4 (with Spring Web MVC, Spring Data MongoDB)
  • MongoDB
  • Maven 3.6.1

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

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 + 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

Happy learning! See you again.

Further Reading

Source Code

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

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

  1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *