Access MongoDB Database from a Spring Boot Application
1. Introduction and Setup
This is an example developing Spring Data MongoDB data access code. This uses the IntelliJ IDEA Community Edition (a free to use IDE for Java application development), Java SE (JDK version 8 or higher) and Spring Framework. This includes connecting to the MongoDB database server and performing basic CRUD operations. Then run the code as a Spring Boot application.
Spring Initializr provides an extensible API to generate JVM based projects, and to inspect the metadata (dependencies and versions) used to generate project. We will use Spring Initializr to create a project. Visit: https://start.spring.io/
The initializr page allows create a Spring Boot project with various options: Project Maven, Language Java, Spring Boot version, Project Metadata including the packaging as JAR and the Java version. Specify project meta data, for example, Name as "spring-mongo-demo" or "demo". We will include one more option, the Dependency, which is the Spring Data MongoDB (this is listed under the NoSQL section of the Dependency).
Now, we have selected all required options. Click Generate, and this generates a downloadable project ZIP folder. Download and extract the project folder into the directory from where you can access it from the IDE.
From the IDE:
- Open or import..., and select the newly downloaded and extracted project folder.
- This will open the project in the IDE development window, takes a few moments resolving dependencies, and the project is ready to run.
- You can navigate to the "src" folder in the IDE's Project window and open the main Java program (for example,
DemoApplication.java
).
Run the project, and the IDE builds the project and runs. The console window opens and shows that the Spring Boot application has run successfully.
2. Accessing MongoDB Database Server
You should have an installed MongoDB Community Server and started the server. The two common tools used to access the server are the mongo
shell (a command-line tool) and the Compass (a GUI tool). We will connect with the shell on the localhost
and default port 27017
.
Form the operating system command prompt, type: mongo
> show dbs
-- This lists all the database names.
> db
-- Shows the currently used database name. By default, this is "test", and you are connected to the "test" database.
> show collections
-- Lists the collections in the database. Initially, there will be no user created collections.
MongoDB data storage has databases. Database has collections. The data is stored as documents within collections. A document is a record, with a set of field names and values, similar to that of JSON. But, MongoDB documents are BSON (binary JSON) type with additional field types, like date, integer, fixed decimal, object and array.
We will create a database, collection, and add some documents from the Spring and Java code.
3. Java and Spring APIs
The code uses the Java SE, MongoDB Java Driver and the Spring Data MongoDB libraries and runs as a Spring Boot application.
Spring Data MongoDB has two main APIs: MongoTemplate
(MongoOperations
interface implementation) and the MongoRepository
(a Repository
interface extension). MongoTemplate has the methods to perform the CRUD operations. Repository APIs are built over the MongoTemplate
and are customizable. In addition, we can also use MongoDB Java Driver APIs within a Spring application.
We will use the MongoTemplate
class in the examples.
In MongoDB you can create a collection explicitly using a specific command, or when you insert a document in a non-existing collection, the collection and the document are created. If no database exists with the specified name, it is also created. The collection is created in the default "test" database, if no database is specified.
Spring allows configuring the default database to use, creating a thread-safe MongoTemplate
instance to be used across the application, and the database connection using the Java driver. This is some basic configuration.
Finally, we will implement the Spring Boot's CommandLineRunner
to run the database operations code, i.e., MongoTemplate
class methods. We will view the results in the IDE's console. Also, we will query the data in the mongo
shell.
4. Connecting to MongoDB with Spring Configuration
Register MongoDB and Spring. Create a MongoClient
object and MongoTemplate
instance using Java based metadata. For more detailed configuration options extend the AbstractMongoClientConfiguration
abstract class.
@Configuration
public class AppConfig {
public MongoClient mongoClient() {
return MongoClients.create("mongodb://localhost:27017");
}
public @Bean MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), "libraryDB");
}
}
Note the database connection URI "mongodb://localhost:27017" and the MongoTemplate
instance creation with "libraryDB" as the default database.
To use default settings this configuration is not required. By default, a MongoDB client connects to a database named "test" on the localhost
and port 27017
. The MongoClient
instance represents a pool of connections to the database. MongoClient
and MongoClients
are defined in the MongoDB Java driver API.
With the above configuration or default settings the MongoTemplate
instance is available in the application. MongoTemplate
lets save, update, and delete the domain objects and map those objects to documents stored in MongoDB.
5. Coding the Application
We will implement the CommandLineRunner
and the class will have methods to perform CRUD operations on the configured database and collection.
@Component
public class AppRunner implements CommandLineRunner {
@Autowired
MongoTemplate mongoTemplate;
@Override
public void run(String... args) throws Exception {
System.out.println("Collection Exists? " + mongoTemplate.collectionExists("book"));
}
}
Run the Spring Boot application, and the method prints a false
, as no collection with name "book" or the database "libraryDB" exist, yet. The database and the collection are created when we insert the first document
6. CRUD Operations
We can save, update and delete an object, using a Pojo (plain old Java object) class. The class maps to the document in the collection which will be affected. Define Book.java
class:
public class Book {
private String id;
private String title;
private String author;
private LocalDate published;
private Double goodreadsRating;
private List<String> genre;
public Book(String title, String author, LocalDate published) {
this.title = title;
this.author = author;
this.published = published;
}
// get methods for id, title, author and published
// set method for goodreadsRating and genre
// toString override method showing id, title and author
}
The class can be customized with annotations. @Document
and @Id
, are the most common. The Pojo class name maps to the collection; e.g., Book
maps to a collection named book
. @Document
allows specify a collection name, e.g., book_collection
or books
. By default, the collection name is determined by class name (not the fully qualified name).
The _id
field is a mandatory field in a MongoDB document and is unique. If no value is supplied the Java driver creates one, of type ObjectId
. There are options to use @Id
annotation or specify a field name as id
to map to the _id
field. The id
field in the Book
class is used to get its value.
6.1. Inserting Documents:
We will use the method MongoTemplate#insert(T objectToSave)
in the AppRunner
class to insert a Book
object as a document in the book
collection.
Book animalFarm = new Book("Animal Farm", "George Orwell", LocalDate.of(1945, 8, 17));
mongoTemplate.insert(animalFarm);
The insert returns the newly inserted object.
Spring Data MongoDB stores the document with the generated _id
field, the fields from the Java object and type information as fully qualified classname as a field _class
. The type to which the JSON object is unmarshalled is determined by the _class
attribute of the document. Note the fields with default value null
(the rating
and genre
) are not stored.
{
"_id" : ObjectId("5ef0226dbdf3ad423757b11a"),
"title" : "Animal Farm",
"author" : "George Orwell",
"published" : ISODate("1945-08-16T17:30:00Z"),
"_class" : "com.example.demo.Book"
}
There is an overloaded insert
and insertAll
methods that can insert multiple documents at once with a collection of Java objects.
Book brothersKaramazov = new Book("Brothers Karamazov", "Fyodor Dostoyevsky", LocalDate.of(1879, 1, 1));
Book crimeAndPunishmant = new Book("Crime and Punishment", "Fyodor Dostoyevsky", LocalDate.of(1866, 1, 1));
mongoTemplate.insert(Arrays.asList(brothersKaramazov, crimeAndPunishmant), Book.class);
NOTE: Most of these CRUD operation methods have variations (i.e., overloaded) to take collection name parameter in addition to the class name or instead of it.
6.2. Querying Documents
There are multiple ways and methods we can query the documents in a collection.
6.2.1. Using Query and Criteria classes:
The common way of querying is using the find methods, and there are multiple versions of this method. The method uses the Query
and Criteria
classes.
Criteria criteria = Criteria.where("title").is("Animal Farm");
Query query = Query.query(criteria);
Book book = mongoTemplate.findOne(query, Book.class);
System.out.println(book)
prints in the IDE console the toString
version of the Book
object:
Book{id='5ef0226dbdf3ad423757b11a', title='Animal Farm', author='George Orwell'}
Other variations of find method are find()
, findAll()
, findById()
and findDistinct()
. findAll
returns a collection of Book
objects:
List<Book> books = mongoTemplate.findAll(Book.class);
6.2.2. Creating a Query instance from a plain JSON string:
BasicQuery query = new BasicQuery("{ author: 'Fyodor Dostoyevsky' }");
List<Book> books = mongoTemplate.find(query, Book.class);
6.2.3. Other Methods:
The query()
provides methods for constructing lookup operations in a fluent way. Then there is the Aggregation Framework Support with the aggregate
method and the Query by Example.
6.2.4. Querying from Mongo Shell:
Connect to the database and query documents using find methods:
use libraryDB
db.book.findOne()
db.book.find().pretty()
Note both the find methods can take a query predicate document as a parameter, for example: { author: "George Orwell" }
6.3. Updating Documents
updateFirst
updates the first matching document for the given query criteria with the provided update. To update multiple documents use the updateMulti
method.
Query query = new Query(new Criteria("title").is("Animal Farm"));
Update update = new Update().set("goodreadsRating", 3.9)
.set("genre", Arrays.asList("Novel", "Allegory", "Satire" ));
UpdateResult result = mongoTemplate.updateFirst(query, update, Book.class);
System.out.println("Modified documents: " + result.getModifiedCount()); // prints modified count as 1
The updated document:
{
"_id" : ObjectId("5ef0226dbdf3ad423757b11a"),
"title" : "Animal Farm",
"author" : "George Orwell",
"published" : ISODate("1945-08-16T17:30:00Z"),
"_class" : "com.example.demo.Book",
"genre" : [
"Novel",
"Allegory",
"Satire"
],
"goodreadsRating" : 3.9
}
There are also an upsert
, save
, findAndModify
and findAndReplace
update related methods. In addition, there is also Aggregation Pipeline Updates introduced with the MongoDB version 4.2, where a pipeline is used to transform and update the data using the AggregationUpdate
class.
Updating multiple documents:
Query query = query(where("author").is("Fyodor Dostoyevsky"));
Update update = new Update().set("author", "Fyodor Mikhailovich Dostoyevsky");
UpdateResult result = mongoTemplate.updateMulti(query, update, Book.class);
System.out.println("Modified documents: " + result.getModifiedCount()); // prints modified count as 2
EDITED (26-Jun-2020): Corrected (the typo) the update's set
from title
to author
.
6.4. Deleting Documents
Delete a document based upon a supplied Java object or the query object.
Query query = query(where("title").is("Animal Farm"));
DeleteResult = mongoTemplate.remove(query, Book.class);
System.out.println("Deleted documents: " + result.getDeletedCount()); // prints deleted documents as 1
If you have the Book
object (with its id
value) then you can use the remove(Object object)
method. If the query criteria matches multiple documents, then all those are deleted. In addition there is also a findAndRemove
method.
7. Useful Links
The code used in the examples can be accessed at the GitHub Repsitory.