DEV Community

Irvin Gil
Irvin Gil

Posted on

Getting started on Spring boot and MongoDB - Part 2

In the previous article, we worked on creating a new Spring Boot service, setting up MongoDB using Docker, and connecting the Spring Boot application to the MongoDB database.

In this article, we'll focus on setting up a basic GET endpoint in our Spring Boot application to read data from the MongoDB instance running on our local machine.

What you'll need

  1. Intellij IDE - for working with code implementation
  2. Postman - for making http calls and testing the endpoint

Creating the entity models

In order to work with MongoDB, we need to create a POJO to represent the data in the database. Each property of the class will represent a field in our document.

In the Spring Boot project, create a new directory under src/com.${project_name}/domain/student. Then, create a Student Java class and add the following code to it:

@Data
@Document                     // We implement this annotation to tell mongodb that this class represent a single document in our collection.
public class Student {

  @Id                         // Every document must have their own id, that is why we create am id property on the class.
  private String id;
  private String firstName;
  private String lastName;
  @Indexed(unique = true)
  private String email;       // We tell mongodb that this field must be unique. This will be one of our business rules on this project.
  private Gender gender;
  private Address address;
  private List<String> favouriteSubjects;
  private BigDecimal totalSpentInBooks;
  private LocalDateTime created;

  // Finally, we define a constructor for the class.
  public Student(String firstName, String lastName, String email, Gender gender, Address address, List<String> favouriteSubjects, BigDecimal totalSpentInBooks, LocalDateTime created) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
    this.gender = gender;
    this.address = address;
    this.favouriteSubjects = favouriteSubjects;
    this.totalSpentInBooks = totalSpentInBooks;
    this.created = created;
  }

Enter fullscreen mode Exit fullscreen mode

⚠️ Note: One of our business rule for this project is that every student must have a unique email address.

You can notice that we have Address and Gender classes that we haven't defined/created yet. So let's go ahead and create them.

 // The contents of Address.java. Please add the contents accordingly.

@Data                  // This lombok annotation allow us to have getters and setters for each properties of the class. Lombok manages this for us so we don't have to write boiletplate code.
@AllArgsConstructor    // This lombok annotation allows us to create a new instance of the Address object without passing any arguments.
public class Address {

  private String country;
  private String city;
  private String postCode;

}

// The contents of Gender.java.
public enum Gender {
  MALE, FEMALE
}
Enter fullscreen mode Exit fullscreen mode

Create separate files for each Java class. It is not best practice to combine different Java objects/classes that have entirely different contexts.

Once the two classes have been created, we can resolve the error appearing in the Student class.

MVC and implementation

Spring MVC is a powerful framework for building web applications in Java. It follows the Model-View-Controller design pattern, which separates the application logic into three interconnected components: the model (data), the view (user interface), and the controller (business logic). This separation allows for more organized and maintainable code.

For more information, you can explore the official documentation:

Repository

Create a new Java interface under the directory src/com.${project_name}/student and name it StudentRepository. Implement the following code in the newly created interface.

public interface StudentRepository extends MongoRepository<Student, String> {
  Optional<Student> findStudentByEmail(String email); // Here, we are implementing a method that would allow us to fetch a student data that matches the email that we provide as parameter.
}
Enter fullscreen mode Exit fullscreen mode

We are leveraging the Mongo repository to allow us to interact with the database.

MongoRepository is an interface provided by Spring Data in the package org.springframework.data.mongodb.repository.MongoRepository extends the PagingAndSortingRepository and QueryByExampleExecutor interfaces that further extend the CrudRepository interface. MongoRepository provides all the necessary methods which help to create a CRUD application and it also supports the custom derived query methods.

Service

We define the business logic of our application in the service classes. The service class acts as a bridge between the controller and repository classes. This is a best practice design when developing Spring Boot applications. The controller must never interact directly with the database implementation, which is the repository in this case.

Create a new class at the same level as the StudentController and name it StudentService. Then, add the following implementation.

@Service             // Service classess need to be annotated with this in order for spring boot to determine that this a service class.
@AllArgsConstructor  // Since we are requireing an instance of the StudentRepository class here, we are levering lombok to do the injection for us via constructor.
public class StudentService {

  private final StudentRepository studentRepository;

  public List<Student> getAllStudents() { // This method will be the one that is accessed by the controller so that i can fetch data from the mongodb.
    return studentRepository.findAll();   // with this implementation, we are following the best practice of not having the controller class directly call the database implementation (repository).
  }
}
Enter fullscreen mode Exit fullscreen mode

Controller

In Spring MVC, we define the endpoints of our application in the controller.

Create a StudentController Java class at the same level as the Student class. Then, implement the following code.

@RestController                                 // All controller classes need to be annotated with this, so that spring will know that there are endpoints on this controller class.
@AllArgsConstructor                             // We are injecting the StudentService instance into this class, and we are leveraging lombok's annotation to do this for us instead of having to implement a constructor and then annotate it with @Autowired annotation.
public class StudentController {

  private final StudentService studentService;  // Inject the instance of StudentService

  @GetMapping                                   // This mapping means that we are mapping this endpoint to a GET HTTP request. 
  @RequestMapping("api/v1/students")            // Here is where we difine the endpoint URI.
  public List<Student> fetchAllStudents() {
    return studentService.getAllStudents();
  }
}

Enter fullscreen mode Exit fullscreen mode

Putting it all together

Implementing code to populate the database

For this tutorial, we need to pre-populate the data in our local MongoDB so that we have data to test and fetch.

Open the class of your application with the main function and add this code implementation under the same class.

  /*
   * @info: Succeeding application runs will throw and error when  `auto-index-creation` is enabled
   * */
  @Bean
  CommandLineRunner runner(StudentRepository studentRepository, MongoTemplate mongoTemplate) {
    return args -> {
      Address address = new Address("England", "London", "NE9");
      Student student = new Student("Jamila", "Ahmed", "jahmed@gmail.com", Gender.FEMALE, address, List.of("Computer Science"), BigDecimal.TEN, LocalDateTime.now());
      /*
       * @note: We implement a find criteria to search for existing students on the database.
       * It should only insert new student if no other students are found with the same email.
       *
       */
      scanAndInsertInitialDataUsingRepository(studentRepository, student);
    };
  }

  private static void scanAndInsertInitialDataUsingRepository(StudentRepository studentRepository, Student student) {
    studentRepository.findStudentByEmail(student.getEmail()).ifPresentOrElse(success -> {

      System.out.println(success + " already exists.");
    }, () -> {

      System.out.println("Inserting student " + student);
      studentRepository.insert(student);
    });
  }

Enter fullscreen mode Exit fullscreen mode

The code implementation above does the following:

  1. Builds a new Student object with the provided details.
  2. Interacts with MongoDB to check if there's an existing user with the specified email address. Remember our business rule for this tutorial: "every student must have a unique email address."
  3. If there is no student with the specified email address, the new student object is persisted to MongoDB. Otherwise, a log message is printed, and no further action is taken."

Testing our application

What it looks like

high-level-overview-of-components

When you open the mongo-express app in your browser, you are actually interfacing with the MongoDB database and viewing the data directly. With Express, you can also make edits to the contents of MongoDB. This means you can edit, create, and delete documents as well as collections.

When you make an HTTP request with Postman to the Spring Boot app, you are not interfacing directly with MongoDB; instead, the Spring Boot app only exposes certain functionalities that are allowed for the client. It does this via endpoints. Each CRUD operation will have an individual endpoint that allows the client to perform specific functionalities. What we have created is a REST API that exposes the functionality of fetching all the students from MongoDB.

Disclaimer: There's a whole field of knowledge that deals with RESTful API designs, and I admit that our GET endpoint needs some improvements. But for the sake of simplicity, we'll just implement the specification to allow us to get all the student data from the database.

Start the application

Open your terminal and run the MongoDB and application with the following commands:

  • make start-db
  • make start-app

⚠️ Note: Wait for the MongoDB database to start successfully before running the spring boot app. Or else error messages and issues will show.

Performing the postman request

Open postman and create a new request with the following details:

Request-Type: GET
URI: http://localhost:8080/api/v1/students
Enter fullscreen mode Exit fullscreen mode

ℹ️ Info: Make sure that the application and database are successfully running before making the postman request.

postman-request

You can also perform a curl request directly from your terminal as such:

curl --location 'http://localhost:8080/api/v1/students'
Enter fullscreen mode Exit fullscreen mode

curl-request

Conclusion

In this article, we continued our work by implementing the entity models and discussing common best practices for implementing Spring Boot MVC. We added implementation code to pre-populate our MongoDB database and successfully created a simple GET endpoint to fetch that data through an HTTP call to our Spring Boot application. Congratulations! By now, you have an understanding of how to build a simple Spring Boot backend service that leverages MongoDB as the database source.

I plan to create a new article to develop the remaining endpoints to achieve a complete CRUD operation for our backend service. This means creating additional endpoints to perform the other CRUD functionalities. Here is the GitHub repository for this Spring Boot project, where you can follow the configurations and implementations discussed in this tutorial.

I hope you learned something new from this small tutorial series.

Top comments (0)