In our post on developing sample Todo Application using JavaFX and MongoDB, we showed how we can use MongoDB, JavaFX to create a sample application. In that post we used the API provided by MongoDB to carry out the DB operations. In this post lets look at how we can use Morphia to carry out DB related operations.
Lets have a look at the DB operations were performed before the use of Morphia and compare them with the code which uses Morphia.
Saving of new Todo to DB
Without the use of Morphia, we wrote the following code:
//Without using Morphia DBObject dbObject = BasicDBObjectBuilder.start() .add("task",todo.getTask()) .add("completed",todo.isCompleted()) .add("added",todo.getAdded()) .get(); DB db = DbManager.getDb(DBNAME); DBCollection dbCollection = db.getCollection(COLLECTION_NAME); dbCollection.save(dbObject);For using Morphia, the class which has to be persisted to the MongoDB has to be annotated with @Entity and if you have an “id” attribute in the class, then it has to be annotated by @Id. Updating the Todo class definition and annotating with @Entity and @Id we have:
import java.util.Date; import org.bson.types.ObjectId; import com.google.code.morphia.annotations.Entity; import com.google.code.morphia.annotations.Id; @Entity public class Todo { private String task; private Boolean completed = false; private Date added; private Date finished; @Id private ObjectId id; public Todo(String task, boolean completed, Date added, ObjectId id){ this.task = task; this.completed = completed; this.added = added; this.setId(id); } public Todo(String task){ this.task = task; this.added = new Date(); this.completed = false; } @Override public String toString(){ return this.getAdded()+": "+this.getTask(); } public String getTask() { return task; } public void setTask(String task) { this.task = task; } public Boolean isCompleted() { return completed; } public void setCompleted(Boolean completed) { this.completed = completed; } public Date getAdded() { return added; } public void setAdded(Date added) { this.added = added; } public Date getFinished() { return finished; } public void setFinished(Date finished) { this.finished = finished; } public ObjectId getId() { return id; } public void setId(ObjectId id) { this.id = id; } }Note that the individual attributes need not be annotated as they are implicitly persisted by Morphia.
The saving of new Todo instance by using Morphia amounts to just 2 lines of code (excluding the creation of Morphia instance and Mongo instance which is generic across all operations)://Using Morphia Mongo mongo = new Mongo(); Morphia morphia = new Morphia(); morphia.map(Todo.class); Datastore ds = morphia.createDatastore(mongo, DBNAME); ds.save(todo);Datastore is the main class with which we would interact.
Obtaining all persisted Todo
The code without using Morphia is:
public static List<Todo> getAllTodos() throws UnknownHostException{ DB db = DbManager.getDb(DBNAME); DBCollection dbCollection = db.getCollection(COLLECTION_NAME); DBCursor dbCursor = dbCollection.find(); List<Todo> allTodos = new ArrayList<Todo>(); while ( dbCursor.hasNext()){ DBObject dbObject = dbCursor.next(); String task = String.valueOf(dbObject.get("task")); Date added = (Date)dbObject.get("added"); boolean completed = (Boolean)dbObject.get("completed"); Todo todo = new Todo(task, completed, added, ObjectId.massageToObjectId(dbObject.get("_id"))); allTodos.add(todo); } return allTodos; }and with the use of Morphia, it reduces to:
public static List<Todo> getAllTodos() throws UnknownHostException{ Datastore ds = getDatastore(); return ds.createQuery(Todo.class).asList(); }Note: getDatastore() and other related methods have been created to move the creation Datastore instance to one place. I will update the Maven project so that one can download the complete code and play around.
Obtaining all the Todos which are not marked as completed yet
The code without using Morphia is:
//Without using Morphia public static List<Todo> getOpenTodos() throws UnknownHostException{ DB db = DbManager.getDb(DBNAME); DBCollection collection = db.getCollection(COLLECTION_NAME); DBObject filterObject = BasicDBObjectBuilder.start() .add("completed",false) .get(); DBCursor dbCursor = collection.find(filterObject); List<Todo> openTodos = new ArrayList<Todo>(); while ( dbCursor.hasNext()){ DBObject dbObject = dbCursor.next(); String task = String.valueOf(dbObject.get("task")); Date added = (Date)dbObject.get("added"); boolean completed = (Boolean)dbObject.get("completed"); Todo todo = new Todo(task, completed, added, ObjectId.massageToObjectId(dbObject.get("_id"))); openTodos.add(todo); } return openTodos; }and the code after using Morphia is:
//Using Morphia public static List<Todo> getOpenTodos() throws UnknownHostException{ return getDatastore().createQuery(Todo.class) .field("completed") .equal(Boolean.FALSE) .asList(); }Setting/Updating the Todo as completed
This is nothing but updating the “completed” field of the Todo object to true. This when performed without using Morphia amounts to:
//Without Using Morphia public static void setTodoAsCompleted(Todo todoRef) throws UnknownHostException{ DB db = DbManager.getDb(DBNAME); DBCollection collection = db.getCollection(COLLECTION_NAME); DBObject queryObject = BasicDBObjectBuilder.start() .add("_id", todoRef.getId()) .get(); DBObject updateValue = BasicDBObjectBuilder.start() .add("completed",true) .get(); collection.update(queryObject,updateValue); }With Morphia it not straight forward though, let me show you the code first:
//Using Morphia public static void setTodoAsCompleted(Todo todoRef) throws UnknownHostException{ getDatastore().update( getDatastore().createQuery(Todo.class) .field("id") .equal(todoRef.getId()), getDatastore().createUpdateOperations(Todo.class) .set("completed", Boolean.TRUE) ); }In update, we need to construct a query to identify the right record to update and then the update operation which has to be performed on the identified record.
For the query part, we want to find out the record with the given ID:getDatastore().createQuery(Todo.class) .field("id") .equal(todoRef.getId())and for updating, we update the field “completed” and set it to TRUE
getDatastore().createUpdateOperations(Todo.class) .set("completed", Boolean.TRUE)the above two operations are passed to the update method of the Datastore class.
Advantages of using Morphia:
- The persisting of the instance and reading it from the db is handled by Morphia
- Considerable reduction in the number of lines of code
- The code is more readable than when the ORM API wasn’t used
I also tried MongoDB-Java-ORM(MJORM), but couldn’t get around even compiling the code. I then looked at Morphia and adopting it was so much easier.
Resources:
- Morphia Home page.
- Morphia JavaDOC
- Sample application developed without using Morphia
The sample code used in this post can be downloaded from here. It requires you to setup Maven and Eclipse.