ZK With MongoDB Part 1 - Using MongoDB Java Driver

From Documentation
DocumentationSmall Talks2012JanuaryZK With MongoDB Part 1 - Using MongoDB Java Driver
ZK With MongoDB Part 1 - Using MongoDB Java Driver

Author
Ashish Dasnurkar, Engineer, Potix Corporation
Date
January 19, 2012
Version
ZK 5


Introduction

ZK is primarily an UI framework and it doesn't make any assumptions about application's persistence layer or database. Application developers are free to use any database (relational or non-relational) and any data access layer solutions that work with those databases. Typically, for relational databases developers either use plain JDBC or some sophisticated object-relational mapping library like Hibernate. In this small talk series I will show how developers can develop non-relational database driven ZK applications.

I will describe a simple TODO list application that uses mongoDB which is a popular non-relational database. In this series I will show three approaches to work with mongoDB. In Part 1 I will use mongoDB Java driver APIs for TODO tasks CRUD operations which is similar to using JDBC driver for relational databases. In Part 2, I will use morphia , a lightweight type-safe library for mapping Java objects to/from mongoDB, which is similar to using Hibernate for relational databases. Finally for developers who are familiar with Spring framework, in Part 3 I will show how to leverage Spring Data mongoDB support to work with mongoDB database.

mongoDB Overview

mongoDB is a document oriented database that manages collections of BSON documents. Each BSON document is a set of fields. Each field is a key-value pair where key is a string and value can be other basic types, a BSON document or array of values. Below is a sample of tasks collection

{ "_id" : ObjectId("4e97d8b7dfa2925b0844a3c9"), "id" : "c011a1df-22ab-4607-a358-ea2f2873b51e", "name" : "Write article", "priority" : 1, "date" : ISODate("2011-10-06T16:00:00Z") }
{ "_id" : ObjectId("4e97d8e5dfa2925b0844a3ca"), "id" : "ce4a059f-1825-4410-bf95-4c30e11aca14", "name" : "Conference call", "priority" : 2, "date" : ISODate("2011-10-13T16:00:00Z") }
{ "_id" : ObjectId("4e97d8f0dfa2925b0844a3cb"), "id" : "0b0db7ac-b5db-40a6-9c8e-2c86581a3e89", "name" : "Release 5", "priority" : 1, "date" : ISODate("2011-10-27T16:00:00Z")}

In this example, each Task document has 5 fields. _id is auto-generated by mongoDB whenever a document is added to the collection. Remaining four fields - id,name,prority and date contains Task data. Note that mongoDB is schema free so you do not have to pre-define this Task document structure. It will be automatically created when first Task document is saved in the database. For this article all you need is an installed instance of mongoDB. You can follow mongoDB quick-start guide to setup and get the mongoDB running.

Using MongoDB Java Driver in TODO Application

TODO application Overview

I will use same TODO application for each part. The application itself is a simple ZUML page showing list of tasks to do in a listbox and couple input components to enter and/or edit task details. Users can add, update and delete tasks by clicking the related buttons.

It is based on ZK's MVC pattern.

Below is the View part todo.zul page

View

 

<window title="Simple Todo application with mongoDB Java driver" width="640px" border="normal" apply="org.zkoss.mongodb.controller.SimpleTodoController">
	<listbox id="tasks" multiple="true" rows="10">
		<listhead>
			<listheader label="Item" />
			<listheader label="Priority" width="50px" />
			<listheader label="Date" width="90px" />
		</listhead>
	</listbox>
	<groupbox>
		<caption label="Event" />
		Item: <textbox id="name" constraint="no empty" cols="25" />
		Priority: <intbox id="priority" cols="1" constraint="no empty"/>
		Date: <datebox id="date" cols="8" constraint="no empty"/>
		<button id="add" label="Add" />
		<button id="update" label="Update" />
		<button id="delete" label="Delete" />
	</groupbox>
</window>

mongoDB Java driver

First, either manually download mongoDB java driver from here and place it in your application classpath (WEB-INF/lib) or if you are using Maven, include the following dependency in your project pom.xml file

 

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

Model

Data for tasks listbox is ListModel model, a list of Task Javabean objects retrieved from mongoDB database. Below is Task Javabean definition

Task.java code

public class Task {
	private String id;
	private String name;
	private int priority;
	private Date executionDate;

	public Task(){ }

	public Task(String id,String name,int priority,Date date){
		this.id = id;
		this.name = name;
		this.priority = priority;
		this.setExecutionDate(date);
	}
	// setters and getter for all fields
}


Next I define a mongoDB connection manager singlton class that holds an instance of Mongo that represents a pool of database connections with internal pooling.


I use this Mongo instance to connect with "simpletasks" database using getDB(String) API.

 

public class MongoDBManager {

	private static Mongo mongo = null;
	private MongoDBManager() {};

	public static synchronized DB getDB(String dbName) throws Exception {
		if(mongo == null) {
			mongo = new Mongo();
		}
		return mongo.getDB(dbName);
	}
}
  • Note that database doesn't have to pre-exist. If it isn't created yet, mongoDB will create it for you.

    Next I create a TaskDAO for performing CRUD operations on "Tasks" collection within our "simpletasks" mongoDB database.
    Lets first see how to get list of all tasks in database.
     
    public List<Task> findAll(){
    		List<Task> tasks = new ArrayList<Task>();
    		try {
    			DB db = MongoDBManager.getDB("simpletasks");
    			DBCollection coll = db.getCollection("Tasks");
    			DBCursor cursor = coll.find();
    
    			while (cursor.hasNext()) {
    				DBObject dbobj = cursor.next();
    				Task task = new Task((String) dbobj.get("id"),
    						(String) dbobj.get("name"),
    						(Integer) dbobj.get("priority"),
    						(Date) dbobj.get("date"));
    				tasks.add(task);
    			}
    
    		} catch (Exception e) {}
    		return tasks;
    	}
    


    First I get a single connection with "simpletasks" database and use it to get "Tasks" collection instance. DBCollection#find() returns a cursor that can be used to iterate over set of documents in that collections. DBCursor#next() returns one DBObject instance that represents one document inside the collection. DBObject is a Map like representation of document stored in mongoDB. I manually retrieve each field from this DBObject instance and create a Task Javabean instance and add it to my tasks ArrayList. DBCollection has all the necessary APIs to insert, remove and update documents. Below is the method that inserts new Task document in "Tasks" collection using DBCollection#insert(DBObject) API

     
    
    private BasicDBObject toBasicDBObject(Task task) {
    		BasicDBObject newdbobj = new BasicDBObject();
    		newdbobj.put("id", task.getId());
    		newdbobj.put("name", task.getName());
    		newdbobj.put("priority", task.getPriority());
    		newdbobj.put("date", task.getExecutionDate());
    		return newdbobj;
    	}
    
    	public void insert(Task task){
    		try {
    			BasicDBObject newdbobj = toBasicDBObject(task);
    			DB db = MongoDBManager.getDB("simpletasks");
    			DBCollection coll = db.getCollection("Tasks");
    			coll.save(newdbobj);
    		} catch (Exception e) {}
    	}
    

    Controller

    Finally I apply a controller to our view and use TaskDAO to perform necessary CRUD operations as user interacts with the todo application.

    public class SimpleTodoController extends GenericForwardComposer {
    	Listbox tasks;
    	Textbox name;
    	Intbox priority;
    	Datebox date;
    
    	TaskDAO taskDao = new TaskDAO();
    
    	public void doAfterCompose(Component comp) throws Exception {
    		super.doAfterCompose(comp);
                    // retrieve and load previously created tasks if any
    		tasks.setModel(new ListModelList(taskDao.findAll()));
    		tasks.setItemRenderer(new ListitemRenderer() {
    			public void render(Listitem item, Object data) throws Exception {
    				Task task = (Task) data;
    				item.setValue(task);
    				new Listcell(task.getName()).setParent(item);
    				new Listcell("" + task.getPriority()).setParent(item);
    				new Listcell(new SimpleDateFormat("yyyy-MM-dd").format(task.getExecutionDate())).setParent(item);
    			}
    		});
    	}
            /** Set selected task details in input components */
    	public void onSelect$tasks(SelectEvent evt) {
    		Task task = (Task) tasks.getSelectedItem().getValue();
    		name.setValue(task.getName());
    		priority.setValue(task.getPriority());
    		date.setValue(task.getExecutionDate());
    	}
    	public void onClick$add(Event evt) {
    		Task newTask = new Task(UUID.randomUUID().toString(), name.getValue(), priority.getValue(), date.getValue());
    		try {
    			taskDao.insert(newTask);
    			tasks.setModel(new ListModelList(taskDao.findAll()));
    		} catch (Exception e) {}
    	}
    
    	// event handlers for update and delete buttons
    
    }
    

    Summary

    As you can see, using mongoDB Java driver low level APIs is bit tedious as you need to manually convert the DBObject into your Javabean entity class. Imagine the complexity when your entity beans compose out of other smaller entity beans. In such cases we can use Morphia which is a lightweight type-safe library that allows mapping Java objects to/from MongoDB. We will take a look at just that in Part 2 of this series.

    Download Sample Code

    You can download/clone the complete source code for this Part 1 application from its github repository here


    Comments



    Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.