Codementor Events

Access MongoDB Database from a Spring Boot Application

Published Jun 22, 2020Last updated Jun 26, 2020
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.

The code used in the examples can be accessed at the GitHub Repsitory.

Discover and read more posts from Prasad Saya
get started