ZK With MongoDB Part 2 - Using Morphia
Ashish Dasnurkar, Engineer, Potix Corporation
January 20, 2011
ZK 5
Introduction
Part 1 introduced you how to write a non-relation database driven ZK application. In particular, I used mongoDB and its Java driver low level APIs. The problem with that approach was I had to manually convert results returned from Java driver APIs to model Javabean instances. Wouldn't it be better if we could just talk to database in terms on POJOs? Morphia, a type-safe Java library does exactly that. It allows mapping Javabean objects to/from mongoDB. Let see how we can use it in our TODO sample application
Using Morphia in TODO Application
Morphia
Follow the dependencies documentation to include Morphia in your project. I use maven so I simply included following dependency in my pom.xml
<dependency>
<groupId>com.google.code.morphia</groupId>
<artifactId>morphia</artifactId>
<version>0.99</version>
</dependency>
View
View part remains the same as in Part 1. Only change is I have a new controller to use morphia for managing task CRUD operations.
<window title="To do list" width="640px" border="normal" apply="org.zkoss.mongodb.controller.MongoDBMorphiaDemoCtrl">
<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>
Model
Like Hibernate, Morphia uses annotations for defining mapping between POJO and MongoDB document templates. Below I annotate Task Javabean properties with Morphia annotations
@Entity("tasks")
public class Task {
@Id
private ObjectId id;
@Indexed(value = IndexDirection.ASC, name = "taskName", unique = true)
private String name;
@Indexed(value = IndexDirection.ASC, name = "taskPriority")
private int priority;
@Indexed(value = IndexDirection.ASC, name = "executionDate")
private Date executionDate;
// getters and setters
}
Here Entity annotation defines mongoDB collection in which Task Javabean will be stored. Id annotation marks a field in an @Entity
to be the id field in mongoDB. MongoDB will auto assign a unique id into it. Indexed annotation defines Javabean property is to be indexed. More on Morphia annotations, refer to here.
Now, as in Part 1 we need a DAO to make persistence calls to MongoDB as it is a good practice to encapsulate persistence calls within DAO so that calling code (controller in this case) can use it without knowing the underlying details. Morphia has a built-in support for DAO interface in the form of BasicDAO implementation of it. This abstract class implements all the basic CRUD methods on model objects. I can just extend from this BasicDAO and since it uses generics to define a parameter class I wouldn't even need to cast the results returned from this DAO methods. Below is my TaskDAO implementation;
public class TaskDAO extends BasicDAO<Task, ObjectId> {
public TaskDAO(Mongo mongo, Morphia morphia) {
super(mongo, morphia, "todo");
}
}
As you can see, all it takes is an instance of Morphia and an instance of Mango. Similar to Part 1 we can reuse mango instance by defining a singleton object to contain it. Morphia instance is also recommended to create once and reuse. So I have defined a MangoDBManager singleton class that has 2 static methods to maintain single instance of Mango as well as Morphia as shown below;
public class MongoDBManager {
private static Mongo mongo = null;
private static Morphia morphia = null;
private MongoDBManager() {};
public static synchronized DB getDB() throws Exception {
if(mongo == null) {
mongo = new Mongo();
}
return mongo.getDB("test");
}
public static synchronized Mongo getMongo() throws Exception {
if(mongo == null) {
mongo = new Mongo();
}
return mongo;
}
public static synchronized Morphia getMorphia() throws Exception {
if(morphia == null) {
mongo = getMongo();
morphia = new Morphia();
morphia.mapPackage("org.zkoss.mongodb.model");
}
return morphia;
}
}
The only other thing notable here is the call to Morphia#mapPackage(String)
which tells morphia from which Java package model classes are to be mapped to MongoDB.
Controller
Finally I define a controller class that uses TaskDAO to perform necessary CRUD operations as user interacts with the TODO application.
public class MongoDBMorphiaDemoCtrl extends GenericForwardComposer {
Listbox tasks;
Textbox name;
Intbox priority;
Datebox date;
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
TaskDAO taskDao = new TaskDAO(MongoDBManager.getMongo(), MongoDBManager.getMorphia());
List<Task> taskList = taskDao.find().asList();
tasks.setItemRenderer(new ListitemRenderer() {
@Override
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);
}
});
tasks.setModel(new ListModelList(taskList));
}
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();
newTask.setName(name.getValue());
newTask.setPriority(priority.getValue());
newTask.setExecutionDate(date.getValue());
try {
TaskDAO taskDao = new TaskDAO(MongoDBManager.getMongo(), MongoDBManager.getMorphia());
taskDao.save(newTask);
tasks.setModel(new ListModelList(taskDao.find().asList()));
} catch (Exception e) {
e.printStackTrace();
}
}
// event handlers for update and delete buttons
}
Summary
Morphia with its annotations and excellent DAO support makes it really intuitive and easy to map application model POJOs to MongoDB document templates. Also since it uses generics to define DAO parameter class there is no need for any explicit casting of returned objects from DAO methods. BasicDAO has generic find()
methods, however it is recommended to implement your own custom finder methods to filter data and for custom sorting. In Part 3 we will take a look at Spring Data support for MongoDB.
Download Sample Code
You can download/clone the complete source code for this Part 2 application from its github repository from here.
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |