Learn MongoDB With Java 8 (Part 1)
Java 8's more functional tools give you more than what you need to connect to MongoDB, query the data, and even transform the results.
Join the DZone community and get the full member experience.
Join For FreeWelcome to my new tutorial series, Learn MongoDB with Java 8. I hope that you have a basic idea of Big Data and the new features offered in Java 8, as we use those features extensively in this tutorial.
In this article, we will learn:
- What MongoDB is.
- How to set up MongoDB.
- How to create a collection in MongoDB.
- How to insert documents in collections.
- How to write a simple Java program to connect to MongoDB and search a value from the Mongo collection.
Here are the short and crisp details about MongoDB.
MongoDB
MongoDB is a NoSQL database where we can store data in BSON format where a key represents the property and the value represents the property value stored against a key.
Documents
In Mongo, the document represents a data structure that can hold any number of key and value pairs. "Employee" can be represented as a document where the name, address, age, and its value are stored as key-value pairs in that document. Please note that documents are stored in binary JSON format, called BSON (Binary JSON).
An example of a document is:
{
"_id" : {
"$oid" : "58eb8c2b1de2b36bfcc74326"
} ,
"name" : “Shamik Mitra”
}
Collections
In Mongo, often, documents with the same structure are put into a bucket, called a collection. You can think of a collection as a table in a relational database, where every row represents a document. So we can say the Employee collection holds multiple Employee documents. Note that this is logical, By definition, a collection can contain any type of documents — for example, a collection can contain Employee documents as well as Car documents. There are no restrictions.
Note: While designing, it is preferable to create a collection basis on similarly structured documents.
No Schema
This is one of the key differences between a SQL and NoSQL database, although personally, I don’t like those terms. I believe it would be better to say relational and non-relational database. By NoSQL, we mean it has no predefined schema — it can hold anything in BSON format. To be specific, any data structure fits into a no-schema database, so it is suitable for storing unstructured data.
That makes life easier for the developer because, in a relational database, there is a fixed schema. If an Employee table contains name, age, and address columns it stores all data which maintains the same data structure. Now, if we want to modify the structure, say we want to an add a gender property, then we need to change the schema altogether to incorporate the new property. But in Mongo, as it is schema free, we can able to put any data structure with any combination of properties.
Note:Although MongoDB is schema free, while designing, we logically put the same structured documents into a collection, so it's maintaining an implicit schema!
Horizontal Scaling
The success of Big Data lies in horizontal scaling. As Mongo is a part of the Big Data stack, it also supports the same. Through horizontal scaling, MongoDB can distribute data to multiple nodes, each node representing a commodity machine — i.e a low-cost computer — and we can add and remove nodes with ease. So when we need to store more data, we can add new nodes without impacting existing architecture, wherein with vertical scaling (which RDBMS follows), we need a supercomputer, and data resides in a single centralized storage space.
Note: By distributing data over multiple nodes, Mongo makes itself a fault tolerant system. If one node goes down, we can use others to fetch data. Of course, with vertical scaling, as data resides in one centralized area, if that goes down, we lose all data, so it is considered to be a single point of failure.
Sharding
Sharding is a technique by which MongoDB breaks gigantic clumps of data to small chunks. Afterward, it creates replicas of each chunk, then distributes those chunks into multiple nodes. When queried, the server holds metadata information that tells the Mongo server which node has the data. The query then fetches data only from that node.
Mongo Setup
- Download the latest Mongo ZIP distribution from this link.
- Create a directory D:\InstalledApps and extract the ZIP in the same location.
- Rename the distribution folder to mongodb.
- Now create a folder named data inside the mongodb folder: D:\InstalledApps\mongodb\data
- Now open a command prompt and go to D:\InstalledApps\mongodb\bin using the following command:
cd D:\InstalledApps\mongodb\bin
- Start MongoDB server using:
mongod.exe --dbpath D:\InstalledApps\mongodb\data
It will start the server in localhost:27017 .
Set Up Mongo Client
We will use RoboMongo as our mongo client. It has a nice looking GUI, so we can easily create a collection, then add documents using RoboMongo GUI.
Download RoboMongo from here. Extract the ZIP file and click on RoboMongo.exe. It will launch the RoboMongo GUI.
Connect to the Mongo server using the following credentials:
Host: localhost and port: 27017 .
Create a new database: Now in RoboMongo GUI, right-click on the computer icon in the right-hand panel to Create a Database. Use test as the name of the database.
Create a collection: Now right-click on the collection icon in the right-hand panel to create a collection. Create a new collection named Employee.
Insert a document: Now right-click on the Employee collection and hit Insert Document, then paste the following in text area
{ "name" : "Shamik" , "address" : "1 Nivedita Lane" , "age" : 32}
Hit save. That will insert a document into the Mongo Server.
The same thing can be done through the Mongo console.
Java Time
Now we are all set to write our first Java program to connect to MongoDB. We'll use:
Eclipse Neon
Eclipse maven plugin.
Java 8
Step 1
Now we will create a maven project in Eclipse called mongoExample. Set the Java compiler version as Java 1.8. Please download Java 1.8 if you do not have Java 8.
Step 2
Write a pom.xml like the following:
<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>
<groupId>com.example</groupId>
<artifactId>mongoExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
</project>
Here we use the mongo-java-driver JAR to connect to the Mongo Server using Java 8.
Create a MongoContext
The next part is to connect to the Mongo Server using Java code. We will create a top-level API class that will abstract the logic of connecting to the MongoDB Server and expose some utility methods for our API clients:
/**
*
*/
package com.example.config;
import java.net.UnknownHostException;
import java.util.function.Function;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
/**
* @author Shamik Mitra
*
*/
public class MongoContext {
private static MongoContext ctx = new MongoContext();
private MongoClient client;
private DB db;
private MongoContext(){
try{
init();
}catch(Exception ex){
ex.printStackTrace();
}
}
private void init() throws UnknownHostException{
this.client = new MongoClient("localhost" , 27017);
}
public static MongoContext get(){
return ctx;
}
public MongoContext connectDb(String dbname){
if(db !=null){
throw new RuntimeException("Already conected to " + db.getName() + "can't connect " + dbname);
}
this.db = client.getDB(dbname);
System.out.println("DB Details :: " + db.getName());
return ctx;
}
public <T,X> DBCursor findByKey(String collectionName,String key,T value,Function<T,X> convertDataType){
DBCollection collection = db.getCollection(collectionName);
BasicDBObject searchQuery = new BasicDBObject();
searchQuery.put(key, convertDataType.apply(value));
System.out.println("search Query ::" + searchQuery);
DBCursor cursor = collection.find(searchQuery);
return cursor;
}
}
Code Explanation
Here, I created a Singleton MongoContext Object, which connects to MongoDB using the MongoClient class in the init method. Note that the MongoClient class is a part of the Mongo Java driver JAR: this.client = new MongoClient("localhost" , 27017);
Now, we return that context with the static get() method.
At this point, we've successfully established a connection with the Mongo server.
The next thing to do is choose a database to connect to. I have created a generic method called connectDb(String dbName), where the caller of our API will pass the database name it wants to connect. Please note that the return type of this method is MongoContext itself. I'm using the Fluent API technique.
After that, we want to do a query to fetch documents from the database.
So I create a Generic method:
public <T,X> DBCursor findByKey(String collectionName,String key,T value,Function<T,X> convertDataType)
This method takes four parameters:
collectionName: It takes the target collection name from the caller. Here, Employee is the target collection. Again, think of it as a table name in RDBMS.
Key: Key is the property by which caller wants to search the collection. You can think it is the column name, which we would put into a SQL where clause.
Value: Value represents the value caller searched for. It is same as the value provided in a SQL where clause.
Function<T, X>: Here, I define a Functional Interface (Java 8), which takes one type of data and transforms it to another datatype. ( T -> X). I need this for when I try to write a generic method findbyKey.
At the moment, I do not know what key a client will search for. For example, in the Employee collection, the name key is a String, but the age key is an Integer. So, if a caller wants to search by the name key, the desired datatype must be a String. An age's datatype will be an Integer. So, I need some strategy that transforms a value to its desired datatype. We'll use lambda expressions for that. Take a look at this post for a detailed understanding of lambda expressions.
In the method body, I got the name of the collection, then created a SearchQuery using BasicDataObject. After that, I put the key and value, passed by the caller, into that query. At last, we pass the above query to the collection, get the result, and wrap in a DBCursor Object, which internally holds the result.
Time to Test
Create a class called Main.java
/**
*
*/
package com.example.mongotest;
import com.example.config.MongoContext;
import com.mongodb.DBCursor;
/**
* @author Shamik Mitra
*
*/
public class Main {
/**
*
*/
public static void main(String[] args) {
DBCursor result = MongoContext.get().connectDb("test").findByKey("Employee", "age", 32,
(value) -> new Integer(value));
while (result.hasNext()) {
System.out.println(result.next());
}
}
}
Here, I create the MongoContext, then connect the test DB and call the findByKey method by passing Employee as the collection, age as the key, and the value as 32. After that, using lambda expressions, we tell to our API that the desired data type is an Integer.
Please note that in the findByKey method, I took the datatype of the value parameter as T.
All right, so why do we need a functional interface? I can simply pass the T datatype to the query object, but I use a functional interface to welcome any future validation or sanity test of the value provided by the user before passing it to the query Object.
Output:
DB Details :: test
search Query ::{ "age" : 32}
{ "_id" : { "$oid" : "58ecde108b308657b44937b1"} , "name" : "Shamik" , "adress" : "1 Nivedita Lane" , "age" : 32}
Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments