MongoDB and Java CRUD operations

MongoDB has been out for quite a while now and with it’s 3.2 release out in Dec 2015 many of the online tutorials have become outdated as the syntax and language drivers have evolved. Let’s take a look at an introduction to the 3.2 MongoDB driver in Java. We’ll cover some real basic implementation but showcase the driver syntax and some basic MongoDB documents.

I am building this project using IntelliJ 15.0.2 and utilizing Maven for dependency management. The code is available on GitHub: here. You will want to add the following dependency to your Maven project:

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.2.1</version>
</dependency>

We also will need a running MongoDB server. I will assume that you have MongoDB downloaded and installed on your local machine for the purposes of this tutorial. Our Java class will be accessing the MongoDB server on localhost, on the default port of 27017. It is also assumed that authentication for the MongoDB server is not implemented. For information on implementing access control on a MongoDB server, take a look here.

For this introductory walk-through, we are going to be doing everything inside of our main method in our Java class. In a later post we’ll see how the project can be refactored to create methods for doing our activities.

I want to show how to do a few important tasks with MongoDB. Namely,

  • Connect to a database
  • Insert individual documents into the database
  • Insert multiple documents into the database
  • Find and print documents from the database
  • Update a document.
  • Drop (delete) documents from the collection.
  • Show how to drop an entire collection.

Connect to a Database

We need to connect to specify the name of the database to use and, if the database does not exist, MongoDB creates it for us.

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoDatabase;

public class MongoJava {

    public static void main(String[] args) {
        try {
            // Connect to MongoDB Server on localhost, port 27017 (default)
            final MongoClient mongoClient = new MongoClient(new MongoClientURI("mongodb://localhost:27017"));
            // Connect to Database "cartoon"
            final MongoDatabase database = mongoClient.getDatabase("cartoon");
            System.out.println("Successful database connection established. \n");

        } catch (Exception exception) {
            System.err.println(exception.getClass().getName() + ": " + exception.getMessage());
        }
    }
}

Awesome, assuming you have MongoDB up and running when we compile and run the code we will get the expected output of:

Successful database connection established.

Great! Now we need to put some data into our cartoon database. Let’s add a character to a characters collection. We do this by creating a BSON Document object, adding (appending) our data to that object, and then inserting it into the collection with the insertOne() method.

Java Crud Operations

Insert Individual Documents

We’ll create two new documents. One for information about Mickey Mouse and one for Charlie Brown.

Our import additions
import com.mongodb.ErrorCategory;
import com.mongodb.MongoWriteException;
import com.mongodb.client.MongoCollection;

import org.bson.Document;

Adding documents

            //Insert a document into the "characters" collection.
            MongoCollection collection = database.getCollection("characters");

            Document mickeyMouse = new Document();
            Document charlieBrown = new Document();

            mickeyMouse.append("_id", 1)
                    .append("characterName", "Mickey Mouse")
                    .append("creator", new Document("firstName", "Walt").append("lastName", "Disney"))
                    .append("pet", "Goofy");

            charlieBrown.append("_id", 2)
                    .append("characterName", "Charlie Brown")
                    .append("creator", new Document("firstName", "Charles").append("lastName", "Shultz"))
                    .append("pet", "Snoopy");

            try {
                collection.insertOne(mickeyMouse);
                collection.insertOne(charlieBrown);
                System.out.println("Successfully inserted documents. \n");
            } catch (MongoWriteException mwe) {
                if (mwe.getError().getCategory().equals(ErrorCategory.DUPLICATE_KEY)) {
                    System.out.println("Document with that id already exists");
                }
            }

We should expect the following output:

Successful database connection established.

Successfully inserted documents.

I know what you’re thinking. Are they really in there? We can go to the Mongo Shell by running mongo cartoon from a command prompt we will open a Mongo Shell and be using the cartoon database. We know that our data is in the characters collection so if we do a find() on our collection, and pretty print it with db.characters.find().pretty() we will get back our two documents.

{
        "_id" : 1,
        "characterName" : "Mickey Mouse",
        "creator" : {
                "firstName" : "Walt",
                "lastName" : "Disney"
        },
        "pet" : "Goofy"
}
{
        "_id" : 2,
        "characterName" : "Charlie Brown",
        "creator" : {
                "firstName" : "Charles",
                "lastName" : "Shultz"
        },
        "pet" : "Snoopy"
}

Notice how we can have documents inside documents like the creator document. Pretty cool, eh? MongoDB allows for a rich schema design in which we can embed information inside other information. Pretty powerful.

Maintenance and Multiple Inserts

This next step we’re going to do a few things. Since we are inserting our documents with specific _id values, we can’t run it again and try to again insert Mickey Mouse into our collection as we’ll generate a Duplicate Key error. So, we’ll do a bit of maintenance at the beginning of our code to delete the current characters collection each time we run our program and yet keep the data in the database at the end.

Let’s get some output about our collection size and also generate multiple documents using the insertMany() method of the driver. We’ll start with inserting documents starting at an _id of 3 (since we already have a 1 and 2) and generate enough so we have 50 documents in our collection.

Our import additions
import java.util.ArrayList;
import java.util.List;

Multiple inserts

            // Delete the collection and start fresh - add before the initial inserts
            collection.drop();

            

            // Basic data on collection
            System.out.println("Collection size: " + collection.count() + " documents. \n");

            // Create and insert multiple documents
            List documents = new ArrayList();
            for (int i = 3; i < 51; i++) {
                documents.add(new Document ("_id", i)
                        .append("characterName", "")
                        .append("creator", "")
                        .append("pet", "")
                );
            }
            collection.insertMany(documents);

            // Basic data on collection
            System.out.println("Collection size: " + collection.count() + " documents. \n");

Very nice. Now we get some output with data about our growing collection!

Successful database connection established.

Successfully inserted documents. 

Collection size: 2 documents. 

Collection size: 50 documents. 

Updating Documents

We should probably consider learning how to update a document as well. After all, we have 50 documents in the collection but only two of them have any useful data. We use the updateOne() method to update a single document along with the $set operator. We’ll print out the document with the _id of 3, update it, and print it out again to show the change. We can find our document to print using a eq Query Filter, or equals filter.

Our import additions
import com.mongodb.client.model.Filters;

Updates, find with filter

            // Update a document
            // print the third document before update.
            Document third = collection.find(Filters.eq("_id", 3)).first();
            System.out.println(third.toJson());

            collection.updateOne(new Document("_id", 3),
                    new Document("$set", new Document("characterName", "Dilbert")
                            .append("creator", new Document("firstName", "Scott").append("lastName", "Adams"))
                            .append("pet", "Dogbert"))
            );

            System.out.println("\nUpdated third document:");
            Document dilbert = collection.find(Filters.eq("_id", 3)).first();
            System.out.println(dilbert.toJson());

Nice work! Our output now is:

Successful database connection established.

Successfully inserted documents. 

Collection size: 2 documents. 

Collection size: 50 documents. 

Original third document:
{ "_id" : 3, "characterName" : "", "creator" : "", "pet" : "" }

Updated third document:
{ "_id" : 3, "characterName" : "Dilbert", "creator" : { "firstName" : "Scott", "lastName" : "Adams" }, "pet" : "Dogbert" }

Whew! Almost there. Let’s print out the entirety of our collection. We do this with a MongoCursor and iterate over all of the documents.

Our import additions
import com.mongodb.client.MongoCursor;

Find all with cursor

            // Find and print ALL documents in the collection
            System.out.println("Print the documents.");

            MongoCursor cursor = collection.find().iterator();
            try {
                while (cursor.hasNext()) {
                    System.out.println(cursor.next().toJson());
                }

            } finally {
                cursor.close();
            }

With all of the new output, and most of it empty, perhaps we should clean up our collection a bit. We currently have 50 documents in there, but only three of them have any pertinent data. Let’s get rid of all documents that don’t have data using the deleteMany() function. We’re going to need another filter here to delete documents whose _id is greater than or equal to 4. Fortunately there is a built in filter, gte that will do just that!

Our final code for this tutorial then is available here as a gist.

            //Delete data
            System.out.println("\nDelete documents with an id greater than or equal to 4.");
            collection.deleteMany(Filters.gte("_id", 4));

            // Find and print ALL documents in the collection
            System.out.println("\nPrint all documents.");

            MongoCursor cursor2 = collection.find().iterator();
            try {
                while (cursor2.hasNext()) {
                    System.out.println(cursor2.next().toJson());
                }

            } finally {
                cursor2.close();
            }

As we would expect, our collection is now left with only three documents, one each for Mickey Mouse, Charlie Brown, and Dilbert. Our final code for this tutorial then is available here as a gist.

Summary

We have learned quite a bit. We have seen how to connect to an active MongoDB server, create new documents, update documents, find documents in our collection, and delete documents. In the database world that is called CRUD, which is an acronym for Create, Read, Update, Delete. Pretty great.

Happy coding!

Facebooktwitterredditlinkedinmail

Java getters and setters

getters and setters come out of one of the fundamental Object Oriented Programming (OOP) concepts called encapsulation. Encapsulation is a way of wrapping data (variables) and the coding doing something with that data (methods) together. We encapsulate class variables and hide them from other classes and make them accessible only through methods of the current class.

Encapsulation with Getters and Setters

To achieve encapsulation in Java

  • Declare the variables of a class as private.
  • Provide public setter and getter methods to modify and view the variables values.

That sounds all great and, if you are anything like me, doesn’t really mean much without an example. So, let’s do that.

Employee.java
public class Employee{

   private String name;
   private String idNum;
   private int age;

   public int getAge(){
      return age;
   }

   public String getName(){
      return name;
   }

   public String getIdNum(){
      return idNum;
   }

   public void setAge( int newAge){
      age = newAge;
   }

   public void setName(String newName){
      name = newName;
   }

   public void setIdNum( String newId){
      idNum = newId;
   }
}

Obviously, we would store much more about an employee, but this is just a sample. Now, to the question of how do you access this “hidden” data from another class.

RunEmployee.java
public class RunEmployee{

   public static void main(String args[]){
      Employee employee = new Employee();
      employee.setName("James");
      employee.setAge(20);
      employee.setIdNum("12343ms");

      System.out.print("Name: " + employee.getName() +  
                                   " | Age: " + employee.getAge() +  
                                   " | Id: " + employee.getIdNum());
    }
}

If we were to run this we would get the following output:

Name: James | Age: 20 | Id: 12343ms

Encapsulation Benefits

Some of the benefits of encapsulation include:

  • The fields of a class can be made read-only or write-only.
  • A class can have total control over what is stored in its fields.
  • The users of a class do not know how the class stores its data. A class can change the data type of a field and users of the class do not need to change any of their code.

Happy coding!

Facebooktwitterredditlinkedinmail