Spring Boot MongoDB Tutorial

Spring Boot MongoDB Tutorial

Introduction

        Spring Boot is one of the most popular and most used frameworks for building microservices.
I am assuming that you have a Basic Knowledge of Spring Boot and have a Basic Spring Boot Application running in your machine. If not, please check my blog on Basic Spring Boot Application by going to the link: Spring Boot Tutorial.

In this tutorial, we will be integrating a Spring Boot Application with MongoDB, which is a non-relational database, to perform CRUD operations. The same steps can be followed to connect to any other non-relational database.

Requirements to Run the Application:
  1. Java
  2. Maven
  3. MongoDB
  4. IDE of your choice
MongoDB should be setup and running in your machine. To setup, run and test if MongoDB is working fine, please refer to my post on: MongoDB Setup.

In my MongoDB, I have created a database named: softwaredevelopercentral and collection named: employees. Hence once I connect, here are a few useful queries that I use:
> show dbs
> use softwaredevelopercentral
> db.employees.find().pretty()

Once you have a Basic Spring Boot Application, here are the additional steps required to integrate it with MongoDB.

Step 1: Maven Dependencies to be added in pom.xml

Here are the additional dependencies:
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>    

Here is the full 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.aj.springbootmongodb</groupId>
    <artifactId>SpringBootMongoDB</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
    </parent>

    <properties>
        <jdk.version>1.8</jdk.version>
        <java.version>1.8</java.version>
        <packaging>jar</packaging>
    </properties>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Setup MongoDB configuration in the yml file

MongoDB configuration is added to the yml file. I have named my database as: softwaredevelopercentral. Here is the yml file(application.yml):
server:
    port: 4000

spring:
    data:
        mongodb:
            host: localhost
            port: 27017
            database: softwaredevelopercentral
Please Note: I have set the server port as: 4000

Step 3: Create the Entity class

In this post, I am using a simple entity Employee with attributes: ID, NAME, DEPARTMENT and SALARY. Here is the Entity Model, Employee.java:
package com.aj.springbootmongodb.domain;

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

@Document(collection="employees")
public class Employee {

    @Id
    private String id;
    private String name;
    private String department;
    private Integer 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 String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public Integer getSalary() {
        return salary;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", department='" + department + '\'' +
                ", salary=" + salary +
                '}';
    }
}
In my MongoDB: softwaredevelopercentral, I have created a collection named as employees. In the entity class above you can see how I am mapping my entity to the collection using the annotation: @Document(collection="employees")

Step 4: Create Repository

Repository interface is created for all database operations. I am extending my repository from the MongoRepository which mainly provides MongoDB related functions. Here is the repository, EmployeeRepository.java:
package com.aj.springbootmongodb.repository;

import com.aj.springbootmongodb.domain.Employee;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EmployeesRepository extends MongoRepository<Employee, String> {

    Employee findByName(String name);
}

Step 5: Create the Service Layer

We will create a service layer, that acts as a mediator between the Controller and Repository layers. Here is the code for EmployeeServiceImpl.java:
package com.aj.springbootmongodb.service.impl;

import com.aj.springbootmongodb.domain.Employee;
import com.aj.springbootmongodb.repository.EmployeesRepository;
import com.aj.springbootmongodb.service.EmployeeService;
import com.mongodb.WriteResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmployeeServiceImpl implements EmployeeService {

    private static final Logger logger = LoggerFactory.getLogger(EmployeeServiceImpl.class);

    @Autowired
    private EmployeesRepository employeesRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    public void save(Employee employeeRequest) {
        logger.info("Entering EmployeeServiceImpl.save Method with Employee Details: {}", employeeRequest.toString());
        Employee employee = new Employee();
        employee.setName(employeeRequest.getName());
        employee.setDepartment(employeeRequest.getDepartment());
        employee.setSalary(employeeRequest.getSalary());
        employeesRepository.save(employee);
        logger.info("Leaving EmployeeServiceImpl.save Method");
    }

    public Iterable<Employee> findAll() {
        return employeesRepository.findAll();
    }

    public Employee findByName(String name) {
        return employeesRepository.findByName(name);
    }

    public List<Employee> findByKeySort(String key) {
        return employeesRepository.findAll(new Sort(Sort.Direction.ASC, key));
    }


    public int updateEmployee(Employee employeeRequest) {
        logger.info("Entering EmployeeServiceImpl.updateEmployee Method with Employee Details: {}", employeeRequest.toString());
        Query query = new Query();
        query.addCriteria(Criteria.where("name").is(employeeRequest.getName()));
        Update update = new Update();
        update.set("department", employeeRequest.getDepartment());
        update.set("salary", employeeRequest.getSalary());
        WriteResult result = mongoTemplate.updateFirst(query, update, Employee.class);
        if (result != null) {
            return result.getN();
        } else {
            return 0;
        }
    }

    public void delete(String name) {
        Employee employee = employeesRepository.findByName(name);
        employeesRepository.delete(employee);
    }
}

Step 6: Create Controller Class with RESTful APIs for CRUD operations on the Database

The EmployeeController class has the following APIs:
  1. GET API to get all employees
  2. GET API to get employee by NAME
  3. GET API to sort all employees by Document Key in Ascending order
  4. POST API to create an employee
  5. PUT API to update an employee
  6. DELETE API to delete an employee by NAME
Here is the code for EmployeeController.java:
package com.aj.springbootmongodb.controller;

import com.aj.springbootmongodb.domain.Employee;
import com.aj.springbootmongodb.service.EmployeeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestController
@RequestMapping("/employee")
public class EmployeeController {

    private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);

    @Autowired
    private EmployeeService employeeService;

    @RequestMapping(method = RequestMethod.GET)
 public ResponseEntity<Iterable<Employee>> getAllEmployees() {
        Iterable<Employee> employees = employeeService.findAll();
        return new ResponseEntity<>(employees, HttpStatus.OK);
 }

    @RequestMapping(value = "/{name}", method = RequestMethod.GET)
    public ResponseEntity<Employee> findByName(@PathVariable String name) {
        Employee employee = employeeService.findByName(name);
        return new ResponseEntity<>(employee, HttpStatus.OK);
    }

    @RequestMapping(value = "/sort/{key}", method = RequestMethod.GET)
    public ResponseEntity<List<Employee>> findByKeySort(@PathVariable String key) {
        List<Employee> employees = employeeService.findByKeySort(key);
        return new ResponseEntity<>(employees, HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.POST,
            consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, String>> addEmployee(@RequestBody Employee employeeRequest) {
        logger.info("Request received in EmployeeController.addEmployee is: " + employeeRequest.toString());
        Map<String, String> response = new HashMap<>();
        employeeService.save(employeeRequest);
        response.put("message", "Employee saved successfully");
        return new ResponseEntity<>(response, HttpStatus.OK);
    }


    @RequestMapping(method = RequestMethod.PUT,
            consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, String>> updateEmployee(@RequestBody Employee employeeRequest) {
        logger.info("Request received in EmployeeController.updateEmployee is: " + employeeRequest.toString());
        Map<String, String> response = new HashMap<>();
        int rowsUpdated = employeeService.updateEmployee(employeeRequest);
        if(rowsUpdated > 0) {
            response.put("message", "Employee updated successfully");
        }
        else{
            response.put("message", "Employee not updated");
        }
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    @RequestMapping(value = "/{name}", method = RequestMethod.DELETE)
    public ResponseEntity<Map<String, String>> deleteEmployee(@PathVariable String name) {
        Map<String, String> response = new HashMap<>();
        employeeService.delete(name);
        response.put("message", "Employee deleted successfully");
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

}

Step 7: Custom MongoTemplate in Application Class

By default Spring Data Mongo adds a key: _class and value as name of the domain class with package name in every document created from the application. Here is an example of how the data looks in MongoDB employees collection:
{
        "_id" : ObjectId("59abc8cb0a35680ef0b0b12d"),
        "_class" : "com.aj.springbootmongodb.domain.Employee",
        "name" : "Daniel",
        "department" : "Accounts",
        "salary" : 8000
}

To get the document in MongoDB without the _class key, add the following code in the Application class:
@Bean
    public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory,
                                       MongoMappingContext context) {

        MappingMongoConverter converter =
                new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), context);
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);

        return mongoTemplate;

    }
Here is the full application class, SpringBootMongoDBApplication.java:
package com.aj.springbootmongodb;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

@SpringBootApplication
public class SpringBootMongoDBApplication {

    private static final Logger logger = LoggerFactory.getLogger(SpringBootMongoDBApplication.class);

    public static void main(String[] args) throws Exception {
  SpringApplication.run(SpringBootMongoDBApplication.class,args);
 }

    @Bean
    public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory,
                                       MongoMappingContext context) {

        MappingMongoConverter converter =
                new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), context);
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);

        return mongoTemplate;

    }
}
After using the code above, the document in MongoDB employees collection will look as below:
{
        "_id" : ObjectId("59abc8cb0a35680ef0b0b12d"),
        "name" : "Daniel",
        "department" : "Accounts",
        "salary" : 8000
}

Project Setup:

For Java Setup, please refer to:  Java Setup
For Maven Setup, please refer to: Maven Setup
For Git and Project Setup, please refer to: Git and Project Setup

Run Application:

1. Start MongoDB by going to the bin directory on the MongoDB folder in command prompt and executing the command given below.
D:\Programs\MongoDB\Server\3.4\bin>mongod.exe --dbpath D:\Programs\MongoDB\Server\3.4\data
    You can see the start-up logs being printed.

2. To run application in your IDE use:
    Program arguments: src/main/resources/application.yml

3. To run JAR from command prompt:
    Build jar by using command:
  mvn clean install
    Run JAR by using command in Project folder location:
java -jar target\SpringBootMongoDB-1.0.0.jar src\main\resources\application.yml

API calls and results:

1. GET API to get all employees:

2. GET API to get employee by NAME
    http://localhost:4000/employee/Jane

3. GET API to sort all employees by Document Key in Ascending order
    As per the Employee domain class, the Document Keys each document in the employees           collection are:

  •     name
  •     department
  •     salary

4. POST API to create an employee
    JSON Request Body:
  {
   "name": "Jane",
   "department": "Accounts",
   "salary": 10000
  }

5. PUT  API to update an employee
    http://localhost:4000/employee
    JSON Request Body:
    {
        "name": "Jane",
        "department": "Accounts",
        "salary": 12000
    }

6. DELETE API to delete an employee by Name
    http://localhost:4000/employee/Jane

Other than these APIs, this application has the following APIs:

1. GET API to Ping and test if the application is up and running:

2. POST API to Ping and test if the application is up and running:
    JSON Request Body:
    {
     "input": "ping"
    }

Conclusion and GitHub link:

    In this post I have shown you how you can integrate your existing Spring Boot Application with MongoDB and perform CRUD operations using RESTful APIs. The code used in this post is available on GitHub.
    Dropwizard and Spring Boot are the most popular and most used frameworks for building microservices. Just like Spring Boot, Dropwizard also can be integrated with MongoDB in a few simple steps. To see how it is done, please check my post on Dropwizard MongoDB integration by going to the link: Dropwizard MongoDB Tutorial.
    Learn the most popular and trending technologies like Machine Learning, Angular 5, Internet of Things (IoT), Akka HTTP, Play Framework, Dropwizard, Docker, Elastic Stack, Spring Boot and Flask in simple steps by reading my most popular blog posts at Software Developer Central.
    If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter. Please leave a note below if you have any questions or comments.

Comments

Popular Posts

Golang gRPC Microservice

Dropwizard MySQL Integration Tutorial

Asynchronous Processing (@Async) in Spring Boot