In this recipe, we will look at executing the query and insert
operations using the Java client for MongoDB. Unlike the Python programming language, Java code snippets cannot be executed from an interactive interpreter, and thus we will be having some unit test cases already implemented, whose relevant code snippets will be shown and explained.
For this recipe, we will start a standalone instance. Refer to the Installing single node MongoDB recipe from Chapter 1, Installing and Starting the Server for instructions on how to start the server.
The next step is to download the Java project, mongo-cookbook-javadriver
, from the Packt website. This recipe uses a JUnit test case to test out various features of the Java client. In this whole process, we will use some of the most common API calls and thus learn to use them.
To execute the test case, one can either import the project in an IDE-like Eclipse and execute the test case or execute the test case from the command prompt using Maven.
The test case that we will execute for this recipe is com.packtpub.mongo.cookbook.MongoDriverQueryAndInsertTest
.
$ mvn -Dtest=com.packtpub.mongo.cookbook.MongoDriverQueryAndInsertTest test
Everything should get executed fine and the test case should succeed if the Java SDK and Maven are properly set up and the MongoDB server is up and running and listening to port 27017
for the incoming connections.
We will now open the test class that we executed and see some of the important API calls in the test
method. The super class of our test
class is com.packtpub.mongo.cookbook.AbstractMongoTest
.
We start by looking at the getClient
method in this class. The client
instance that has been created is an instance of the com.mongodb.MongoClient type
. There are several overloaded constructors for this class; however, we use the following to instantiate the client:
MongoClient client = new MongoClient("localhost:27017");
Another method to look at is getJavaDriverTestDatabase
in the same abstract class that gets us the database instance. This instance is synonymous to the implicit variable db
in the shell. Here in Java, this class is an instance of the com.mongodb.DB
type. We get an instance of this DB
class by invoking the getDB()
method on the client instance. In our case, we want the DB
instance for the javaDriverTest
database, which we get as follows:
getClient().getDB("javaDriverTest");
Once we get the instance of com.mongodb.DB
, we use it to get the instance of com.mongodb.DBCollection
, which would be used to perform various operations—find
and insert
—on the collection. The getJavaTestCollection
method in the abstract test class returns one instance of DBCollection
. We get an instance of this class for the javaTest
collection by invoking the getCollection()
method on com.mongodb.DB
as follows:
getJavaDriverTestDatabase().getCollection("javaTest")
Once we get an instance of DBCollection
, we are now ready to perform the operations on it. In the scope of this recipe, it is limited to the find
and insert
operations.
Now, we open the main test case class, com.packtpub.mongo.cookbook.MongoDriverQueryAndInsertTest
. Open this class in an IDE or a text editor. We will look at the methods in this class. The first method that we will look at is findOneDocument
. Here, the line of our interest is the one that queries for the document with the value of _id
as 3
: collection.findOne(new BasicDBObject("_id", 3))
.
This method returns an instance of com.mongodb.DBObject
, which is a key value map returning the fields of a document as a key and the value of this corresponding key. For instance, to get the value of _id
from the returned DBObject
instance, we invoke result.get("_id")
on the returned result.
Our next method to inspect is getDocumentsFromTestCollection
. This test case executes a find
operation on the collection and gets all the documents in it. The collection.find()
call executes the find
operation on the instance of DBCollection
. The return value of the find
operation is com.mongodb.DBCursor
. An important point to note is that invoking the find
operation itself doesn't execute the query but just returns the instance of DBCursor
. This is an inexpensive operation that doesn't consume server-side resources. The actual query gets executed on the server side only when the hasNext
or next
method is invoked on the DBCursor
instance. The hasNext()
method is used to check if there are more results and the next()
method is used to navigate to the next DBObject
instance in the result. An example usage of the DBCursor
instance returned to iterate through the results is as follows:
while(cursor.hasNext()) { DBObject object = cursor.next(); //Some operation on the returned object to get the fields and //values in the document }
We now look at two methods, withLimitAndSkip
and withQueryProjectionAndSort
. These methods show us how to sort, limit the number of results, and skip a number of initial results. As we can see, the sort, limit, and skip methods are chained to each other:
DBCursor cursor = collection .find(null) .sort(new BasicDBObject("_id", -1)) .limit(2) .skip(1);
All these methods return an instance of DBCursor
itself, which allows us to chain the calls. These methods are defined in the DBCursor
class, which changes certain states according to the operation that they perform in the instance and has return this at the end of the method to return the same instance.
Remember that the actual operation is invoked on the server only on invoking the hasNext
or next
method on DBCursor
. Invoking any method such as sort
, limit
, and skip
after the execution of the query on the server will throw java.lang.IllegalStateException
.
We used two variants of the find
method. One accepts one parameter for the query to be executed and one accepts two parameters—the first one for the query and the second is another DBObject
, which is used for projection that will return only a selected set of fields from the document in the result.
For instance, the following query from the withQueryProjectionAndSort
method of the test case selects all the documents as the first argument as null
and the returned DBCursor
will have documents containing just one field called value
:
DBCursor cursor = collection .find(null, new BasicDBObject("value", 1).append("_id", 0)) .sort(new BasicDBObject("_id", 1));
The _id
field is to be explicitly set to 0
, or else it will be returned by default.
Finally, we look at two more methods in the test case, insertDataTest
and insertTestDataWithWriteConcern
. We use a couple of variants of the insert
method in these two methods. All insert
methods are invoked on the DBCollection
instance and return an instance, com.mongodb.WriteResult
. The result can be used to get the error that occurred during the write operation by invoking the getLastError()
method, the number of documents inserted using the getN()
method, and the write concern for the operation among the few operations. Refer to the Javadoc of the MongoDB API for more detail on the methods. The two insert operations that we did are as follows:
collection.insert(new BasicDBObject("value", "Hello World")); collection.insert(new BasicDBObject("value", "Hello World"), WriteConcern.JOURNALED);
Both of these accept a DBObject
instance for the document to be inserted as the first parameter. The second method allows us to provide the write concern to be used for the write
operation. There are insert
methods in the DBCollection
class that allow bulk insert as well. Refer to the Javadocs for more details on various overloaded versions of the insert
method.
The Javadocs for the current version of the MongoDB driver can be found at https://api.mongodb.org/java/current/.