MongoDB in 30 minutes.

I have recently been bitten by the NoSQL bug - or as colleague of mine Mark Atwell coined "Burn the Where!" movement. While I have no intention of shunning friendly "SELECT ... WHERE" anytime soon - or in foreseeable future, I did manage to get my hands dirty with some code. In this article I share the knowledge of my first attempts in NoSQL world.

In this article we will aim to get a basic java application up and running that is capable of interacting with MongoDB. By itself that is not really a tall task and perhaps you could get that in under 10 minutes. Click here or click here or click here, for some excellent material. However, I wanted to push it a bit further.

I want to add ORM support. I have chosen Morphia for this article. I also want to add DAO pattern, unit testing, and logging. In short, I want to it feel, "almost like" the kind of code that most of us would have written for enterprise applications using, let's say Java, Hibernate and Oracle. And, we will try to do all this in under 30 minutes.

My intention is to give a reassurance to Java + RDBMS developers that Java + NoSQL is not very alien. It is similar code and easy to try out. It is perhaps pertinent to add at this point that I have no affinity to NoSQL and no issues with RDBMS. I beieve they both have their own uses (click here for some excellent material). As a technologist, I just like knowing my tools better so that I can do justice to my profession. This article is solely aimed at helping likeminded people to dip their toes in NoSQL.

Ok, enought talk. Let's get started. You will need a few softwares / tools before you follow this article. Download and install MongoDB server, if you have not already done so. I am assuming you have Java, some Java IDE and a build and release tool. I am using jdk 7, Eclipse (STS) and Maven 3 for this article.

I start by creating a vanilla standard java application using Maven. I like using a batch file for this.

File: codeRepo\MavenCommands.bat
ECHO OFF

REM =============================
REM Set the env. variables.
REM =============================
SET PATH=%PATH%;C:\ProgramFiles\apache-maven-3.0.3\bin;
SET JAVA_HOME=C:\ProgramFiles\Java\jdk1.7.0

REM =============================
REM Create a simple java application.
REM =============================
call mvn archetype:create ^
       -DarchetypeGroupId=org.apache.maven.archetypes ^
       -DgroupId=org.academy ^
       -DartifactId=dbLayer002

pause
Import it in Eclipse. Use Maven to compile and run just to be sure.
mvn -e clean install. 
This should compile the code and run the default tests as well. Once you have crossed this hurdle, now let's get down to some coding. Let's create an Entity object to start with. I think a Person class with fname would serve our purpose. I acknowledge that it is trivial but it does the job for a tutorial.

File: /dbLayer002/src/main/java/org/academy/entity/Person.java
package org.academy.entity;

public class Person {
       private String fname;

       [...]
}
I have not mentioned the getters and setters for brevity.

Now let us get the MongoDB java driver and attach it to the application. I like have Maven do this bit for me. You could obviously do this bit manually as well. Your choice. I am just lazy.

File: /dbLayer002/pom.xml
[...]
<!-- MongDB java driver to hook up to MongoDB server -->
<dependency>
       <groupId>org.mongodb</groupId>
       <artifactId>mongo-java-driver</artifactId>
       <version>2.7.3</version>
</dependency>
[...]
This will allow us to write a util class for connecting to MongoDB server instance. I am assuming you have a server up and running in your machine with default settings.

File: /dbLayer002/src/main/java/org/academy/util/MongoUtil.java
public class MongoUtil {

       private final static Logger logger = LoggerFactory
                       .getLogger(MongoUtil.class);

       private static final int port = 27017;
       private static final String host = "localhost";
       private static Mongo mongo = null;

       public static Mongo getMongo() {
               if (mongo == null) {
                       try {
                               mongo = new Mongo(host, port);
                               logger.debug("New Mongo created with [" + host + "] and ["
                                               + port + "]");
                       } catch (UnknownHostException | MongoException e) {
                               logger.error(e.getMessage());
                       }
               }
               return mongo;
       }
}
We will need logger setup in our application for this class to compile. Click here for my article around logging. All we need to do is to hook up Maven with the correct dependencies.

File: /dbLayer002/pom.xml
[...]
<slf4j.version>1.6.1</slf4j.version>
[...]
<!-- Logging -->
<dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
       <version>${slf4j.version}</version>
</dependency>
<dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>jcl-over-slf4j</artifactId>
       <version>${slf4j.version}</version>
       <scope>runtime</scope>
</dependency>
<dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
       <version>${slf4j.version}</version>
       <scope>runtime</scope>
</dependency>  
And also, we will need to specify, exactly what to log.

File: /dbLayer002/src/java/resources/log4j.properties
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# configure A1 to spit out data in console
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
At this point, you already have an Entity class and a utility class to hook up to the database. Ideally I would go about writing a DAO and then somehow use the ORM to join up the DAO with the database. However, Morphia has some excellent DAO support. Also it has some annotations to tie up the Entity with database elements. So, although I would have liked the Entity and DAO to be totally agnostic of the ORM and database, it is not the case here. I know on the face of it, it sounds like Morphia or MongoDB is forcing me to deviate from good code structure, let me hasten to add, that it is not any worse than other ORMs e.g. Hibernate with Annotations would have also forced me to make the same kind of compromise.

So, let's bring in Morphia in the picture. Enters the stage our ever helpful tool, Maven. A bit of an avoidable hitch here. When I was writing this document I could not get the version of Morphia that I wanted in the central Maven repository. So, I had to configure Maven to use the Morphia repository as well. Hopefully this is just a temporary situation.

File: /dbLayer002/pom.xml
[...]
<!-- Required for Morphia -->
<repositories>
       <repository>
               <id>Morphia repository</id>
               <url>http://morphia.googlecode.com/svn/mavenrepo/</url>
       </repository>
</repositories>
[...]
<!-- Morphia - ORM for MongoDB -->
<dependency>
       <groupId>com.google.code.morphia</groupId>
       <artifactId>morphia</artifactId>
       <version>0.98</version>
</dependency>                                 
As I mentioned above, Morphia allows us to annotate the Entity class (much like Hibernate annotations). So, here is how the updated Entity class looks like.

File: /dbLayer002/src/main/java/org/academy/entity/Person.java
[...]
@Entity
public class Person {
       @Id private ObjectId id;
       [...]


And now we can also add a DAO layer and lean on the BasicDAO that Morphia provides.

File: /dbLayer002/src/main/java/org/academy/dao/PersonDAO.java
public class PersonDAO extends BasicDAO<Person, ObjectId> {

       public PersonDAO(Mongo mongo, Morphia morphia, String dbName) {
               super(mongo, morphia, dbName);
       }
       [...]
The beauty of the BasicDAO is that just by extending that, my own DAO class already has enough functionality to do the basic CRUD operations, although I have just added a constructor. Don't believe it? Ok, lets write a test for that.

File: /dbLayer002/src/test/java/org/academy/dao/PersonDAOTest.java
public class PersonDAOTest {
       private final static Logger logger = LoggerFactory
                       .getLogger(PersonDAOTest.class);

       private Mongo mongo;
       private Morphia morphia;
       private PersonDAO personDao;
       private final String dbname = "peopledb";

       @Before
       public void initiate() {
               mongo = MongoUtil.getMongo();
               morphia = new Morphia();
               morphia.map(Person.class);
               personDao = new PersonDAO(mongo, morphia, dbname);
       }

       @Test
       public void test() {
               long counter = personDao.count();
               logger.debug("The count is [" + counter + "]");

               Person p = new Person();
               p.setFname("Partha");
               personDao.save(p);

               long newCounter = personDao.count();
               logger.debug("The new count is [" + newCounter + "]");

               assertTrue((counter + 1) == newCounter);
       [...]
As you might have already noticed. I have used JUnit 4. If you have been following this article as is till now you would have an earlier version of JUnit in your project. To ensure that you use JUnit 4, you just have to configure Maven to use that by adding the correct dependency in pom.xml.

File: /dbLayer002/pom.xml
<!-- Unit test. -->
<dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.10</version>
       <scope>test</scope>
</dependency>
You are good to go. If you run the tests they should pass. Of course you could / should get into your database and check that the data is indeed saved. I will refer you to the MongoDB documentation which I think are quite decent.

Last but not least, let me assure you that BasicDAO is not restrictive in any way. I am sure puritans would point out that if my DAO class needs to extend the BasicDAO that is a limitation on the source code structure anyway. Ideally should not have been required. And I agree with that. However, I also want show that for all practical purposes of DAO, you have sufficient flexibility. Let's go on and add a custom find method to our DAO, that is specific to this Entity i.e. Person. Let's say we want to be able to search on the basis of firstname and that we want to use regular expression for that. Here is how the code will look like.

File: /dbLayer002/src/main/java/org/academy/dao/PersonDAO.java
public class PersonDAO extends BasicDAO<Person, ObjectId> {

       [...]
       public Iterator<Person> findByFname(String fname){
               Pattern regExp = Pattern.compile(fname + ".*", Pattern.CASE_INSENSITIVE);
               return ds.find(entityClazz).filter("fname", regExp).iterator();
       }
       [...]
}
Important to reemphasize here that, I have just added a custom search function to my DAO, by adding precisely three lines of code (four if you add the last parentheses). In my books, that is quite flexible. Being true to my unflinching love for automated testing, lets add a quick test to check this functionality before we wrap up.

File: /dbLayer002/src/test/java/org/academy/dao/PersonDAOTest.java
[...]
Iterator<Person> iteratorPerson = personDao.findByFname("Pa");
int personCounter = 0 ;
while(iteratorPerson.hasNext()){
       personCounter ++;
       logger.debug("["+personCounter+"]" + iteratorPerson.next().getFname());
}
[...]
Before you point out, yes, there are no assert() here, so this is not a real test. Let me assure you that my test class is indeed complete. It's just that this snippet here does not have the assert function.

So, that's it. You have used Java to connect to a NoSQL database, using an ORM, through a DAO layer (where you have used the ORM's functionalities and done some addition as well). You have also done proper logging and unit testing. Not bad use of half an hour, eh? Happy coding.

Further reading

  • A version of this article - slightly edited, is also available at this link at Javalobby.

If you want to get in touch, you can look me up at Linkedin or Google + .

3 comments:

Anonymous said...

Very Nice article. Thanks. PS. You may wish to define all acronyms to increase your audience.

Anonymous said...

NIce article. Tried it on Mac. So if anyone plans to try this on Mac just remember the following

1. While installing mongodb on ur mac instead of following line

mongod --config /etc/mongod.conf

use

./mongod --dbpath /data/db

(if the data directory is set to /data/db)

Of course this is not a professional approach. It's just to have a mongo db running for this tutorial.

2. As mentioned in the tutorial, Morphia repository will still need to be added to maven configuration.

Overall a great article. Keep posting!

Anonymous said...

Great tutorial. Thanks a lot!