Blog
navigate_next
Java
Spring Boot MongoDB
Gaurav Sharma
August 22, 2024

Relational or Non-Relational? The Great Database Debate

So, you’ve got a project, and you’re trying to figure out which type of database to use—relational or non-relational?

Here’s the deal: neither is inherently better than the other. It really comes down to what you’re working with and what you need.

Relational Databases

Relational databases (MySQL, PostgreSQL, and Oracle) are the veterans of the database world. They’ve been around forever and are fantastic for handling structured data. By structured, I mean data that’s nicely organized into tables with rows and columns, kind of like an Excel spreadsheet. You’ve got your tables, and they’re connected to each other through keys, which helps keep everything in sync.

Example of Structured Data:

Say you’re running a small online store. Your relational database might have a <span class="pink">Customers</span> table that looks something like this:

Everything here is neat and tidy. Every customer has their own ID, and all their information fits perfectly into these predefined columns. Relational databases shine in scenarios like this because they keep your data clean, organized, and easy to query.

Non-Relational Databases

Now, let’s talk about non-relational databases like MongoDB, Cassandra, and DynamoDB. They’re built to handle unstructured or semi-structured data—stuff that doesn’t fit so neatly into rows and columns. Instead, these databases use formats like JSON, key-value pairs, or even graphs to store data.

Example of Unstructured Data:

On a social media platform like X, where users post tweets. Here's an example of what a tweet might look like in a non-relational database:

	
{
  "TweetID": "12345",
  "User": "user789",
  "Content": "Loving the new features in the app! #awesome",
  "Timestamp": "2024-08-17T14:52:00Z",
  "Likes": 150,
  "Retweets": 30,
  "Mentions": ["user123", "user456"],
  "Hashtags": ["#awesome"]
}

Above example of a tweet is just for a basic text tweet. But the challenge is that tweets are unpredicatable and can also include attachments like audio, video, or images, making the data more complex and unpredictable. Non-relational databases are ideal for handling this kind of unstructured data. They allow you to store diverse and varying information without needing a fixed schema. You can add or remove fields as necessary, making these databases highly adaptable to different data formats and requirements.

Some more considerations while choosing a database for your project:

Scalability

Scalability is a big one. Relational databases traditionally scale vertically, meaning you increase the power of a single server (more RAM, faster CPU) to handle more load. This can get expensive and might hit limits as your application grows. On the other hand, non-relational databases are designed to scale horizontally, meaning you can spread your database across multiple servers or nodes. This makes it easier and more cost-effective to handle massive amounts of data and high traffic.

Flexibility

Then there’s flexibility. Relational databases require a fixed schema, which means you need to define the structure of your data upfront. This can be limiting if your data structure is likely to change over time. Non-relational databases, on the other hand, are much more flexible since they don’t require a fixed schema. You can easily add new fields or change the structure of your data without major disruptions.

Transaction Handling

Transaction handling is also worth mentioning. Relational databases excel at handling transactions where consistency is crucial. If you’re working on a banking app, for example, you need to ensure that every transaction is processed accurately and consistently. While some NoSQL databases now offer transaction support, they’re generally more suited for scenarios where performance and availability are prioritized over strict consistency.

The Bottom Line

There’s no one-size-fits-all answer here. The best choice really depends on what you’re trying to achieve. In some cases, you might even want to use both—relational for the structured stuff and non-relational for everything else. The key is to understand your data and choose the tool that makes the most sense for your project.

In, this article we are going to discuss mongodb a popular NoSQL db and its integration with springboot.

MongoDB: An Introduction to Modern Databases

  • MongoDB is a free, open-source NoSQL database that stores data in flexible, JSON-like BSON (Binary JSON) documents with a dynamic schema.
  • It’s designed for scalability, high availability, and performance—perfect for the MEAN/MERN stack, but it also integrates nicely with Spring Boot. Plus, it supports automatic sharding, indexing, and multi-document transactions.
  • Thanks to its schema-less design and horizontal scaling (basically adding more servers to keep up with growing data and traffic), MongoDB is great for handling unstructured or semi-structured data across different environments.
  • With rich querying capabilities, high performance, and integration with big data tools, MongoDB is a go-to for modern web apps, real-time analytics, and IoT solutions. And yes, well-known companies like Amazon, Uber, eBay, and Facebook use it.

Collection and documents in Mongo DB

This image provides a visual comparison between how data is organized in a traditional relational database (RDBMS) and in MongoDB.

In MongoDB, collections and documents work a lot like tables and rows in a traditional SQL database, but with a bit more flexibility.

Collections (Similar to Tables)

  • A collection in MongoDB is like a table in SQL. It’s where you store related pieces of data. For instance, if you’re keeping track of books in a library, you’d have a <span class="pink">books</span> collection, just like you’d have a <span class="pink">Books</span> table in SQL.

Documents (Similar to Rows)

  • A document is what you’d call a row in SQL. Each document in a collection is a single record, like a single row in a table. Documents store data in a JSON-like format, which means they can hold different types of data, all packed together.

Example:

  • SQL: Imagine a <span class="pink">Books</span> table where each row has columns like <span class="pink">Title</span>, <span class="pink">Author</span>, and <span class="pink">Year</span>.
  • MongoDB: In the <span class="pink">books</span> collection, each document would store the same information, but in a flexible format:

	
{
  "title": "Malgudi Days",
  "author": "R.K. Narayan",
  "year": 1943
}

The Difference:

The big difference is that in SQL, every row in a table has to follow the same structure—every row needs to have the same columns. But in MongoDB, documents in a collection don’t have to look the same. One document might have an extra field or leave one out entirely, giving you more flexibility with how you structure your data.

Integrating SpringBoot With MongoDB

💡 SpringBoot provides excellent support for MongoDB. In this article, we’ll cover everything related to Spring Boot projects using Maven.

Different ways:Connecting Spring Application with MongoDB

When connecting a Spring Boot application with MongoDB, there are several approaches you can take, each with varying degrees of functionality and abstraction:

1. Using Spring Data MongoDB

Spring Data MongoDB is the most straightforward and go-to approach to connecting a Spring Boot application with MongoDB. It provides an abstraction over MongoDB, allowing developers to interact with the database using repositories, similar to how they would with JPA.

You just need to add this dependency to your Spring Boot project:

	
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

2. Direct MongoDB Access with Java Driver

For scenarios where you need complete control over how you interact with MongoDB, using the MongoDB Java Driver directly is a powerful approach. This method is especially useful when you want to bypass the abstractions provided by higher-level libraries and work directly with MongoDB's core features.

he key component of the MongoDB Java Driver is the <span class="pink">MongoClient</span>. This class acts as the main gateway to connect to your MongoDB instance. It manages the connection and allows you to perform operations on databases and collections.

When you use the <span class="pink">MongoClient</span>, you can access various databases and collections within MongoDB. This direct interaction lets you execute queries, insert documents, and handle other database operations with precise control. It’s especially beneficial for tasks that require specific database features or fine-tuned performance.

Understanding how to use <span class="pink">MongoClient</span> directly gives you a deeper insight into how MongoDB operations work under the hood. This knowledge is valuable because it helps you grasp how higher-level abstractions like <span class="pink">MongoRepository</span> and <span class="pink">MongoTemplate</span> in Spring Data MongoDB are built on top of the MongoDB Java Driver.

	
<!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-sync -->
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>5.1.3</version>
</dependency>

3. Using Reactive Programming with Spring WebFlux

If your application demands high concurrency or non-blocking I/O, Spring WebFlux in combination with MongoDB’s reactive driver is a powerful option. It allows you to build reactive applications that can handle more traffic with fewer resources.

	
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

Additional:

MongoDB Atlas

MongoDB Atlas is a cloud-based MongoDB service offering a fully managed solution. Connecting to Atlas is similar to connecting to a local MongoDB instance but involves using connection strings specific to your Atlas cluster.

Connecting via a Docker Container of MongoDB

Running MongoDB in a Docker container is a practical approach for managing your database during development or deployment. By using Docker, you can easily set up and isolate your MongoDB environment, ensuring consistency across different systems. Once MongoDB is running in a container, you can connect your Spring Boot application to it just as you would with a locally installed instance. This method is especially useful when you want to quickly spin up a MongoDB instance without going through the traditional installation process.

Conclusion

Each approach to connecting a spring application to a MongoDB instance has its strengths, and the right choice depends on your application's specific needs, such as the level of control you require, the complexity of your queries, and whether you're building a traditional or reactive application. Whether you're looking for simplicity or advanced control, Spring Boot provides versatile ways to connect with MongoDB.

Spring Data MongoDB builds upon MongoTemplate, which in turn uses the MongoDB Driver. The choice depends on the level of abstraction and specific features required for your application.

For reactive applications, Reactive MongoDB provides similar functionality to Spring Data MongoDB, but with support for non-blocking operations.

MongoDB Atlas and Docker focus on where and how MongoDB is hosted, rather than the connection method in your code. You would still use one of the other approaches to interact with the database, regardless of whether it's running locally, in the cloud, or in a container.

Developing a RESTful API with Spring Boot and MongoDB Integration

Objective: We will be developing a book management system using Spring Boot to create a RESTful API, with MongoDB as our data store. The primary focus of this project is to learn how to integrate MongoDB with Spring Boot, covering how to configure and interact with MongoDB in a Spring Boot application.
We'll create endpoints to manage books, including operations for adding, retrieving, updating, and deleting book records.

1. Set Up Your Spring Boot Project

First, you'll need to create a new Spring Boot project. You can do this using Spring Initializr or directly from your IDE.

Dependencies to include:

  • Spring Web
  • Spring Data MongoDB
  • Lombok (optional, for reducing boilerplate code)

Please note that we are working with Spring Boot 3, which requires a minimum of JDK 17.

Spring Data MongoDB focuses on ODM (Object-Document Mapping) for MongoDB., Spring Data MongoDB complements JPA by offering a similar level of abstraction and ease of use for MongoDB, while catering to the needs of NoSQL databases.

The <span class="pink">pom.xml</span> file should resemble the following:

	
<?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">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.telusko</groupId>
	<artifactId>SpringBootMongoDB</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringBootMongoDB</name>
	<description>SpringBootMongoDB</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>21</java.version>
	</properties>
	<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>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

2. Configure MongoDB in Your Application

configure MongoDB connection properties in your <span class="pink">application.properties</span>

	
spring.application.name=SpringBootMongoDB
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=gaurav

3. Define Your MongoDB Model

Next, define a model class that maps to a MongoDB collection. This model will represent the data structure you’re working with in your API.

Book.java

	
package com.unlogged.model;

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

@Data
@Document(collection = "books")
public class Book {
    @Id
    private String id;
    private String title;
    private String author;
    private int year;
}

  • <span class="pink">@Document</span>: This annotation indicates that this class is a MongoDB document, and <span class="pink">collection</span> specifies the name of the collection.
  • <span class="pink">@Id</span>: This annotation marks the primary key field in the document.

MongoDB ID Fields: Auto-generation Strategy

Understanding how MongoDB handles ID fields is crucial for effective database management. Here's a concise overview of the auto-generation behavior for different types of ID fields:

Standard Behavior

  • Automatic <span class="pink">_id</span> Generation: MongoDB auto-generates an <span class="pink">_id</span> field when not provided. By default, this is an <span class="pink">ObjectId</span>, a unique identifier that ensures document uniqueness.

Custom ID Fields: The Difference

  1. Integer IDs (<span class="pink">private int id;</span>):
    • Not Auto-generated: MongoDB does not auto-generate integer IDs. You must manually manage these numeric IDs.
    • Control vs. Complexity: While you have full control over the IDs, this approach requires additional effort to ensure uniqueness and handle increments.
  1. String IDs (<span class="pink">private String id;</span>):
    • Possible Auto-generation: For <span class="pink">String IDs</span>, MongoDB can auto-generate values if configured as the <span class="pink">_id</span> field, typically using its <span class="pink">ObjectId</span> generator.
    • Flexibility and Convenience: MongoDB's <span class="pink">ObjectId</span> is a string representation that simplifies ID management and ensures uniqueness.

Why the Difference?

  • Safety: Auto-generating numeric IDs could lead to conflicts. Strings, particularly <span class="pink">ObjectId</span>, are safer for ensuring unique identifiers.
  • Flexibility: Numeric IDs allow custom sequences, but require manual management. Strings offer built-in uniqueness with less manual effort.
  • Performance: <span class="pink">ObjectId</span> strings are optimized for MongoDB's indexing and internal operations, providing better performance.

Practical Considerations

  • Numeric IDs: If you require auto-generated numeric IDs, you’ll need to implement your own mechanism.
  • String IDs: Consider using MongoDB's <span class="pink">ObjectId</span> for simplicity and built-in support, which integrates seamlessly with MongoDB’s features.

4. Create a Repository Interface

Spring Data MongoDB allows you to interact with your database with minimal code by extending the <span class="pink">MongoRepository</span> interface.

BookRepo.java

	
package com.unlogged.repo;

import com.unlogged.model.Book;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BookRepo extends MongoRepository<Book, Integer> {
}

  • The <span class="pink">BookRepository</span> interface extends <span class="pink">MongoRepository</span>, providing simple CRUD operations and custom query methods.

5. Build RESTful Endpoints with a Controller

Create a controller class to define RESTful API endpoints that interact with your MongoDB collection via the repository.

BookController.java

	
package com.unlogged.controller;

import com.unlogged.model.Book;
import com.unlogged.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @GetMapping
    public List<Book> getAllBooks() {
        return bookService.getAllBooks();
    }

    @GetMapping("/{id}")
    public Book getBookById(@PathVariable Integer id) {
        return bookService.getBookById(id);
    }

    @PostMapping
    public Book createBook(@RequestBody Book book) {
        return bookService.createBook(book);
    }

    @PutMapping("/{id}")
    public Book updateBook(@PathVariable Integer id, @RequestBody Book bookDetails) {
        return bookService.updateBook(id, bookDetails);
    }

    @DeleteMapping("/{id}")
    public void deleteBook(@PathVariable Integer id) {
        bookService.deleteBook(id);
    }
}

This <span class="pink">BookController</span> manages all book-related API requests. It handles everything from listing all books, retrieving a book by its ID, creating and updating books, to deleting them.

6. Create methods in BookService interface

BookService.java

	
package com.unlogged.service;


import com.unlogged.model.Book;

import java.util.List;

public interface BookService {
    List<Book> getAllBooks();

    Book getBookById(Integer id);

    Book createBook(Book book);

    Book updateBook(Integer id, Book bookDetails);

    void deleteBook(Integer id);
}

This <span class="pink">BookService</span> interface just handles the basics: fetching, adding, updating, or deleting books.

7. Implement methods defined in BookService Interface

BookServiceImpl.java

	
package com.unlogged.serviceImpl;

import com.unlogged.model.Book;
import com.unlogged.repo.BookRepo;
import com.unlogged.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookRepo bookRepo;

    @Override
    public List<Book> getAllBooks() {
        return bookRepo.findAll();
    }

    @Override
    public Book getBookById(Integer id) {
        return bookRepo.findById(id).orElse(null);
    }

    @Override
    public Book createBook(Book book) {
        return bookRepo.save(book);
    }

    @Override
    public Book updateBook(Integer id, Book bookDetails) {
        Book book = bookRepo.findById(id).orElse(null);
        if (book != null) {
            book.setTitle(bookDetails.getTitle());
            book.setAuthor(bookDetails.getAuthor());
            book.setYear(bookDetails.getYear());
            return bookRepo.save(book);
        } else {
            return null;
        }
    }

    @Override
    public void deleteBook(Integer id) {
        bookRepo.deleteById(id);
    }
}

The <span class="pink">BookServiceImpl</span> class implements the <span class="pink">BookService</span> interface and contains the actual code to manage books.

MongoDB Connection String

In MongoDB, the connection string is a URI (Uniform Resource Identifier) used by applications to connect to a MongoDB database. It specifies the database's location, authentication credentials, and other options to establish a connection. The format of the connection string is standardized and allows for various parameters to be included depending on the needs of your application.

Example

Here’s a simple example of a MongoDB connection string:

	
mongodb://root:example@localhost:27018/gaurav

This connection string connects to a MongoDB server running on <span class="pink">localhost</span> at port <span class="pink">27018</span> and uses the <span class="pink">gaurav</span> database. The credentials <span class="pink">root</span> as the username and <span class="pink">example</span> as the password are provided for authentication.

Use in Spring Boot

In a Spring Boot application, you can specify the connection string in the <span class="pink">application.properties</span> or <span class="pink">application.yml</span> file like this:


spring.data.mongodb.uri=mongodb://localhost:27017/gaurav

This tells Spring Boot to connect to the <span class="pink">gaurav</span> database on the local MongoDB instance.

Analogy Summary

  • SQL (MySQL): Think of <span class="pink">jdbc:mysql://localhost:3306/mydatabase</span> where you connect to a MySQL database running on your computer on port 3306.
  • MongoDB: Similarly, <span class="pink">mongodb://localhost:27017/gaurav</span> connects to a MongoDB database running on your computer on port 27017.

In both cases, the connection string tells your application where to find the database and which specific database to connect to.

Testing API via Postman:

Adding a new document to the books collection.

💡 Note: We won't be testing each API, but you are welcome to test them on your own, as they involve simple CRUD operations only.

Graphical Tools for Managing and Interacting with MongoDB

For managing and interacting with MongoDB, several GUI tools are available:

MongoDB Compass is the official MongoDB tool offering data visualization, schema analysis, and indexing support for a comprehensive database management experience.

Robo 3T is a lightweight tool with an intuitive interface, shell integration, and query autocompletion, ideal for quick database interactions.

Studio 3T is a feature-rich tool with advanced capabilities like SQL to MongoDB migration, visual query building, and data import and export.

NoSQLBooster provides a rich query editor, IntelliSense support, and MongoDB shell integration for advanced querying and scripting.

Mongo Express is a web-based interface for basic database management, allowing CRUD operations and simple querying through a user-friendly browser interface.

and many more ….

Exploring MongoDB Compass

MongoDB Compass is the official GUI tool provided by the MongoDB team for visualizing data, supporting indexing, creating databases, and more.

On the MongoDB Compass home page, simply enter your connection string to connect to your MongoDB instance.

MongoDB requires that a database contains at least one collection. Therefore, when you create a new database using MongoDB Compass, you must also add at least one collection to it.

In MongoDB, collections are not created until you insert the first document (record in SQL) into them. So even though you have a <span class="pink">Book</span> class annotated with <span class="pink">@Document(collection = "books")</span>, the <span class="pink">books</span> collection won't appear in your database until you actually save a <span class="pink">Book</span> object.

Create a database named "gaurav" and insert a book document into the "books" collection.

Explanation: On the left side, you can see a database named <span class="pink">gaurav</span>. When a book document is inserted, a collection called <span class="pink">books</span> is created. Inside this collection, you'll find the book document with <span class="pink">id=1</span>.

Now that the foundations are established, let us explore more in-depth advanced features.

MongoTemplate

MongoTemplate is a core component of Spring Data MongoDB, designed to make interactions with MongoDB more flexible and code-driven. It offers a higher level of abstraction for tasks like querying, updating, and deleting documents in MongoDB collections. Compared to the repository abstraction, MongoTemplate supports more complex and dynamic queries.

Earlier, we were using the <span class="pink">MongoRepository</span> interface, which offers a higher level of abstraction and simplifies basic CRUD operations. However, with <span class="pink">MongoTemplate</span>, we write our own queries for data operations directly within the service class methods. This approach gives us greater control over how we interact with our data.

Remove <span class="pink">BookRepo</span>

Since <span class="pink">MongoTemplate</span> replaces the need for a repository interface, you can remove the <span class="pink">BookRepo</span> interface.

Redefining the implementation class methods, with each method having its own custom query.

BookServiceImpl.java

	
package com.unlogged.serviceImpl;

import com.unlogged.model.Book;
import com.unlogged.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public List<Book> getAllBooks() {
        return mongoTemplate.findAll(Book.class);
    }

    @Override
    public Book getBookById(Integer id) {
        Query query = new Query(Criteria.where("id").is(id));
        return mongoTemplate.findOne(query, Book.class);
    }

    @Override
    public Book createBook(Book book) {
        return mongoTemplate.save(book);
    }

    @Override
    public Book updateBook(Integer id, Book bookDetails) {
        Query query = new Query(Criteria.where("id").is(id));
        Book book = mongoTemplate.findOne(query, Book.class);
        if (book != null) {
            book.setTitle(bookDetails.getTitle());
            book.setAuthor(bookDetails.getAuthor());
            book.setYear(bookDetails.getYear());
            return mongoTemplate.save(book);
        }
        return null;
    }

    @Override
    public void deleteBook(Integer id) {
        Query query = new Query(Criteria.where("id").is(id));
        mongoTemplate.remove(query, Book.class);
    }
}

Custom Query

To write custom queries with <span class="pink">MongoTemplate</span>, one should focus on the following areas:

Query and Criteria API

Check out the <span class="pink">Query</span> and <span class="pink">Criteria</span> classes in Spring Data MongoDB. They let you build custom queries by defining conditions programmatically.

MongoDB Query Language (MQL)

Even though <span class="pink">MongoTemplate</span> uses Java code rather than MongoDB’s native query language, knowing MQL can help you understand how to construct and translate complex queries.

MongoDB Aggregation Framework

For advanced queries, dive into MongoDB's Aggregation Framework. It’s great for tasks like grouping and sorting data, and you can use this knowledge to craft powerful queries with <span class="pink">MongoTemplate</span>.

Spinning a MongoDB Instance Using Docker Compose

Setting up a MongoDB instance with Docker Compose is a quick and easy way to get your database running. Instead of dealing with tricky installations, you just use a simple configuration file to start MongoDB in a container. This makes it easy to manage your database, keep things consistent, and get up and running fast, whether you’re working on your local machine or preparing for deployment.

docker-compose.yml

	
version: '3.8'

services:
  mongodb:
    image: mongo:latest
    container_name: mongodb
    ports:
      - "27018:27017"
    volumes:
      - mongodb_data:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=example

volumes:
  mongodb_data:

This Docker Compose configuration deploys a MongoDB instance with the following setup:

  • <span class="pink">version: '3.8'</span>: Specifies the Compose file format version 3.8.
  • <span class="pink">services</span>: Defines the <span class="pink">mongodb</span> service:
    • <span class="pink">image: mongo:latest</span>: Utilizes the latest MongoDB image from Docker Hub.
    • <span class="pink">container_name: mongodb</span>: Assigns the container the name <span class="pink">mongodb</span>.
    • <span class="pink">ports</span>: Maps host port <span class="pink">27018</span> to container port <span class="pink">27017</span>, facilitating database access.
    • <span class="pink">volumes</span>: Mounts the named volume <span class="pink">mongodb_data</span> to <span class="pink">/data/db</span> in the container for persistent storage.
    • <span class="pink">environment</span>: Configures MongoDB with a root username (<span class="pink">root</span>) and password (<span class="pink">example</span>).
  • <span class="pink">volumes</span>: Declares the <span class="pink">mongodb_data</span> volume for storing MongoDB data persistently.

You can update the <span class="pink">application.properties</span> file to connect your Spring Boot application to the MongoDB instance running inside the Docker container with the following URI:

	
spring.data.mongodb.uri=mongodb://root:example@localhost:27018/gaurav?authSource=admin

The container named <span class="pink">springbootmongodb</span> is running, and it is running a MongoDB image inside.

Linking Collections and underlying issues

When working with databases like MongoDB, the concept of linking collections becomes vital. While MongoDB's flexibility with schemas offers significant advantages, it also introduces unique challenges when managing linked collections.

In relational databases, relationships between tables are typically managed through foreign keys and joins. However, NoSQL databases like MongoDB do not have built-in join operations.

MongoDB lacks traditional joins found in SQL databases.

Methods for linking data:

  • References: Store IDs of related documents
  • Embedding: Nest related data within documents
  • Hybrid: Combine both approaches

Linking Collections:

In this section, we extend our data model by introducing an additional document, <span class="pink">Publisher</span>, which references the <span class="pink">Book</span> document. This process demonstrates how to establish relationships between different collections in MongoDB, allowing for efficient data management and querying. By linking the <span class="pink">Book</span> and <span class="pink">Publisher</span> documents, we create a structured way to handle related information.

To facilitate this linkage, we utilize a <span class="pink">DataLoader</span> class. This class is crucial for pre-populating the <span class="pink">Publisher</span> collection with initial data. By doing so, we ensure that there are existing publisher records in the database that our <span class="pink">Book</span> documents can reference. The <span class="pink">DataLoader</span> automates the process of inserting sample data, making it easier to test and work with linked collections. This setup is essential for verifying the integrity of our references and ensuring that the data relationships function as intended.

Publisher.java

	
package com.unlogged.model;

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

@Data
@Document(collection = "publishers")
public class Publisher {
    @Id
    private String id;
    private String name;
    private String location;
    private String contact;
}

Explanation: Represents the <span class="pink">Publisher</span> document model in MongoDB with fields for <span class="pink">name</span>, <span class="pink">location</span>, and <span class="pink">contact<span>.

Book.java

	
package com.unlogged.model;

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

@Data
@Document(collection = "books")
public class Book {
    @Id
    private Integer id;
    private String title;
    private String author;
    private int year;

    @DBRef
    private Publisher publisher;
}

Explanation: Defines the <span class="pink">Book</span> document with a reference to a <span class="pink">Publisher</span>, linking the two collections in MongoDB.

What <span class="pink">@DBRef</span> Does

  • Reference Linking: The <span class="pink">@DBRef</span> annotation indicates that the <span class="pink">publisher</span> field in the <span class="pink">Book</span> class is a reference to another document in a different collection, specifically the <span class="pink">Publisher</span> collection. Instead of storing the entire <span class="pink">Publisher</span> object within the <span class="pink">Book</span> document, MongoDB stores a reference to the <span class="pink">Publisher</span> document's ID.
  • Normalization: Using <span class="pink">@DBRef</span> is akin to normalizing a database in relational databases. It helps avoid duplication of data by storing only a reference to the <span class="pink">Publisher</span> document rather than embedding the entire <span class="pink">Publisher</span> object in every <span class="pink">Book</span> document.

How It Works

  • In the <span class="pink">Book</span> Class: When a <span class="pink">Book</span> document is saved in MongoDB, the <span class="pink">publisher</span> field will store a reference (a DBRef) pointing to the corresponding <span class="pink">Publisher</span> document in the <span class="pink">Publisher</span> collection.
  • In MongoDB: The actual <span class="pink">Publisher</span> data is stored in a separate document within the <span class="pink">Publisher</span> collection. When you retrieve a <span class="pink">Book</span> document, Spring Data MongoDB can automatically fetch the referenced <span class="pink">Publisher</span> document using this DBRef.

PublisherRepo.java

	
package com.unlogged.repo;

import com.unlogged.model.Publisher;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface PublisherRepo extends MongoRepository<Publisher, String> {
}

DataLoader.java

	
package com.unlogged;

import com.unlogged.model.Publisher;
import com.unlogged.repo.PublisherRepo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class DataLoader {

    @Bean
    CommandLineRunner loadInitialData(PublisherRepo publisherRepository) {
        return args -> {

            Publisher publisher1 = new Publisher();
            publisher1.setName("Penguin Random House");
            publisher1.setLocation("New Delhi, India");
            publisher1.setContact("contact@penguinrandomhouse.in");

            Publisher publisher2 = new Publisher();
            publisher2.setName("HarperCollins");
            publisher2.setLocation("Noida, India");
            publisher2.setContact("info@harpercollins.co.in");

            Publisher publisher3 = new Publisher();
            publisher3.setName("Hachette India");
            publisher3.setLocation("Gurgaon, India");
            publisher3.setContact("info@hachetteindia.com");

            Publisher publisher4 = new Publisher();
            publisher4.setName("Rupa Publications");
            publisher4.setLocation("Delhi, India");
            publisher4.setContact("contact@rupapublications.com");

            Publisher publisher5 = new Publisher();
            publisher5.setName("Scholastic India");
            publisher5.setLocation("Gurgaon, India");
            publisher5.setContact("contact@scholastic.co.in");

            // Save publishers to the database
            publisherRepository.saveAll(Arrays.asList(publisher1, publisher2, publisher3, publisher4, publisher5));
        };
    }
}

Explanation: Loads initial <span class="pink">Publisher</span> data into the MongoDB database when the application starts.

Demo:

Underlying issues:

  • Performance considerations: Fetching related data often requires multiple queries or complex aggregations, potentially impacting response times.
  • Data consistency: Maintaining consistency across linked collections becomes challenging, especially when data is duplicated or distributed.
  • Schema design complexity: Deciding between embedding and referencing requires careful consideration of data access patterns and future scalability needs.
  • Transactional integrity: Ensuring atomic operations across multiple collections can be challenging, especially in distributed environments.
  • Effective solutions require careful data modeling tailored to specific use cases and access patterns.

MongoDB Atlas Vector Database: Briefing

MongoDB Atlas Vector Database is a specialized feature within MongoDB Atlas designed to handle vector data and perform efficient similarity searches, a key component for applications involving machine learning, natural language processing, and recommendation systems.

Key Highlights:

  • Vector Storage: It allows you to store complex, high-dimensional vectors—such as those used for representing text or images—making it easier to manage and work with this type of data.
  • Similarity Search: The database excels at finding similar vectors quickly using techniques like Approximate Nearest Neighbors (ANN). This is perfect for applications that need to match or recommend items based on similarity.
  • Scalability: Built on MongoDB Atlas’s managed infrastructure, it scales effortlessly to handle large amounts of vector data, ensuring smooth performance as your needs grow.
  • Integration: Seamlessly integrates with MongoDB's existing features, so you can leverage its powerful querying and indexing capabilities alongside vector data.
  • Real-World Use: Ideal for applications needing real-time recommendations, advanced search features, or intelligent data insights, such as personalized content delivery and fraud detection.

To integrate this into your Spring Boot project, simply add the following dependency:

	
 <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>sprin
      g-ai-mongodb-atlas-store-spring-boot-starter</artifactId>
    </dependency>

Long Story Short

MongoDB with Spring Boot is a vast topic that can't be fully explored in a single article. However, this article covers more than enough to get you started and running. We've covered key concepts, provided practical code examples, and highlighted important considerations. If any specific topics pique your interest, don’t hesitate to dive deeper and explore them further to enhance your understanding and skills.

Stay tuned for upcoming articles where we will dive into more advanced topics such as indexing, aggregation pipelines, caching, and connection pooling and much more.

Cheers!

Happy Coding.

Gaurav Sharma
August 22, 2024
Use Unlogged to
mock instantly
record and replay methods
mock instantly
Install Plugin