eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

In this article, we’ll have a look at integrating MongoDB, a very popular NoSQL database with a standalone Java client application.

MongoDB is a distributed database at its core, which means high availability, horizontal scaling, and geographic distribution are built-in and easy to use.

2. MongoDB Concepts and Terminology

Let’s start with a few key points about MongoDB itself.

MongoDB stores data as a document, which is a data structure composed of field and value pairs. MongoDB documents look similar to JSON objects. The values of fields can be scalar values, such as strings and numbers, but may include other documents, arrays, and arrays of documents. This allows for more complex data models to represent our data.

The advantages of using documents include:

  • Documents correspond to native data types in many programming languages
  • Embedded documents and arrays reduce the need for expensive joins
  • Dynamic schema supports fluent polymorphism

We can also compare traditional relational database terms with their MongoDB equivalents:

  • Table in relational databases becomes Collection in MongoDB
  • Row becomes Document
  • Column becomes Field
  • Joins are achieved through embedded documents, document references, or $lookup to combine data from different collections

This is a simplified but useful way to look at the MongoDB core concepts.

Now, let’s look into a sample implementation to understand the MongoDB database.

3. Using MongoDB

Throughout this tutorial, we’ll be connecting to an external MongoDB server. To set up our own free cluster, we head to MongoDB Atlas cluster and follow the steps to get a database hosted in the cloud.

Next, we’ll connect to our MongoDB database, and run some CRUD operations against it.

3.1. Maven Dependency

We’ll be using the mongodb-driver-sync Java Driver for MongoDB:

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>5.0.1</version>
</dependency>

This driver is suited for synchronous Java applications that make blocking calls to the MongoDB database. MongoDB also has a Java Reactive Streams Driver for asynchronous applications.

3.2. Make a Connection With MongoClient

First, let’s make a connection to our MongoDB server. We’ll use the MongoClient:

String uri = "mongodb+srv://user:[email protected]/";
MongoClient mongoClient = MongoClients.create(uri)

This uri example follows the same format we can expect our connection string to resemble.

3.3. Connecting to a Database

After that, let’s connect to our database. When we specify a database to use, if no database of that name exists, MongoDB creates it upon the first write operation.

For this, we’ll use the sample_mflix database MongoDB provides, which comes loaded with several sample collections already:

MongoDatabase database = mongoClient.getDatabase("sample_mflix");

To connect to another database, we just specify the name in place of our database sample_mflix.

3.4. Create a Collection

Additionally, if we wanted to create a collection (table equivalent for MongoDB) in the code, we use:

database.createCollection("movies");

This creates a collection called movies in our database, if one doesn’t exist already.

3.5. Inserting Documents

We can insert a single document into a collection using the insertOne() method on a MongoCollection object, or multiple documents using the insertMany() method.

To insert a document, we construct a Document object with the fields and values that we want to store. If we call the insertOne() method on a collection that doesn’t exist yet, the server automatically creates it for us.

Let’s insert a new movie Document into our movies collection collection:

MongoDatabase database = mongoClient.getDatabase("sample_mflix");
MongoCollection<Document> collection = database.getCollection("movies");

collection.insertOne(new Document()
  .append("_id", new ObjectId())
  .append("title", "Silly Video")
  .append("genres", Arrays.asList("Action", "Adventure")));

We create a Document object and add a title field with a string value, and a genres field with a List<String> values.

We also add an _id field. In MongoDB, every document needs an immutable _id value, which we’re initializing using an ObjectId object. Each ObjectId is unique by design. While we can technically use any value we can expect to never change, such as the title, it can be difficult to guarantee uniqueness, and so often using a unique random value is the way to go.

The entity will be sent data to MongoDB and stored as BSON (Binary JSON), which is faster compared to JSON itself when it comes to querying the data:

{
  "_id": {
    "$oid": "662117c69611c5728c351ebf"
  },
  "title": "Silly Video",
  "genres": [
    "Action",
    "Adventure"
  ]
}

To insert multiple documents at the same time, we can use the insertMany() method:

List<Document> movieList = Arrays.asList(
  new Document().append("title", "Silly Video 2"),
  new Document().append("title", "Silly Video: The Prequel"));

InsertManyResult result = collection.insertMany(movieList);

This takes in our array of JSON documents and send these to MongoDB to be stored as individual documents.

3.6. Finding Documents

We can retrieve a single document from a collection by using the find() and first() methods.

A simple find operation might look like this:

collection.find(eq("title", "The Great Train Robbery"))
  .first();

A query filter is passed to the find() method. The eq() filter matches only movies with the exact title “The Great Train Robbery”. This returns the rather large document in our database, and if there are multiple with that title, there is no guarantee it’s the document we want. We can refine our results using various methods, all of which we can find in the MongoDB docs.

For example, let’s do a search:

Bson projectionFields = Projections.fields(
  Projections.include("title", "genres"),
  Projections.excludeId());

collection.find(eq("title", "The Great Train Robbery"))
  .projection(projectionFields)
  .sort(Sorts.ascending("year"))
  .first();

There are a couple of new steps to our find() operation:

  • A sort() that organises matched documents in ascending order by year. Pairing this with the first() method allows us to return only the oldest movie with the matching title.
  • A projection() that includes the objects in the title and genre fields and excludes the _id field using the helper method excludeId().

We can query for multiple documents using the find() method in much the same way, but how we access and traverse the results is slightly different.

Let’s create a query which finds all documents with a year greater than 1900 and sort them from oldest to newest, then limit the fields projected to just the title and year of each document returned.

Bson projectionFields = Projections.fields(
  Projections.include("title", "year"),
  Projections.excludeId());

MongoCursor<Document> cursor = collection.find(gt("year", 1900))
  .projection(projectionFields)
  .sort(Sorts.asscending("title")).iterator();

We call the iterator() method which returns a MongoCursor instance. We use it to traverse the results.

A cursor is just a mechanism that allows us to iterate over our database results while only holding a subset in memory at any given time.

MongoCursor has methods such as hasNext() to check whether additional results exist, or next() to return the next document in the collection. If no documents match the query, calling hasNext() returns false and therefore calling next() throws an exception. So we always use hasNext() to verify there is another document first.

This means if we wanted to print the results of the find() operation, we could do something like this:

try {
    while (cursor.hasNext()) {
        logger.info(cursor.next().toJson());
    }
} finally {
    cursor.close();
}

This parses through the values in our cursor and display our documents.

3.7. Updating Documents

Now let’s look at how we can update our documents in our MongoDB database. We’ll start with just one document using the updateOne() method.

Let’s update the document we added earlier:

Document updateQuery = new Document().append("title", "Silly Video");

Bson updates = Updates.combine(
  Updates.set("runtime", 99),
  Updates.addToSet("genres", "Comedy")
  Updates.currentTimestamp("lastUpdated"));

collection.updateOne(updateQuery, updates);

This sets the runtime of our document to 99, and add the value Comedy to the genres array. It also adds a field for lastUpdated, automatically populated with the current timestamp, which is a good idea for auditing our database and tracking changes in data.

To update many documents, the updateMany() method takes the query filter and updates all the documents that match:

Bson query = gt("num_mflix_comments", 50);

Bson updates = Updates.combine(
  Updates.addToSet("genres", "Frequently Discussed"),
  Updates.currentTimestamp("lastUpdated"));

collection.updateMany(query, updates);

This query updates all documents with more than 50 comments to add the value “Frequently Discussed” in the genre array. MongoDB also allows us to replace a document with the replaceOne() operator.

3.8. Deleting Documents

Finally, let’s move on to our last CRUD operation, deletion. Like the other methods, deleting documents requires passing in a query filter, and deletes the first document it matches with.

Let’s delete the document we added earlier:

Bson deleteQuery = eq("title", "Silly Video");

collection.deleteOne(deleteQuery);

If, however, we wanted to delete multiple documents that match our query filter, we’d use the deleteMany(). Without any query filter, deleteMany() matches all documents in our collection. But if that is the goal, we should consider using the drop() method for better performance.

Let’s delete all the movies in the sample movie collection with a runtime of less than 60 minutes:

Bson query = lt("runtime", 60);

collection.deleteMany(query);

This matches to and deletes all the documents in our collection with a runtime with a value less than 60.

4. Conclusion

This article was a quick introduction to using MongoDB from Java. We learned how to create documents and write them into our database, how to find these documents, update, and delete them. We also learned how to perform these operations on individual documents, or multiple documents at a time.

These are the basic database operations we need to get started with CRUD on our MongoDB database, but only touches the surface for what we can achieve with MongoDB and Java.

For more in-depth knowledge on using MongoDB with Java, the official MongoDB documentation provides quickstarts and usage guides, with plenty of sample code to get started. Alternatively, we can see the MongoDB Developer Center to learn more about how to use MongoDB with Java.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)