Saturday, April 28, 2012

MongoDB - Jongo and Morphia

When presented with the opportunity to store data into a database without schema restrictions, some of us are hesitant to leap into the NoSQL void.  I'm not.  I am tired of having to work with RDBMS restrictions for simple data changes in my applications.  I am tired of having to cram non-relational domain models into relational schemas.  I want an easier way to update my application data structures without having to constantly change my database, while still maintaining backwards compatibility, and providing minimal data transformation.  Lately I have been working with MongoDB and a couple of APIs (Jongo and Morphia) as well as MongoVUE.  What follows is my odyssey into MongoDB.

First I started with running MongoDB on my laptop.  On my Windows 7 machine I am using MongoDB 2.0.4 (mongodb-win32-x86_64-2.0.4).  The MongoDB daemon start-up script I am using is:

call mongod.exe --dbpath "C:/Tools/mongodb-win32-x86_64-2.0.4/data/db"

Once it is running, I can immediately connect to it with MongoVUE.  MongoVUE is a GUI that allows me to view the database, as well as edit data and other database level tasks.

The cool thing about MongoVUE is that most tasks that you execute in the GUI are read out in the log.  This has the added benefit of helping new comers to MongoDB more quickly learn the shell commands that are used to manage the database. Without the GUI, I would use the MongoDB shell.  In Windows this is launched by mongo.exe.

The Jongo API

Jongo allows Java developers to write programs that use the MongoDB Java Driver, while using MongoDB shell style JSON queries.  To see how it works, I will first use the MongoDB Java Driver API to insert an Employee object into the database.  First I start by connecting the database:

Next I get an Employee object.  Note:  MongoDriver is just the name of the Java class that I use to run this code.  I then serialize the Employee object into JSON via the FlexJSON API.  There is a MongoDB JSON API that includes a serializer, but it does not know how to serialize objects that are not intrinsic to MongoDB.  I wanted to serialize the object graph into JSON to experiment with how I would push JSON into MongoDB from my application.

Finally I parse the JSON into an Object and cast it to the com.mongodb.DBObject, and insert it into the "employees" collection.

To verify the insert I can view the employees collection in the MongoVUE GUI.  The interesting thing here is that the two date elements in the Employee document are serialized as longs and stored in the document store as numbers.  This seems counter intuitive to how dates should b e stored in MongoDB (ISO-Date).  In fact when working with the com.mongodb.util.JSON parser, I found a bug where longs were being improperly cast and truncated to ints.  This happened when the dates serialized were before 1/1/1970.  This bug is fixed in the MongoDB Java Driver version 2.8.0.  I am using version 2.7.3, so I had to fix it locally.

At this point the reader should notice that the Employee object contains two member objects:  Address and Department.  The JSON for this Employee object is seen below:

Now I can read the employee document using the Jongo API using a JSON query syntax:

The output is seen below:

The Morphia API

According Google Code, "Morphia is a lightweight, type-safe library for mapping Java objects to/from MongoDB."  Below is an example that I wrote to stuff a collection of Employee objects in the database as JSON documents.

Using Morphia requires a few Morphia annotations (@Entity, @Id, @Embedded) in the model objects (Employee, Address, Department).  In the Employee class I use all three.  The @Entity annotation is similar to the JPA @Entity.  The "employees" argument tells the Morphia API that this MongoDB document will be added to the employees collection.

The interesting thing about MongoDB collections is that if they do not yet exist in the database, they will be created in-line when documents are inserted.  This is also true of the database itself.  If the database specified by the API does not yet exist in the MongoDB environment, it too will be created on the first document insert.

The @Id annotation is needed for the for the "auto-generated" ObjectId field.  @Embedded is used on the Address and Department members to add them as embedded documents to the Employee document.  @Embedded is also on each of the Address and Department classes.

After execution I can again view the documents in MongoVUE.  In this Morphia insert, I have correctly mapped the java.util.Date to the ISODate MongoDB Date/Time data type.

Next I will investigate a scalable solution for MongoDB as well as Couchbase.