enes

Enes Altınkaya

How to Connect and Make Queries on Mongodb using Spring

How to Connect and Make Queries on Mongodb using Spring

Creating the project

Lets start by heading to https://start.spring.io/ and creating our project.

  • Add Web and MongoDB dependencies then hit the Generate Project button.

  • Unzip the file and load the project on your ide.
You can install Lombok plugin to your ide and add it's dependency into pom.xml to avoid writing getters and setters everytime you create a java bean.
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
    <scope>provided</scope>
</dependency>
package com.example.demo;

import lombok.Data;

@Data
public class Person {
    private String name;
    private int age;
}

Getters, Setters, ToString, EqualsAndHashCode and No Args Constructor methods are all automatically set up for the above class.

  • The resulting pom.xml.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.6</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
  • Make sure MongoDB is up and running.

Setting up the properties

  • Open src/main/resources/application.properties file and add the following lines.
spring.data.mongodb.database=demo
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

This is all we need to do to establish the database connection. Spring Boot will handle the rest.

Creating the Person bean

  • Create a Beans package and Person class.
package com.example.demo.Beans;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

// Lombok's data annotation
@Data
// Spring will create MongoDB collection/table
@Document
public class Person {
    // Table id
    @Id
    private String id;

    // A unique index for email field will be created
    @Indexed(unique = true)
    private String email;
    private String name;
    private int age;
}

Creating the controller

  • Create a Controllers package and PersonController class.
package com.example.demo.Controllers;

import com.example.demo.Beans.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/person")
public class PersonController {

    @Autowired
    private MongoTemplate mongoTemplate;

    // POST http://localhost:8080/person
    @PostMapping
    public ResponseEntity savePerson(@RequestBody Person person) {
        mongoTemplate.save(person);
        return ResponseEntity.ok("OK");
    }
}

Starting the application and adding a person

  • Start the application.























 




  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.3.RELEASE)

2019-03-23 09:52:01.680  INFO 5410 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on enes with PID 5410 (/Users/enes/Downloads/demo/target/classes started by enes in /Users/enes/Downloads/demo)
2019-03-23 09:52:01.683  INFO 5410 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2019-03-23 09:52:02.557  INFO 5410 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2019-03-23 09:52:02.579  INFO 5410 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17ms. Found 0 repository interfaces.
2019-03-23 09:52:03.401  INFO 5410 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-03-23 09:52:03.466  INFO 5410 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-03-23 09:52:03.466  INFO 5410 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.16]
2019-03-23 09:52:03.508  INFO 5410 --- [           main] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/enes/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2019-03-23 09:52:03.871  INFO 5410 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-03-23 09:52:03.871  INFO 5410 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2093 ms
2019-03-23 09:52:04.343  INFO 5410 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=MULTIPLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
2019-03-23 09:52:04.344  INFO 5410 --- [           main] org.mongodb.driver.cluster               : Adding discovered server localhost:27017 to client view of cluster
2019-03-23 09:52:04.411  INFO 5410 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1, serverValue:35}] to localhost:27017
2019-03-23 09:52:04.416  INFO 5410 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=ServerVersion{versionList=[4, 0, 4]}, minWireVersion=0, maxWireVersion=7, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=3021897}
2019-03-23 09:52:04.417  INFO 5410 --- [localhost:27017] org.mongodb.driver.cluster               : Discovered cluster type of STANDALONE
2019-03-23 09:52:04.678  INFO 5410 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:36}] to localhost:27017
2019-03-23 09:52:05.004  INFO 5410 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-03-23 09:52:05.262  INFO 5410 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-03-23 09:52:05.268  INFO 5410 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 4.409 seconds (JVM running for 5.554)

You'll notice that the MongoDB connection is automagically established for us.

  • Create a couple Person records by sending post requests using your favorite rest client. I am using Insomnia Rest Client.

post request

Using Robo 3T check the records.

robo 3t check the record

Now that we have a couple records, we can make some queries.

Query MongoDB records

  • Add following following methods to PersonController and test them with rest client.
package com.example.demo.Controllers;

import com.example.demo.Beans.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/person")
public class PersonController {

    @Autowired
    private MongoTemplate mongoTemplate;

    // POST http://localhost:8080/person
    @PostMapping
    public ResponseEntity savePerson(@RequestBody Person person) {
        mongoTemplate.save(person);
        return ResponseEntity.ok("OK");
    }

    // Get all person records
    // GET http://localhost:8080/person
    @GetMapping
    public ResponseEntity getAllRecords() {
        List<Person> people = mongoTemplate.findAll(Person.class);
        return ResponseEntity.ok(people);
    }

    // Find by email
    // GET http://localhost:8080/person/[email protected]
    @GetMapping("/email")
    public ResponseEntity findByEmail(@RequestParam("email") String email) {
        Query query = new Query();
        query.addCriteria(Criteria.where("email").is(email));
        Person person = mongoTemplate.findOne(query, Person.class);
        return ResponseEntity.ok(person);
    }

    // Find by email containing text
    // GET http://localhost:8080/person/emailContaining?email=demo
    @GetMapping("/emailContaining")
    public ResponseEntity emailContains(@RequestParam("email") String email) {
        Query query = new Query();
        query.addCriteria(Criteria.where("email").regex(email));
        List<Person> people = mongoTemplate.find(query, Person.class);
        return ResponseEntity.ok(people);
    }

    // Find by age greater than
    // GET http://localhost:8080/person/age?ageGreaterThan=27
    @GetMapping("/age")
    public ResponseEntity findByAge(@RequestParam("ageGreaterThan") int age) {
        Query query = new Query();
        query.addCriteria(Criteria.where("age").gt(age));
        List<Person> people = mongoTemplate.find(query, Person.class);
        return ResponseEntity.ok(people);
    }

    // Find by age between
    // GET http://localhost:8080/person/ageBetween?age1=25&age2=27
    @GetMapping("/ageBetween")
    public ResponseEntity findByAgeBetween(@RequestParam("age1") int age1,
                                           @RequestParam("age2") int age2) {
        Query query = new Query();
        query.addCriteria(Criteria.where("age").gt(age1).lt(age2));
        List<Person> people = mongoTemplate.find(query, Person.class);
        return ResponseEntity.ok(people);
    }

}

insomina get request

Pagination and Sort

PageRequest.of()'s page starts with 0. So first page is 0.

    // Page and sort
    // GET http://localhost:8080/person/pageAndSort?page=0&size=3
    @GetMapping("/pageAndSort")
    public ResponseEntity pageAndSort(@RequestParam("page") int page, @RequestParam("size") int size) {
        Query query = new Query();
        Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, "age");
        query.with(pageable);
        List<Person> people = mongoTemplate.find(query, Person.class);
        return ResponseEntity.ok(people);
    }

Use the following method to check if your queries are using an index.













 
 




    // import org.bson.Document;
    public String explainQuery(Query query, String collectionName) {
        MongoCollection<Document> collection = mongoTemplate.getCollection(collectionName);
        FindIterable<Document> result = collection.find(query.getQueryObject()).modifiers(new Document("$explain", true));
        return result.first().toJson();
    }
    
    // Find by email
    @GetMapping("/email")
    public ResponseEntity findByEmail(@RequestParam("email") String email) {
        Query query = new Query();
        query.addCriteria(Criteria.where("email").is(email));
        String explainResult = explainQuery(query, "person");
        System.out.println(explainResult);
        Person person = mongoTemplate.findOne(query, Person.class);
        return ResponseEntity.ok(person);
    }

If the explain result contains "stage" : "IXSCAN" then the index is being used.

That's all for now. Have a nice day.

Share on