Spring Data: MongoDB Tutorial

Overview

Spring Data is an umbrella project which contains many submodules, each specific to a particular database. In this article, we'll be covering Spring Data MongoDB by building an application that stores and retrieves data from MongoDB, a document based NO-SQL database.

If you'd like to read more about Spring Data, we've covered it in detail in - Guide to Spring Data JPA.

MongoDB

MongoDB is a document-oriented NoSQL database that stores JSON-like documents with dynamic schemas. It is commonly used for high volume data storage.

Before moving forward, it would be good to know some of the NoSQL database terms. Please note that these terms are not exactly a one-to-one in comparison with relational SQL databases:

  • Database: It is a container for collections and can be thought of as similar to an RDBMS database, which is a container for Tables.
  • Collection: It is equivalent to Tables in RDBMS, but unlike a collection it has a dynamic schema. A collection exists within a database.
  • Document: It is a single record in a MongoDB collection. It can be thought of as a row in RDBMS.
  • Field: A document has zero or more fields. It's like an RDBMS column having a key-value pair.

To setup MongoDB server on your local machine you can download the installable here according to your operating system. You can then also download a tool like Compass for a nice GUI to interact with your server.

Another option and the one which we will be using is MongoDB Atlas, which is a cloud database as a service. After you sign up, login and build a cluster using the free tier:

To connect to our cluster, we have to create a user:

Let's now create our database and collection:

We are now set to connect to our collection using our Spring application.

Spring Data MongoDB Project

Setup

The best way to start with a skeleton project is to visit Spring Initializr. Select your preferred version of Spring Boot and add the Web and MongoDB dependencies:

After this, generate it as a Maven project and you're all set!

Defining a Collection

First, let's define our collection Candidate model class:

@Document(collection = "candidate")
public class Candidate {
    @Id
    private String id;

    private String name;

    private double exp;

    @Indexed(unique = true)
    private String email;

    // getters and setters
}

Now let's take a look at these annotations:

  • @Document: This marks the class as a domain object that will be persisted into the database. The default collection name that is used is the class name (first character lowercased). We can map to a different collection in the database by using the collection attribute of the annotation.
  • @Id: This marks the field used for identity purposes.
  • @Indexed(unique = true): This is applied to the field that will be indexed with a constraint of unique.

Defining Repository

We create a repository by making an interface:

public interface CandidateRepository extends MongoRepository<Candidate, String> {}

CandidateRepository extends the MongoRepository interface and plugs in the datatype of the document that we are working with, i.e Candidate and String respectively.

This will give us access to all the CRUD operations around the MongoDB collection.

Connection Setup

To set up a proper connection, we need to define the connection properties in application.properties:

spring.data.mongodb.uri=mongodb+srv://<USERNAME>:<PASSWORD>@<ClUSTER-NAME>-<INSTANCE-ID>/<DATABASE-NAME>?retryWrites=true

You can get these values directly from the MongoDB Atlas UI:

Note: If your password contains special characters then it must be URL encoded.

By default, your cluster is secured not to take requests from any client IP. We need to allow our IP to be able to connect to this cluster via an IP Whitelist:

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Defining the Controller

Now, let's use our repository in our CandidateController via the @Autowired annotation:

@RestController
@RequestMapping("/candidate")
public class CandidateController {

    @Autowired
    private CandidateRepository candidateRepository;

Simple CRUD Operations

Insert

Let's create a POST mapping that will insert data to our MongoDB:

@PostMapping
@ResponseStatus(code = HttpStatus.CREATED)
public Candidate add(@RequestBody Candidate candidate) {
    return candidateRepository.save(candidate);
}

We used the save() method on the candidateRepository object. The Candidate object is captured by @RequestBody and is used directly in the save() method.

If we try to use the same email ID again, we will get a duplicate key error:

We can check our collection status in Atlas too:

Read

Let's create a couple of GET mappings to fetch our records.

@GetMapping
public List<Candidate> getAll() {
    return candidateRepository.findAll();
}

@GetMapping(value = "/{id}")
public Candidate getOne(@PathVariable String id) {
    return candidateRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException());
}

findAll() will return all the records in our database, while the findById() method will return a single record based on the ID passed.

If the record is not present it throws a custom runtime exception. ResourceNotFoundException is a custom class that returns 404 status if it's thrown:

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException() {
    }
}

If you'd like to read more about this, we've covered it in detail in - Exception Handling in Spring.

Update

Now to update a particular record, we'll use a PUT mapping:

@PutMapping(value = "/{id}")
public Candidate update(@PathVariable String id, @RequestBody Candidate updatedCandidate) {
    Candidate candidate = candidateRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException());
    candidate.setName(updatedCandidate.getName());
    candidate.setExp(updatedCandidate.getExp());
    candidate.setEmail(updatedCandidate.getEmail());
    return candidateRepository.save(candidate);
}

We first check whether the Candidate with the given id is present or not. If not, we return a 404 status, otherwise we update the whole object and save it using the save() method:

Delete

Now, let's delete a particular record by using the DELETE mapping:

@DeleteMapping(value = "/{id}")
@ResponseStatus(code = HttpStatus.ACCEPTED)
public void delete(@PathVariable String id) {
    Candidate candidate = candidateRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException());
    candidateRepository.delete(candidate);
}

We used the delete() method on the candidateRepository to delete the entry:

Custom Query Methods

We can add some methods to our CandidateRepository to have some additional functionality based on our business requirements:

public interface CandidateRepository extends MongoRepository<Candidate, String> {

    Optional<Candidate> findByEmail(String email);

    List<Candidate> findByExpGreaterThanEqual(double exp);

    List<Candidate> findByExpBetween(double from, double to);
}

Above, we added search functionality based on email and experience. All we need to do is follow a naming convention laid down by Spring Data.

After the findBy() method we write the name of the attribute in camel case, followed by any other restriction that we may want to enforce. The arguments to the method should match the where clause expectation. Spring Data will create actual queries for you during the startup of the application by using this interface.

Let's use this in our controller:

@GetMapping("/searchByEmail")
public Candidate searchByEmail(@RequestParam(name = "email") String email) {
    return candidateRepository.findByEmail(email)
        .orElseThrow(() -> new ResourceNotFoundException());

}

@GetMapping("/searchByExp")
public List<Candidate> searchByExp(@RequestParam(name = "expFrom") Double expFrom, @RequestParam(name = "expTo", required = false) Double expTo) {
    List<Candidate> result = new ArrayList<>();
    if (expTo != null) {
        result = candidateRepository.findByExpBetween(expFrom, expTo);
    } else {
        result = candidateRepository.findByExpGreaterThanEqual(expFrom);
    }
    return result;
}

Conclusion

In this article, we've covered how to use Spring Data MongoDB to connect to a MongoDB server. We first created a MongoDB server in the cloud using MongoDB Atlas and then used Spring Data to connect to it. After that, we performed some simple CRUD operations as well as wrote some custom queries.

As always, the code for the examples used in this article can be found on GitHub.

Was this article helpful?

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms