Link Search Menu Expand Document

This article assumes that you have worked through the steps in MongoDB: New Database to set up a MongoDB deployment on the site https://cloud.mongodb.com, with:

  • a cluster than can accessed from 0.0.0.0/0, i.e. from any IP address
  • a username and password with read/write access
  • a database called database and a collection called posts

From that starting point, we’ll explain:

  • How to set up a Java class that represents a single Document in the collection
  • How to set up a Java class that represents the entire collection
  • How to use those classes in backend controller methods to do CRUD operations on the collection.

Setting up the pom.xml

To work with MongoDB, we should add the following dependency to our pom.xml. Check the URL in the comment for the latest version; at this writing that is 2.6.3 but it may be later by the time you are reading this.

   <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb -->
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-mongodb</artifactId>
   </dependency>

If using the the spring-boot-starter-parent as many of our projects are, i.e. the code below, then it is not necessary to specify a version; that will be determined by the version in the <parent> element:

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

Otherwise consult the URL in the comment to determine the latest version to use.

Adjusting our collections

If you followed the MongoDB: New Database to set up your database, you have:

  • a database named database
  • in that database, a collection named posts

To see that, here are the navigation steps you need:

  • Select your organization:

    )

  • Select your project:

    )

  • When you see the cluster where your project is deployed, you can either click the Browse Collections button:

    )

    Or you can click into the cluster, and then select the Collections tab:

    )

When you get there, you see this:

)

Renaming a collection

The first thing we will try to do is rename posts to reddit_posts.

Note that as far as I can tell, at the current time, there is no way to rename a collection in the web user interface.

Fortunately, since our collection is empty, we can just drop it and recreate it.

To drop the collection posts, hover over posts and you’ll see a trash can icon pop up:

)

Then, click the trash can and a confirmation modal appears. Type in posts and click Drop

)

That returns us to a state of having no collections, which looks like this:

)

From here, we’ll click Add your own data and create two collections: reddit_posts and students. The user interface here is a bit tricky, so I’ll walk you through it. First click Add my own data which gets us here, where you can fill in database and reddit_posts

)

That results in this:

)

Then, to create the second collection (or additional ones after that), hover over the name database, and you’ll see a plus sign pop up. Click that to add the second collection.

)

Enter students as the name of the second collection:

)

Now we have a database named database and two collections: reddit_posts and students. We are ready to start doing Java coding.

Configuration for a connection to MongoDB

The place that you are supposed to set up Mongo DB Connection URI is in .env file (copied from .env.SAMPLE) as the value of the variable MONGODB_URI.

Here’s where to get the value of MONGODB_URI:

In the left hand navigation on the https://cloud.mongodb.com site, find Database in the left nav, and get to this page:

)

Then, click where it says Connect. That brings up this Modal. Click the “Connect Your Application” link:

)

That brings up this. Do not worry about setting the programming language and version.

  • That matters only if you are looking for example code, and it turns out the code the MongoDB site offers for Java is low level code that is not appropriate for Spring Boot anyway.
  • The part that does matter, the connection string, is the same across all programming languages.

)

Copy the connection string, and paste it in as the value for MONGODB_URI. But you are not finished. There is still the step of filling in the password, and replacing the myFirstDatabase with the correct name of the database.

MONGODB_URI=mongodb+srv://readWrite01:<password>@cluster0.v6z4u.mongodb.net/myFirstDatabase?retryWrites=true&w=majority

First, let’s deal with the database, since that’s easy. If you called your database database, then just change myFirstDatabase to database, like this:

MONGODB_URI=mongodb+srv://readWrite01:<password>@cluster0.v6z4u.mongodb.net/myFirstDatabase?retryWrites=true&w=majority

Now, that places where it says <password> needs to be replaced with the correct password. To do this, navigate back to Database Access in the left nav:

)

Click Edit beside the user you are going to use.

As an aside: note that you can have multiple users with different privilege levels, so if your application only needs read access to the data, it may be better to configure a read only user for your web app, and the upload data using a different user with a script. (We’ll provide an example of an uploader script in a different article.) For now, though, we’ll use a user with both read and write privileges.

Clicking Edit next to a user brings up this:

)

Click where it says “Edit password”. That opens up this:

)

Note that it is not possible to look up the password, only to change it. So, we are going to autogenerate a new-password, and then paste it into our .env file in place of password. (Note that example I’m using here is a fake one; so don’t bother trying to hack with it.)

Click Autogenerate, then Show, so you see something like this:

)

Now copy the value BkZGP3TZZJG0dAfE and paste it into your .env file in place of the password. Do not include the <> around <password>

  • Correct: MONGODB_URI=mongodb+srv://readWrite01:BkZGP3TZZJG0dAfE@cluster0.v6z4u.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
  • Incorrect: MONGODB_URI=mongodb+srv://readWrite01:<BkZGP3TZZJG0dAfE>@cluster0.v6z4u.mongodb.net/myFirstDatabase?retryWrites=true&w=majority

Now, the part that everyone always forgets: Do not just click the X or navigate away from the window! The password is not saved unless you scroll down to the bottom of the modal and click Update User!

)

But the documentation says spring.data.mongodb.uri ?

You may wonder: why MONGODB_URI when the Spring Boot documentation (and/or various tutorials) say that it should be set via spring.data.mongodb.uri?

The place to set up the connection string is indeed the application property spring.data.mongodb.uri; however, it is a bad idea to hard code usernames and passwords in the application.property file:

spring.data.mongodb.uri=DO-NOT-HARD-CODE-THE-URI-HERE

Instead, we set it up to pick up the value from an environment variable MONGODB_URI and put in a fake default value that can be used as a fallback so that the code will at least run to some extent if/when we fail to set a value:

spring.data.mongodb.uri=${MONGODB_URI:${env.MONGODB_URI:mongodb+srv://fakeUsername:fakePassword@cluster0.ulqcw.mongodb.net/fakeDatabase?retryWrites=true&w=majority}}

This says: try to get a property MONGODB_URI; failing that, try to get MONGODB_URI from the environment, failing that, use this hard coded fake default.

Note that the application will at least start up with fake values for the username, password, and database, but if the host in the URI does not exist, you may get a fatal error on startup (i.e. at the mvn spring-boot:run stage.)

Setting up a Document class

A MongoDB document is a representation of a JSON object. As such, it can be nested arbitrarily deep.

The same is true when setting up a Java object to represent a MongoDB document.

Suppose we have a simple JSON object, such as the following JSON object representing a student with a name and a perm:

{
   "first_name" : "Chris",
   "last_name" : "Gaucho",
   "perm" : 1234567
}

In this case, it is straightforward to make a class Student.java that represents objects of this type. But our first step will be to create a directory under src/main/java/ that is a sibling of our controllers, services, entities, etc. called documents, like this:

Before After
) )

Under that folder, create Student.java like this:

package edu.ucsb.cs156.example.documents;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

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


@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document(collection = "students")
public class Student {
    @Id
    private String id;

    private String firstName;
    private String lastName;
    private int perm;
}

Then, create a folder called collections:

)

In that folder, create a file StudentCollection.java like this:

package edu.ucsb.cs156.example.collections;

import java.util.Optional;

import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import edu.ucsb.cs156.example.documents.Student;

@Repository
public interface StudentCollection extends MongoRepository<Student, ObjectId> {
  Optional<Student> findOneByPerm(int perm);
  Optional<Student> findOneByFirstNameAndLastName(String firstName, String lastName);
}

Finally, we can create a controller, StudentsController.java

package edu.ucsb.cs156.example.controllers;

import edu.ucsb.cs156.example.collections.StudentCollection;
import edu.ucsb.cs156.example.documents.Student;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


@Api(description = "Students")
@RequestMapping("/api/students")
@RestController
@Slf4j
public class StudentsController extends ApiController {

    @Autowired
    StudentCollection studentCollection;

    @Autowired
    ObjectMapper mapper;

    @ApiOperation(value = "List all students")
    @PreAuthorize("hasRole('ROLE_USER')")
    @GetMapping("/all")
    public Iterable<Student> allStudents() {
        loggingService.logMethod();
        Iterable<Student> students = studentCollection.findAll();
        return students;
    }

    @ApiOperation(value = "Add a Student to the collection")
    @PreAuthorize("hasRole('ROLE_USER')")
    @PostMapping("/post")
    public Student postStudent(
            @ApiParam("firstName") @RequestParam String firstName,
            @ApiParam("lastName") @RequestParam String lastName,
            @ApiParam("perm") @RequestParam int perm) {
        loggingService.logMethod();

        Student student = new Student();
        student.setFirstName(firstName);
        student.setLastName(lastName);
        student.setPerm(perm);

        // OR

        // Student student = Student.builder()
        //         .firstName(firstName)
        //         .lastName(lastName)
        //         .perm(perm)
        //         .build();

        Student savedStudent = studentCollection.save(student);
        return savedStudent;
    }

}

This will give us endpoints that we can test with Swagger:

)

A POST gives us a new Student:

)

)

And a GET shows that the student was added to the Collection:

)

)

Finally, we can navigate to our Collection on https://cloud.mongodb.com and see that the data is in the Collection:

)

Next steps: A more complex collection

As you can see by the code above, it is relatively straightforward to set up a Document and a Collection for a simple flat JSON object.

However, for simple flat object with no nesting, an SQL database may be a better choice anyway. The power of MongoDB is really for representing hierarchical nested JSON data.

So in the next article, we’ll look at RedditPosts, which have several layers of complexity, and describe how to set up a Java class hierarchy to represent these objects.