loading...

Insert Data in MongoDB with Spring Boot - Building a Blog

shadowphoenix profile image Rose Originally published at rosafiore.eu ・6 min read

Last time, we set up our database and Spring Boot project, and secured our database credentials. Now, we have to configure MongoDB in our project. Then we can start to insert data in mongodb and getting a feel for the queries to get the data from the database.

Configuring Spring Boot to Access MongoDB

To get started, we need to configure our Spring application, so our application knows where the database is to begin with. We do that by adding the following lines to the application.properties file in the resources directory.

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=rosafiore
spring.data.mongodb.authentication-database=admin

When configuring MongoDB, we added a user to our database. This user uses the admin database to authenticate, and thus to access the other databases. That's why we need to define the authentication database, rather than just inputting the credentials to the database we will be talking to.

However, we still have to insert our secured credentials. Truth be told, it took a long time to get this one figured out. There was no clear resource online that I could find. So, after a lot of digging and trying, I have managed to create a mongo configuration that inserts the secured credentials and creates a connection to the database.

MongoConfig

So, let's get started with this new config file. First off, we need to create a new class in our project. I call it MongoConfig, because it contains the configuration for MongoDB. We have to annotate the class with the @Configuration and @EnableMongoRepositories. That way, Spring Boot knows that this is a configuration class, and thus must be taken along during initialization and to enable the repositories. Furthermore, we need to inherit the AbstractMongoClientConfiguration for a couple of methods to complete our configuration. So, our bare config class now looks like this:

@Configuration
@EnableMongoRepositories
public class MongoConfig extends AbstractMongoClientConfiguration {

    @Override
    protected String getDatabaseName() {
        return null;
    }
}

In this class, we will create our own MongoClient to insert data in mongodb. For this, we need the properties we've defined in our application.properties file. We can access the properties by using a spring annotation on our local variable: @Value("${<property name>}"). With this construction, we create our variables with our property values and use them for configuring our client. One such variable will look like this in code:

@Value("${spring.data.mongodb.database}")
private String database;

As you might have seen a bit before, the MongoConfig class inherited the method getDatabaseName(). There, we have to replace the null value with the database variable. Now, we need to dive in to configuring the MongoClient itself.

MongoClientSettings

After searching for some time, I came across the mongodb documentation for connecting to a database. Further down the page, I finally found what I was looking for: applying custom settings. I decided to create a separate private method to configure the settings to keep things tidy. Here is the code for my settings and I'll walk you through the code as we go.

private MongoClientSettings setMongoClientSettings() {
    MongoCredential credential = MongoCredential.createCredential(
        Config.USERNAME, authDatabase, Config.PASSWORD);

    return MongoClientSettings.builder()
            .credential(credential)
            .applyToClusterSettings(builder ->
                    builder.hosts(Arrays.asList(new ServerAddress(host, port))))
            .build();
}

I start off with defining the credentials and the authentication database for my client. That is where I get to use my secured database credentials, instead of hardcoding them.

Then comes the MongoClientSettings.builder(), that is where the actual settings are created. The first thing I do, is setting the credentials to the one I created earlier in the method. Next, I need to configure the location of the database in the applyToClusterSettings. Here, I set the host to a new ServerAddress with the host and port number. Finally, the building can create the settings and return those. The settings are then used to create the MongoClient for Spring Boot to use.

@Override
@Bean
public MongoClient mongoClient() {
    return MongoClients.create(setMongoClientSettings());
}

So, the MongoConfig class now looks like this:

@Configuration
@EnableMongoRepositories
public class MongoConfig extends AbstractMongoClientConfiguration {

    @Value("${spring.data.mongodb.host}")
    private String host;

    @Value("${spring.data.mongodb.port}")
    private int port;

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Value("${spring.data.mongodb.authentication-database}")
    private String authDatabase;

    @Override
    protected String getDatabaseName() {
        return database;
    }

    @Override
    @Bean
    public MongoClient mongoClient() {
        return MongoClients.create(setMongoClientSettings());
    }

    private MongoClientSettings setMongoClientSettings() {
        MongoCredential credential = MongoCredential.createCredential(
            Config.USERNAME, authDatabase, Config.PASSWORD);

        return MongoClientSettings.builder()
                .credential(credential)
                .applyToClusterSettings(builder ->
                        builder.hosts(Arrays.asList(new ServerAddress(host, port))))
                .build();
    }
}

Insert Data in MongoDB

With our configuration completed, it is time to set up the rest of the code to insert data in mongodb. For this, we first need to create a class that is basically the blueprint of our object. In my case, I want to start out with a simple blog post object and the data I expect in it.

BlogPost Model

As I explained in Choosing a Database: SQL or NoSQL?, my blogpost object contains the following properties:

  • EndPoint
  • Publish Date
  • Categories
  • Tags
  • Blog Post (the content)

I reflected these properties in my BlogPost class which looks like this (I removed the getters and setters to avoid clutter):

@Document(collection = "blogposts")
public class BlogPost {

    @Id
    private String documentId;
    @Indexed(unique = true)
    private String endPoint;
    private Date publishDate;
    private List<String> categories;
    private List<String> tags;
    private String article;
}

As you can see, I annotated the class with the @Document annotation. That allows me to choose the name of the collection where mongodb will be storing my blog posts. Furthermore, I have created a documentId with the @Id annotation to inform mongodb that this property will contain the ID for every blog post object stored in the database. Finally, I have added the @Indexed(unique = true) annotation to my EndPoint property. This annotation allows me to have mongodb check if the property is actually unique. I don't want duplicate endpoints for my blog posts after all!

BlogPost Repository and Controller

With the model complete, it is time to insert the first blog post in the database. For this, we need a Repository first. Fortunately, this is a relatively simple one, I only need to create the interface and that is about it for now.

With that, the BlogPostRepository looks like this:

@Repository
public interface BlogPostRepository extends MongoRepository <BlogPost, Long> {
}

That is it for the repository, now we can continue with the controller. The controller contains the endpoints for our requests. I've made this a very simple controller, because it is still a proof of concept for me. I created two methods: one for posting a blog post and one for getting a blog post. For the POST method, I have hardcoded the values for the blog post.

In order to insert data into mongodb, I'll be using MongoTemplate which I only have to autowire into the application like this:

@Autowired
private MongoTemplate mongoTemplate;

With the mongoTemplate autowired, creating the POST and GET method was relatively easy and straightforward.

@PostMapping
    public void createPost() {
        BlogPost blogPost = new BlogPost();
        blogPost.setEndPoint("/test");
        blogPost.setPublishDate(new Date());
        blogPost.setCategories(Arrays.asList("TestCategory"));
        blogPost.setTags(Arrays.asList("TestTag"));
        blogPost.setArticle("testPost.md");

        mongoTemplate.save(blogPost, "blogposts");
    }

    @GetMapping
    public List<BlogPost> getPost() {
        Query query = new Query();
        query.addCriteria(Criteria.where("endPoint").is("/test"));
        return mongoTemplate.find(query, BlogPost.class);
    }
}

Of course, the only way to know for sure, is to test it. So, I went into Postman, inserted the requests and finally, after a long days struggle I get the following response when trying to obtain the blog post I had inserted.

Getting my test post back from the database

With that, I have finally managed to insert data in mongodb! Now, I can start with expanding the backend code with more endpoints. Furthermore, I need to decide how to proceed with this project and what steps I will take next.

Posted on by:

shadowphoenix profile

Rose

@shadowphoenix

A curious mind with an interest in all things software engineering.

Discussion

markdown guide
 

Hey Rose! I have some constructive feedback on some of your code. It may look a bit cleaner if you use a constructor for your BlogPost model instead of using setters. Also you declared a MongoRepository interface, but never actually made use of it. From my own research it seems MongoTemplate is the better way to go since it's more flexible so I think you are on a good path. Keep up the great work! I'm making a YouTube series using the same stack: youtube.com/channel/UCxxHkYTfLe5EM...

 

Hey Steven!
Thank you for the feedback! I must admit that I had completely forgotten to write the constructor, so I'll definitely add that asap.
About the MongoRepository and MongoTemplate, it was hard to find proper resources and to figure out the actual difference between the two. I am using MongoTemplate and I think that somewhere in the process I figured that I had to use the MongoRepository in order to use MongoTemplate. I know, bit weird, but that's what you get when working with new stuff. ^,^" I definitely want to take a deeper dive into the difference between these two to figure out what's best for me to use.
I'll definitely check out your YT vid, and thanks again for the feedback! :D

 

I can explain the difference between MongoTemplate and MongoRepository. So Mongo Repository is from Spring Data JPA. Out of the box you get methods like save, deleteById, findById, and findAllById. There's also a way you can write queries just by creating a specific method. So let's say you want to get a List if Blog Posts from a particular category you could define a method without any implementation called List findAllByCategory(String category). Under the hood Spring writes the appropriate query for you. It's pretty cool stuff!
MongoTemplate is a bit more lower level where you need to write your own queries. With embedded documents and denormalization it can be easier to write complex queries with MongoTemplate. For simple things I would use MongoRepository. I've seen some examples where both are used together in a hybrid approach. I hope that helps.