Getting Started with MongoDB and Java - CRUD Operations Tutorial
Rate this quickstart
- Update Java Driver to 4.2.2.
- Added Client Side Field Level Encryption example.
- Update Java Driver to 4.1.1.
- The MongoDB Java Driver logging is now enabled via the popular SLF4J API so I added logback in the
pom.xml
and a configuration filelogback.xml
.

In this very first blog post of the Java Quick Start series, I will show you how to set up your Java project with Maven and execute a MongoDB command in Java. Then we will explore the most common operations — such as create, read, update, and delete — using the MongoDB Java driver. I will also show you some of the more powerful options and features available as part of the MongoDB Java driver for each of these operations, giving you a really great foundation of knowledge to build upon as we go through the series.
In future blog posts, we will move on and work through:
- Multi-document ACID transactions
- The MongoDB Java reactive streams driver
Java is the most popular language in the IT industry at the date of this blog post, and developers voted MongoDB as their most wanted database four years in a row. In this series of blog posts, I will be demonstrating how powerful these two great pieces of technology are when combined and how you can access that power.
To follow along, you can use any environment you like and the integrated development environment of your choice. I'll use Maven 3.6.2 and the Java OpenJDK 13, but all the code will be compatible with Java versions 8 to 13, so feel free to use the JDK of your choice and update the Java version accordingly in the pom.xml file we are about to set up.
For the MongoDB cluster, we will be using a M0 Free Tier MongoDB Cluster from MongoDB Atlas. If you don't have one already, check out my Get Started with an M0 Cluster blog post.
Get your free M0 cluster on MongoDB Atlas today. It's free forever, and you'll be able to use it to work with the examples in this blog series.
Let's jump in and take a look at how well Java and MongoDB work together.
To begin with, we will need to set up a new Maven project. You have two options at this point. You can either clone this series' git repository or you can create and set up the Maven project.
If you choose to use git, you will get all the code immediately. I still recommend you read through the manual set-up.
You can clone the repository if you like with the following command.
You can either use your favorite IDE to create a new Maven project for you or you can create the Maven project manually. Either way, you should get the following folder architecture:
The pom.xml file should contain the following code:
To verify that everything works correctly, you should be able to create and run a simple "Hello MongoDB!" program. In
src/main/java/com/mongodb/quickstart
, create the HelloMongoDB.java
file:Then compile and execute it with your IDE or use the command line in the root directory (where the
src
folder is):The result should look like this:
Note: If you see some warnings about an illegal reflective access from
guice.java
, it's safe to ignore them. Guice is used by Maven and needs an update. You can read more about it in this GitHub issue. These warnings will disappear in a future release of Guice and Maven.Now that our Maven project works and we have resolved our dependencies, we can start using MongoDB Atlas with Java.
If you have imported the sample dataset as suggested in the Quick Start Atlas blog post, then with the Java code we are about to create, you will be able to see a list of the databases in the sample dataset.
The first step is to instantiate a
MongoClient
by passing a MongoDB Atlas connection string into the MongoClients.create()
static method. This will establish a connection to MongoDB Atlas using the connection string. Then we can retrieve the list of databases on this cluster and print them out to test the connection with MongoDB.In
src/main/java/com/mongodb
, create the Connection.java
file:As you can see, the MongoDB connection string is retrieved from the System Properties, so we need to set this up. Once you have retrieved your MongoDB Atlas connection string, you can add the
mongodb.uri
system property into your IDE. Here is my configuration with IntelliJ for example.
Or if you prefer to use Maven in command line, here is the equivalent command line you can run in the root directory:
Note: Don't forget the double quotes around the MongoDB URI to avoid surprises from your shell.
The standard output should look like this:
In the Connecting with Java section, we created the classes
HelloMongoDB
and Connection
. Now we will work on the Create
class.If you didn't set up your free cluster on MongoDB Atlas, now is great time to do so. Get the directions for creating your cluster.
In the sample dataset, you can find the database
sample_training
, which contains a collection grades
. Each document in this collection represents a student's grades for a particular class.And here is the extended JSON representation of the same student. You can retrieve it in MongoDB Compass, our free GUI tool, if you want.
Extended JSON is the human readable version of a BSON document without loss of type information. You can read more about the Java driver and BSON in the MongoDB Java driver documentation.
As you can see, MongoDB stores BSON documents and for each key-value pair, the BSON contains the key and the value along with its type. This is how MongoDB knows that
class_id
is actually a double and not an integer, which is not explicit in the mongo shell representation of this document.We have 10,000 students (
student_id
from 0 to 9999) already in this collection and each of them took 10 different classes, which adds up to 100,000 documents in this collection. Let's say a new student (student_id
10,000) just arrived in this university and received a bunch of (random) grades in his first class. Let's insert this new student document using Java and the MongoDB Java driver.In this university, the
class_id
varies from 0 to 500, so I can use any random value between 0 and 500.Firstly, we need to set up our
Create
class and access this sample_training.grades
collection.Secondly, we need to represent this new student in Java using the
Document
class.As you can see, we reproduced the same data model from the existing documents in this collection as we made sure that
student_id
, class_id
, and score
are all doubles.Also, the Java driver would have generated the
_id
field with an ObjectId for us if we didn't explicitly create one here, but it's good practice to set the _id
ourselves. This won't change our life right now, but it makes more sense when we directly manipulate POJOs and we want to create a clean REST API. I'm doing this in my mapping POJOs post.Note as well that we are inserting a document into an existing collection and database, but if these didn’t already exist, MongoDB would automatically create them the first time you to go insert a document into the collection.
Finally, we can insert this document.
Here is the final
Create
class to insert one document in MongoDB with all the details I mentioned above.You can execute this class with the following Maven command line in the root directory or using your IDE (see above for more details). Don't forget the double quotes around the MongoDB URI to avoid surprises.
Note that the order of the fields is different from the initial document with
"student_id": 0
.We could get exactly the same order if we wanted to by creating the document like this.
But if you do things correctly, this should not have any impact on your code and logic as fields in JSON documents are not ordered.
An object is an unordered set of name/value pairs.
Now that we know how to create one document, let's learn how to insert many documents.
Of course, we could just wrap the previous
insert
operation into a for
loop. Indeed, if we loop 10 times on this method, we would send 10 insert commands to the cluster and expect 10 insert acknowledgments. As you can imagine, this would not be very efficient as it would generate a lot more TCP communications than necessary.Instead, we want to wrap our 10 documents and send them in one call to the cluster and we want to receive only one insert acknowledgement for the entire list.
Let's refactor the code. First, let's make the random generator a
private static final
field.Let's make a grade factory method.
And now we can use this to insert 10 documents all at once.
As you can see, we are now wrapping our grade documents into a list and we are sending this list in a single call with the
insertMany
method.By default, the
insertMany
method will insert the documents in order and stop if an error occurs during the process. For example, if you try to insert a new document with the same _id
as an existing document, you would get a DuplicateKeyException
.Therefore, with an ordered
insertMany
, the last documents of the list would not be inserted and the insertion process would stop and return the appropriate exception as soon as the error occurs.As you can see here, this is not the behaviour we want because all the grades are completely independent from one to another. So, if one of them fails, we want to process all the grades and then eventually fall back to an exception for the ones that failed.
This is why you see the second parameter
new InsertManyOptions().ordered(false)
which is true by default.Let's refactor the code a bit and here is the final
Create
class.As a reminder, every write operation (create, replace, update, delete) performed on a single document is ACID in MongoDB. Which means
insertMany
is not ACID by default but, good news, since MongoDB 4.0, we can wrap this call in a multi-document ACID transaction to make it fully ACID. I explain this in more detail in my blog about multi-document ACID transactions.We created the class
Create
. Now we will work in the Read
class.We wrote 11 new grades, one for the student with
{"student_id": 10000}
and 10 for the student with {"student_id": 10001}
in the sample_training.grades
collection.As a reminder, here are the grades of the
{"student_id": 10000}
.We also discussed BSON types and we noted that
student_id
and class_id
are doubles.MongoDB treats some types as equivalent for comparison purposes. For instance, numeric types undergo conversion before comparison.
So, don't be surprised if I filter with an integer number and match a document which contains a double number for example. If you want to filter documents by value types, you can use the $type operator.
Let's read the document above. To achieve this, we will use the method
find
, passing it a filter to help identify the document we want to find.Please create a class
Read
in the com.mongodb.quickstart
package with this code:Also, make sure you set up your
mongodb.uri
in your system properties using your IDE if you want to run this code in your favorite IDE.Alternatively, you can use this Maven command line in your root project (where the
src
folder is):The standard output should be:
The MongoDB driver comes with a few helpers to ease the writing of these queries. Here's an equivalent query using the
Filters.eq()
method.Of course, I used a static import to make the code as compact and easy to read as possible.
In the previous example, the benefit of these helpers is not obvious, but let me show you another example where I'm searching all the grades with a student_id greater than or equal to 10,000.
As you can see, I'm using the
$gte
operator to write this query. You can learn about all the different query operators in the MongoDB documentation.The
find
method returns an object that implements the interface FindIterable
, which ultimately extends the Iterable
interface so we can use an iterator to go through the list of documents we are receiving from MongoDB:Lists are usually easier to manipulate than iterators, so we can also do this to retrieve directly an
ArrayList<Document>
:We could also use a
Consumer
which is a functional interface:As we saw above with the
Iterator
example, MongoDB leverages cursors to iterate through your result set.If you are already familiar with the cursors in the mongo shell, you know that transformations can be applied to it. A cursor can be sorted and the documents it contains can be transformed using a projection. Also, once the cursor is sorted, we can choose to skip a few documents and limit the number of documents in the output. This is very useful to implement pagination in your frontend for example.
Let's combine everything we have learnt in one query:
Here is the output we get:
Remember that documents are returned in the natural order, so if you want your output ordered, you need to sort your cursors to make sure there is no randomness in your algorithm.
To make my last query efficient, I should create this index:
With this index, we can see that we have no SORT stage, so we are not doing a sort in memory as the documents are already sorted "for free" and returned in the order of the index.
Also, we can see that we don't have any FETCH stage, so this is a covered query, the most efficient type of query you can run in MongoDB. Indeed, all the information we are returning at the end is already in the index, so the index itself contains everything we need to answer this query.
Let's edit the document with
{student_id: 10000}
. To achieve this, we will use the method updateOne
.Please create a class
Update
in the com.mongodb.quickstart
package with this code:As you can see in this example, the method
updateOne
takes two parameters:- The first one is the filter that identifies the document we want to update.
- The second one is the update operation. Here, we are setting a new field
comment
with the value"You should learn MongoDB!"
.
In order to run this program, make sure you set up your
mongodb.uri
in your system properties using your IDE if you want to run this code in your favorite IDE (see above for more details).Alternatively, you can use this Maven command line in your root project (where the
src
folder is):The standard output should look like this:
An upsert is a mix between an insert operation and an update one. It happens when you want to update a document, assuming it exists, but it actually doesn't exist yet in your database.
In MongoDB, you can set an option to create this document on the fly and carry on with your update operation. This is an upsert operation.
In this example, I want to add a comment to the grades of my student 10002 for the class 10 but this document doesn't exist yet.
As you can see, I'm using the third parameter of the update operation to set the option upsert to true.
I'm also using the static method
Updates.push()
to push a new value in my array comments
which does not exist yet, so I'm creating an array of one element in this case.This is the output we get:
The same way I was able to update one document with
updateOne()
, I can update multiple documents with updateMany()
.In this example, I'm using the same
updateOperation
as earlier, so I'm creating a new one element array comments
in these 10 documents.Here is the output:
Finally, we have one last very useful method available in the MongoDB Java Driver:
findOneAndUpdate()
.In most web applications, when a user updates something, they want to see this update reflected on their web page. Without the
findOneAndUpdate()
method, you would have to run an update operation and then fetch the document with a find operation to make sure you are printing the latest version of this object in the web page.The
findOneAndUpdate()
method allows you to combine these two operations in one.Here is the output:
As you can see in this example, you can choose which version of the document you want to return using the appropriate option.
I also used this example to show you a bunch of update operators:
set
will set a value.inc
will increment a value.rename
will rename a field.mul
will multiply the value by the given number.addToSet
is similar to push but will only push the value in the array if the value doesn't exist already.
Let's delete the document above. To achieve this, we will use the method
deleteOne
.Please create a class
Delete
in the com.mongodb.quickstart
package with this code:As you can see in this example, the method
deleteOne
only takes one parameter: a filter, just like the find()
operation.In order to run this program, make sure you set up your
mongodb.uri
in your system properties using your IDE if you want to run this code in your favorite IDE (see above for more details).Alternatively, you can use this Maven command line in your root project (where the
src
folder is):The standard output should look like this:
Are you emotionally attached to your document and want a chance to see it one last time before it's too late? We have what you need.
The method
findOneAndDelete()
allows you to retrieve a document and delete it in a single atomic operation.Here is how it works:
Here is the output we get:
This time we will use
deleteMany()
instead of deleteOne()
and we will use a different filter to match more documents.This is the output we get:
Deleting all the documents from a collection will not delete the collection itself because a collection also contains metadata like the index definitions or the chunk distribution if your collection is sharded for example.
If you want to remove the entire collection and all the metadata associated with it, then you need to use the
drop()
method.With this blog post, we have covered all the basic operations, such as create and read, and have also seen how we can easily use powerful functions available in the Java driver for MongoDB. You can find the links to the other blog posts of this series just below.
If you want to learn more and deepen your knowledge faster, I recommend you check out the M220J: MongoDB for Java Developers training available for free on MongoDB University.