Using Morphia- Java library for MongoDB

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:

The sample code used in this post can be downloaded from here. It requires you to setup Maven and Eclipse.

Comments

comments

About Mohamed Sanaulla

In his day job he works on developing enterprise applications using ADF. He is also the moderator of JavaRanch forums and an avid blogger.

Speak Your Mind

*