NoSQL Zone is brought to you in partnership with:

I like software - reading, tinkering, designing, coding. I have been doing so for 20 years or so and I would not mind continuing this for foreseeable future. Fortunately for me, this is my profession as well and I have managed to get paid for this for some 14 years now. Although I do not have any strong bias for any business domain, I have been working with some pretty big names in the finance domain and you might get a hint of that from my entries. Partha is a DZone MVB and is not an employee of DZone and has posted 20 posts at DZone. You can read more from them at their website. View Full User Profile

Java App on MongoDB in 30 Minutes

05.22.2012
| 19112 views |
  • submit to reddit

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 the 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 like minded 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.

Note: The original article is available at author's blog. Click here.  

Published at DZone with permission of Partha Bhattacharjee, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Herry Johnson replied on Tue, 2012/06/12 - 1:11pm

LD_LIBRARY_PATH isn't the same thing as java.library.path, but it should do. However I agree 100% with gimbal2's advice. Don't futz around with PATH, java.library.path, LD_LIBRARY_PATH, and who knows what else: use the current directory. It never fails.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.