Spring Boot is an auto-configured microservice-based web framework that provides built-in features for security and database access.
With Spring boot, we can quickly create stand-alone applications without having to make too many configuration changes (as we will see later). MongoDB is the most popular NoSQL database because of the ease with which data can be stored and retrieved. Combining Spring Boot and MongoDB results in applications that are fast, secure, reliable, and require minimum development time.
This tutorial demonstrates how we can integrate Spring Boot with MongoDB using the Spring Data MongoDB API.
Spring Boot creates quick production-ready applications. MongoDB and Spring Boot interact using the MongoTemplate class and MongoRepository interface.
Spring Boot MongoDB configuration using both approaches needs only a few lines of code.
Spring is an application framework for Java web applications. It is based on the MVC (Model-View-Controller) framework. Spring’s dependency injection handles features like database access, security, initialization, and so on, so that developers can focus on the business logic.
Spring Boot is built on top of the Spring framework mainly for REST APIs. Spring Boot requires very few configurations. It has four layers:
MongoDB is fast and can handle large amounts of structured and unstructured data, making it a database of choice for web applications. The Spring framework provides powerful connectors to easily perform database operations with MongoDB. Data is stored as BSON objects in MongoDB making data retrieval easy.
For this Spring Boot and MongoDB example tutorial, we are only concerned with the Persistence and Database layers. We want to keep the focus on CRUD operations, so we will run our program from an Integrated Development Environment (IDE). We will add Spring Boot MongoDB configurations to connect Spring Boot and MongoDB.
Let us create a hypothetical grocery shopping list for a user. We will do the following operations:
Read our article Getting started with MongoDB and Java to learn more about POJO and MongoDB documents mapping.
We need
The first step is to create the Spring Boot project using Spring Initializr with the following settings:
Select Maven Project with language as Java (8) and Spring Boot version 2.5.3. Also, add dependencies — we have added Spring Web and Spring Data MongoDB. Spring Web embeds Apache Tomcat server, REST, and Spring MVC in your application, managing all the common dependencies at one place.
We use Spring Data MongoDB dependency to access the data from our MongoDB Atlas cluster in this application.
Enter the project metadata (as shown in the image above) and select the JAR option. Using Spring Initializr takes care of creating a pom.xml file. Maven uses pom.xml to download the required dependencies.
With this, we are ready with all the settings. Next, we can click on the Generate button, which will generate all the necessary files for bootstrapping the Spring Boot project. The browser will automatically download a ZIP file.
Once the ZIP file is downloaded, unzip the project. Open the project from the IDE. You can see a project structure similar to:
We can see that the dependencies we added are present in the pom.xml as artifactId:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
...
</dependencies>
...
</project>
Refer to github for complete code.
It is time to create the content in the src/main/java folder.
Our model here is the POJO or the GroceryItem class.
Let’s create a package named com.example.mdbspringboot.model and add the class GroceryItem.java.
We use the annotation @Document to set the collection name that will be used by the model. If the collection doesn’t exist, MongoDB will create it.
@Document("groceryitems")
public class GroceryItem {
@Id
private String id;
private String name;
private int quantity;
private String category;
public GroceryItem(String id, String name, int quantity, String category) {
super();
this.id = id;
this.name = name;
this.quantity = quantity;
this.category = category;
}
}
Using the Eclipse Source -> Generate Getters and Setters option, we can create getters and setters for this code. Specify the MongoDB document’s primary key _
id using the @Id annotation. If we don’t specify anything, MongoDB will generate an _
id field while creating the document.
The API implementation happens in the repository. It acts as a link between the model and the database, and has all the methods for CRUD operations.
Let’s create a package called com.example.mdbspringboot.repository to store all the repository files.
We first create an ItemRepository public interface which extends the MongoRepository interface.
public interface ItemRepository extends MongoRepository<GroceryItem, String> {
@Query("{name:'?0'}")
GroceryItem findItemByName(String name);
@Query(value="{category:'?0'}", fields="{'name' : 1, 'quantity' : 1}")
List<GroceryItem> findAll(String category);
public long count();
}
The first method, findItemByName, requires a parameter for the query, i.e., the field to filter the query by. We specify this with the annotation @Query. The second method uses the category field to get all the items of a particular category. We only want to project the field’s name and quantity in the query response, so we set those fields to 1. We reuse the method count() as it is.
Now, we are ready to create our Spring Application and run the methods to see what happens.
To connect to MongoDB Atlas, we specify the connection string in the application.properties file in the src/main/resources folder. The connection string for a cluster can be found in the Atlas UI. There is no need to write connection-related code in any other file. Spring Boot takes care of the database connection for us.
spring.data.mongodb.uri=mongodb+srv://<username>:<pwd>@<cluster>.mongodb.net/mygrocerylist
spring.data.mongodb.database=mygrocerylist
We are also specifying the database name here — if it doesn’t exist, MongoDB will create one.
In this Spring Boot MongoDB example, we are not using the Controller and the View. We will use a CommandLineRunner to view the output on the console.
Create the main class MdbSpringBootApplication.java in the root package com.example.mdbspringboot:
@SpringBootApplication
@EnableMongoRepositories
public class MdbSpringBootApplication implements CommandLineRunner{
@Autowired
ItemRepository groceryItemRepo;
public static void main(String[] args) {
SpringApplication.run(MdbSpringBootApplication.class, args);
}
Our class MdbSpringBootApplication implements the CommandLineRunner interface to run the spring application. ItemRepository is Autowired, allowing Spring to find it automatically. Spring initializes the Application Context using the @SpringBootApplication annotation. We also activate the Mongo Repositories using @EnableMongoRepositories. Our project structure should be similar to the below structure now:
Let’s now add the repository methods to the main class for CRUD operations:
In order to create new documents, we will use the save method. The save method is available to us through the SimpleMongoRepository class, which implements the MongoRepository interface. Our ItemRepository interface extends MongoRepository.
The save method will take a GroceryItem object as a parameter. Here, we will create five grocery items (documents) and save them into MongoDB using the save method.
//CREATE
void createGroceryItems() {
System.out.println("Data creation started...");
groceryItemRepo.save(new GroceryItem("Whole Wheat Biscuit", "Whole Wheat Biscuit", 5, "snacks"));
groceryItemRepo.save(new GroceryItem("Kodo Millet", "XYZ Kodo Millet healthy", 2, "millets"));
groceryItemRepo.save(new GroceryItem("Dried Red Chilli", "Dried Whole Red Chilli", 2, "spices"));
groceryItemRepo.save(new GroceryItem("Pearl Millet", "Healthy Pearl Millet", 1, "millets"));
groceryItemRepo.save(new GroceryItem("Cheese Crackers", "Bonny Cheese Crackers Plain", 6, "snacks"));
System.out.println("Data creation complete...");
}
In this application, we are performing four different read operations:
// READ
// 1. Show all the data
public void showAllGroceryItems() {
groceryItemRepo.findAll().forEach(item -> System.out.println(getItemDetails(item)));
}
// 2. Get item by name
public void getGroceryItemByName(String name) {
System.out.println("Getting item by name: " + name);
GroceryItem item = groceryItemRepo.findItemByName(name);
System.out.println(getItemDetails(item));
}
// 3. Get name and quantity of a all items of a particular category
public void getItemsByCategory(String category) {
System.out.println("Getting items for the category " + category);
List<GroceryItem> list = groceryItemRepo.findAll(category);
list.forEach(item -> System.out.println("Name: " + item.getName() + ", Quantity: " + item.getQuantity()));
}
// 4. Get count of documents in the collection
public void findCountOfGroceryItems() {
long count = groceryItemRepo.count();
System.out.println("Number of documents in the collection: " + count);
}
We can create a helper method to display the output of read operations in a readable format:
// Print details in readable form
public String getItemDetails(GroceryItem item) {
System.out.println(
"Item Name: " + item.getName() +
", \nQuantity: " + item.getQuantity() +
", \nItem Category: " + item.getCategory()
);
return "";
}
Suppose we change our mind and like the word “munchies” better in place of “snacks” for our grocery list. We would need to change all the documents where the category is “snacks.” To do that, we have to first fetch all the documents of the category “snacks,” set the category to “munchies,” and then save all the documents.
public void updateCategoryName(String category) {
// Change to this new value
String newCategory = "munchies";
// Find all the items with the category snacks
List<GroceryItem> list = groceryItemRepo.findAll(category);
list.forEach(item -> {
// Update the category in each document
item.setCategory(newCategory);
});
// Save all the items in database
List<GroceryItem> itemsUpdated = groceryItemRepo.saveAll(list);
if(itemsUpdated != null)
System.out.println("Successfully updated " + itemsUpdated.size() + " items.");
}
If, instead of updating an item or category, we want to delete a grocery item off of our list, we can. We can delete the grocery item with a particular ID using the pre-defined deleteById method.
// DELETE
public void deleteGroceryItem(String id) {
groceryItemRepo.deleteById(id);
System.out.println("Item with id " + id + " deleted...");
}
To delete all the items, we can use the groceryItemRepo.deleteAll(); method. Deleting all the documents will not delete the collection.
Next, we implement the CommandLineRunner.run() method to call the above methods:
public void run(String... args) {
System.out.println("-------------CREATE GROCERY ITEMS-------------------------------\n");
createGroceryItems();
System.out.println("\n----------------SHOW ALL GROCERY ITEMS---------------------------\n");
showAllGroceryItems();
System.out.println("\n--------------GET ITEM BY NAME-----------------------------------\n");
getGroceryItemByName("Whole Wheat Biscuit");
System.out.println("\n-----------GET ITEMS BY CATEGORY---------------------------------\n");
getItemsByCategory("millets");
System.out.println("\n-----------UPDATE CATEGORY NAME OF SNACKS CATEGORY----------------\n");
updateCategoryName("snacks");
System.out.println("\n----------DELETE A GROCERY ITEM----------------------------------\n");
deleteGroceryItem("Kodo Millet");
System.out.println("\n------------FINAL COUNT OF GROCERY ITEMS-------------------------\n");
findCountOfGroceryItems();
System.out.println("\n-------------------THANK YOU---------------------------");
}
The system.out statements are only for beautification of the output.
Here is the expected output when we run the program:
-------------CREATE GROCERY ITEMS-------------------------------
Data creation started...
Data creation complete...
----------------SHOW ALL GROCERY ITEMS---------------------------
Item Name: Whole Wheat Biscuit,
Item Quantity: 5,
Item Category: snacks
Item Name: XYZ Kodo Millet healthy,
Item Quantity: 2,
Item Category: millets
Item Name: Dried Whole Red Chilli,
Item Quantity: 2,
Item Category: spices
Item Name: Healthy Pearl Millet,
Item Quantity: 1,
Item Category: millets
Item Name: Bonny Cheese Crackers Plain,
Item Quantity: 6,
Item Category: snacks
--------------GET ITEM BY NAME-----------------------------------
Getting item by name: Whole Wheat Biscuit
Item Name: Whole Wheat Biscuit,
Item Quantity: 5,
Item Category: snacks
-----------GET ITEMS BY CATEGORY---------------------------------
Getting items for the category millets
Name: XYZ Kodo Millet healthy, Quantity: 2
Name: Healthy Pearl Millet, Quantity: 1
-----------UPDATE CATEGORY NAME OF SNACKS CATEGORY----------------
Successfully updated 2 items.
----------DELETE A GROCERY ITEM----------------------------------
Item with id Kodo Millet deleted...
------------FINAL COUNT OF GROCERY ITEMS-------------------------
Number of documents in the collection = 4
-------------------THANK YOU---------------------------
To perform update operations using a particular field, we can also use the MongoTemplate class. It provides out-of-the-box functionalities present in the org.springframework.data.mongodb.core.query package. We don’t need to write many lines of code and the update can be done in a single database interaction. We can use MongoTemplate for more complex operations like aggregations, as well (not in scope for this tutorial).
To use the MongoTemplate class for the update, we should create a custom repository where we build the update query.
Let’s write a method to update the quantity of a grocery item.
Create an interface CustomItemRepository:
public interface CustomItemRepository {
void updateItemQuantity(String name, float newQuantity);
}
We can add as many methods as we need in the interface and provide the implementations in the CustomItemRepositoryImpl class:
@Component
public class CustomItemRepositoryImpl implements CustomItemRepository {
@Autowired
MongoTemplate mongoTemplate;
public void updateItemQuantity(String name, float newQuantity) {
Query query = new Query(Criteria.where("name").is(name));
Update update = new Update();
update.set("quantity", newQuantity);
UpdateResult result = mongoTemplate.updateFirst(query, update, GroceryItem.class);
if(result == null)
System.out.println("No documents updated");
else
System.out.println(result.getModifiedCount() + " document(s) updated..");
}
}
Since MongoTemplate is @Autowired, Spring will inject the object dependency. In addition, @Component annotation will allow Spring itself to detect the CustomItemRepository interface.
The next step is to call this method from our main class. Similar to how we declared the groceryItemRepo, we should declare our customRepo:
@Autowired
CustomItemRepository customRepo;
Next, write the method in the main class that calls our customRepo method:
// UPDATE
public void updateItemQuantity(String name, float newQuantity) {
System.out.println("Updating quantity for " + name);
customRepo.updateItemQuantity(name, newQuantity);
}
Add the above method in the run method to call it when the application is executed:
System.out.println("\n-----------UPDATE QUANTITY OF A GROCERY ITEM------------------------\n");
updateItemQuantity("Bonny Cheese Crackers Plain", 10);
You should get the result as:
-----------UPDATE QUANTITY OF A GROCERY ITEM------------------------
Updating quantity for Bonny Cheese Crackers Plain
1 document(s) updated..
Like we mentioned earlier, we were able to do the update in a single database transaction as compared to with MongoRepository, where we had to do three operations, i.e., find, set, and then save. MongoTemplate also provides the updateMulti() method, to update multiple documents in a single go.
Through this article, we demonstrated that MongoDB Spring Boot integration is easy using:
Connection to MongoDB Atlas does not require any other code. MongoDB Atlas is a convenient way to store and access data anywhere. Visit the MongoDB Atlas page to get started, if you would like to work along with this tutorial yourself. If you have already worked along with us in this simple tutorial and understand how to use Spring Boot with MongoDB, read REST APIs with Java, Spring Boot, and MongoDB to learn how to perform advanced operations and appreciate the full benefits of using a Spring Boot application by using the business (controller) and presentation (view) layers as well. Additionally, you can check out the full MongoDB University course on MongoDB for Java Developers.
MongoDB can connect to Spring Boot in two ways — the MongoRepository interface and the MongoTemplate class. MongoRepository extends the CrudRepository interface that contains methods to perform basic CRUD operations. For writing custom queries and aggregations, and for a finer control over query filters, we can use the MongoTemplate class. MongoTemplate class implements interfaces that support operations like aggregation, find, update, upsert, index, remove, and more.
Spring Boot framework is used to create production-ready web applications with default configurations. Developers need not write extensive code. Spring Boot significantly reduces the development time. It automatically adds commonly used libraries for web applications, such as:
for easier dependency management.
Spring Boot also has embedded servlet container support. We can run Java programs as a standalone application by adding the spring-boot-starter-web dependency in pom.xml.
A common Spring Boot with MongoDB CRUD example could be a grocery shopping list of a user. A user may want to
The CRUD operations are done using MongoRepository and MongoTemplate. We can use both by adding the spring-boot-starter-data-mongodb dependency in pom.xml.