DEV Community

Ricardo Mello
Ricardo Mello

Posted on

Spring Data Unlocked: Getting Started With Java and MongoDB

Description: Learn how to integrate MongoDB with Spring Data in a simple and easy way. This first part of the series covers getting started with Spring Data, modeling your data, and using MongoRepository to perform basic CRUD operations.


If you are a Java developer who is passionate about MongoDB and Spring, I have good news: You're in the right place.

This series of articles will explore how to integrate MongoDB with Spring Data in a very simple and easy way. The main idea is to have a vision of how to take the first step and, at the same time, evolve our knowledge by exploring the capabilities that Spring Data offers us.

By the end of this series, you'll have a clear understanding of the differences between MongoRepository and MongoTemplate, know when to use each one, and be able to create complex queries while improving their performance by using indexes.

Pre-requisites

Spring Data

Spring Data is a solution that simplifies data access in databases like MongoDB. Its main goal is to provide an easy and consistent approach to working with data while preserving the unique characteristics of MongoDB. And, of course, all of this is within the Spring ecosystem.

We can perform basic CRUD operations easily, and the framework can automatically create queries based on method names, reducing the amount of boilerplate code we need to write. Some key points:

  • Repository support: Spring offers us a way to communicate with MongoDB at a high level of abstraction, providing very simple CRUD operations to use.
  • Query derivation: We can create quick queries using the method signature, which is ideal for those who want agility.
  • Mapping and serialization: Simplify the mapping and serialization of objects through annotations.
  • Aggregation framework: Spring provides support for the aggregation framework through annotations and also through the aggregation class itself.
  • Auto-index-Creation: We have the option to work with indexes managed by Spring itself (we will explore this in more detail in the second series of this article).

The business model

To explore the capabilities of Spring, we will define a data model where we will work with transactions between bank accounts. Our model will ultimately deliver a document like this:

{
   "id": "672182814338f60133ee26e1",
   "transactionType": "Debit",
   "amount": 888.0,
   "currency": "USD",
   "status": "In Progress",
   "description": "Transfer to Ricardo",
   "createdAt": "2024-10-09T14:00:00",
   "accountDetails": {
       "originator": {
           "accountNumber": "2376543213",
           "name": "Maria",
           "bank": "Bank G"
       },
       "beneficiary": {
           "accountNumber": "2234987651",
           "name": "Ricardo Mello",
           "bank": "Bank V"
       }
   }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we have several types of fields and subdocuments, which will be very useful for exploring our queries.

The Java application

To build our application, let's use Spring Initializr using the following options:

  • Project: Gradle - Kotlin
  • Language: Java
  • Spring Boot: The latest version

We'll also add the following dependencies:

  • Lombok: This simplifies code by automatically generating boilerplate code, like getters and setters.
  • Spring Web: We'll use this to create endpoints for our application.
  • Spring Data MongoDB: We'll use this to store data in flexible, JSON-like (BSON) documents.

After that, simply click on "Generate" and open the project in your favorite IDE (I'm using IntelliJ).

Setting up the application

To connect to MongoDB using Spring, we'll use the application.properties file and define the following variables:

spring.application.name=SpringShop
spring.data.mongodb.uri=<YOUR_CONNECTION_STRING>
spring.data.mongodb.database=springshop
Enter fullscreen mode Exit fullscreen mode

The transaction class

Next, we will create our transaction class (based on our business model). To do this, simply create a class as follows:

package com.mongodb;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.LocalDateTime;

@Document(collection = "transactions")
@Data
public class Transaction {
   @Id
   private String id;
   private String transactionType;
   private Double amount;
   private String currency;
   private String status;
   private String description;
   private LocalDateTime createdAt;
   private AccountDetails accountDetails;

   public record AccountDetails(Originator originator, Beneficiary beneficiary) {}
   public record Originator(String accountNumber, String name, String bank) {}
   public record Beneficiary(String accountNumber, String name, String bank) {}
}
Enter fullscreen mode Exit fullscreen mode

The transaction class is quite simple and includes the information from our model. Here, we have three annotations that I would like to discuss:

  • @Document: This indicates to Spring that this class is a MongoDB document.
  • @Data: This is a Lombok annotation that generates boilerplate code such as getters, setters, toString, equals, and hashCode methods.
  • @Id: This will automatically generate an _id (ObjectId) for us.

MongoTemplate and MongoRepository

Before we move on to communication with MongoDB, we need to talk about the level of abstraction. MongoTemplate and MongoRepository are both part of Spring Data and offer different levels of abstraction for interacting with MongoDB.

While MongoRepository is designed for those who want faster development, MongoTemplate is better suited for those who need more flexibility.

MongoTemplate MongoRepository
Low-level abstraction High-level abstraction
When you require precise control over MongoDB operations When you want Spring Data to automatically create queries for you
When you need to run complex queries or aggregations When you need basic CRUD operations
When you need to do custom updates or bulk actions When you prefer simplicity and convention over configuration

Alright, now that we can differentiate between them, we can choose one (or both) to work with.

TransactionRepository

Now, it's time to create our class that will handle communication with MongoDB. In this first part, we'll focus on simplicity and efficiency and use the MongoRepository interface provided by Spring. To do this, create a class called TransactionRepository as shown below:

package com.mongodb;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TransactionRepository extends MongoRepository<Transaction, String> {}
Enter fullscreen mode Exit fullscreen mode

In this example, we're extending the MongoRepository interface, which allows us to inherit several powerful features provided by Spring Data that we can apply to our Transaction entity.

Service and controller

Let's take advantage of the high level of abstraction provided by MongoRepository and create two methods for insertion and querying. To do this, we will create a TransactionService class:

package com.mongodb;

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class TransactionService {
   private final TransactionRepository transactionRepository;

   public TransactionService(TransactionRepository transactionRepository) {
       this.transactionRepository = transactionRepository;
   }

   List<Transaction> getTransactions() {
       return transactionRepository.findAll();
   }

   public Transaction save(Transaction transaction) {
       return transactionRepository.save(transaction);
   }
}
Enter fullscreen mode Exit fullscreen mode

Now, to finish this first part of our series, we will create our controller to test the insert and find operations. To do this, simply execute the following code:

package com.mongodb;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/transactions")
public class TransactionController {
   private final TransactionService transactionService;

   public TransactionController(TransactionService transactionService, TransactionRepository transactionRepository) {
       this.transactionService = transactionService;
   }

   @GetMapping
   public List<Transaction> getTransactions() {
       return transactionService.getTransactions();
   }

   @PostMapping
   public ResponseEntity<Transaction> create(@RequestBody Transaction transaction) {
       return ResponseEntity.status(HttpStatus.CREATED).body(transactionService.save(transaction));
   }
}
Enter fullscreen mode Exit fullscreen mode

Notice: We could create some classes to better handle our traffic objects. However, we will focus on simplicity for now.

Testing our endpoint

If you haven't run the application yet, remember to update the connection string to your database. With the application running, let's insert a document using the following cURL command:

curl --location 'http://localhost:8080/transactions' \
--header 'Content-Type: application/json' \
--data '{
    "transactionType": "Transfer",
    "amount": 1500.5,
    "currency": "USD",
    "status": "Completed",
    "description": "Transfer to Ricardo",
    "createdAt": "2024-10-09T14:00:00.000Z",
    "accountDetails": {
        "originator": {
            "accountNumber": "9876543210",
            "name": "Maria Faria",
            "bank": "Bank A"
        },
        "beneficiary": {
            "accountNumber": "1234987654",
            "name": "Ricardo Mello",
            "bank": "Bank B"
        }
    }
}'
Enter fullscreen mode Exit fullscreen mode

If everything goes well, we can query our records using the findAll method:

curl --location 'http://localhost:8080/transactions'
Enter fullscreen mode Exit fullscreen mode

I'm using Postman to perform the cURL requests and see the results. As you can see, our inserted document matches the model we defined initially.

Conclusion

In this first part of the Spring Data Unlocked series, we learned how to get started with Spring Data and MongoDB. The article demonstrates how to create a project from scratch and integrate it in a simple and straightforward way. We modeled our entity that will be used in the upcoming articles, where we will explore the capabilities of Spring with aggregations and more complex queries.

If you have any questions, feel free to leave them in the comments.

The complete code is available in mongo-developer GitHub.

Top comments (0)