Monday, June 27, 2011

Morphia for MongoDB ORM

We have seen enough of "Object/Relational mapping with the JPA" is used with the SQL based databases. It is more popular, as it gives a simple layer of access to the database without depending on underlying database management system. This helps to hide a database, which might be hard to understand.

NoSQL based databases increase in popularity due to various requirements. So there should be a way to provide a additional Object/Relational mapping. This extra feature is provided by the Google code project called Morphia. This library helps to get a simple view of the database rather than seeing the complex JSON view. I simply tried the following steps to setup my sample project.

1) Downloaded the Mongo java connector
2) Downloaded the Morphia library
3) Created sample model classes based on Pizza (I tried just before the lunch time :))
4) Tried Simple, OneToMany, Embedded, Query examples

I will explain simple save & query example based on Morphia.

Step1 : Annotations

1) Entity Annotation - need to specified for the model classes. If you want a different name to be specified for the datacollection, you can specify the custom name within the brackets. Here I have used "pizza_tb" as the specific name for the data collection.

Eg:

@Entity("pizza_tb")

public class Pizza {

...

...

}


2) Id annotation - will be used to specify the id field. It is mandatory for a entity class to have ObjectId type field.

Eg:

@Id
private ObjectId id;


3) Embedded Annotation - when you want to embed a class within a entity class, then you can declare it as "Embedded". You still get a chance to specify the custom name for that field.

Eg:

@Embedded("pizza_type")
private PizzaType pizzaType;

4) Reference Annotation - another entity can be referred within the current entity.

Eg:

@Reference
private List<pizzacontent> pizzaContents;


5) Transient Annotation - to avoid a field to be mapped as a database field.

Eg:

@Transient
private String comment;


6) Indexed Annotation - to index a field

Eg:

@Indexed(value = IndexDirection.ASC, name = "IDX_NAME", unique = true)
String name;


Step2 : Define Entity classes

1) Pizza class - here it is necessary to have the ObjectId type Id field.

@Entity("pizza")
public class Pizza {

@Id
private ObjectId id;
private String name;
private String description;

// pizzaType is to be embedded with the Pizza class
@Embedded("pizza_type")
private PizzaType pizzaType;

// Collection of pizzaContents to be referred
@Reference
private List<pizzacontent> pizzaContents;

public Pizza() {
// default contructor for Morphia
}

public ObjectId getId() {
return id;
}

public void setId(ObjectId id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}


public PizzaType getPizzaType() {
return pizzaType;
}


public void setPizzaType(PizzaType pizzaType) {
this.pizzaType = pizzaType;
}


public List<pizzacontent> getPizzaContents() {
return pizzaContents;
}


public void setPizzaContents(List<pizzacontent> pizzaContents) {
this.pizzaContents = pizzaContents;
}
}


2) PizzaType class - this has the Embedded annotation to tell the system, that it is just going to be embedded into a another main class. But this is not mandatory.

@Embedded
public class PizzaType {
private String typeName;
private String comments;

public PizzaType() {
// default constructor for morphia
}

public String getTypeName() {
return typeName;
}

public void setTypeName(String typeName) {
this.typeName = typeName;
}

public String getComments() {
return comments;
}

public void setComments(String comments) {
this.comments = comments;
}
}


3) PizzaContent Class - another entity class

@Entity("pizza_content")
public class PizzaContent {

@Id
private ObjectId id;
private String contentName;
private String contentDesc;

public ObjectId getId() {
return id;
}

public void setId(ObjectId id) {
this.id = id;
}

public String getContentName() {
return contentName;
}

public void setContentName(String contentName) {
this.contentName = contentName;
}

public String getContentDesc() {
return contentDesc;
}

public void setContentDesc(String contentDesc) {
this.contentDesc = contentDesc;
}

}



Step3 : To save

i) Create a datastore

Morphia morphia = new Morphia();
Mongo mongo = new Mongo("localhost");
Datastore datastore = morphia.createDatastore(mongo, "dominos_hut");

ii) Save the instances
Here PizzaContent need to saved before Pizza

PizzaContent pizzaContent = new PizzaContent();
pizzaContent.setContentName("Wheat");
pizzaContent.setContentDesc("Flour");
dtStore.save(pizzaContent);

List<pizzacontent> pizzaContents = new ArrayList<pizzacontent>();
pizzaContents.add(pizzaContent);
pizza.setPizzaContents(pizzaContents);

PizzaType pizzaType = new PizzaType();
pizzaType.setComments("comments");
pizzaType.setTypeName("Italian");

pizza.setPizzaType(pizzaType);

dtStore.save(pizza);


Step4 : Querying interface

Morphia provides a good amount of functions to query the MongoDB.


1) To select all the records in the Pizza data collection

Query<pizza> qryPizza = (Query<pizza>)dtStore.find(Pizza.class);
for (Pizza retPizza : qryPizza.fetch()){
System.out.println("Pizza Description: " + retPizza.getDescription());
}


2) To add a custom where" condition filter

Pizza retPizza1 = dtStore.createQuery(Pizza.class).filter("pizzaType.typeName = ", "Italian").get();
System.out.println("Pizza Description: " + retPizza1.getDescription());

3) It provides specific off the shelf functionalities as well

Typical equals condition

List<pizza> retPizzas2 = dtStore.createQuery(Pizza.class).field("name").containsIgnoreCase("Tomato Pizza").asList();
System.out.println("No. of Tomato Pizzas - " + retPizzas2.size());

Helps to order the results

List<pizza> retPizzas3 = dtStore.createQuery(Pizza.class).order("name").asList();
System.out.println("pizza.getId() - " + retPizzas3.size());

Endswith String matching

List<pizza> retPizzas2 = dtStore.createQuery(Pizza.class).field("name").endsWith("1").asList();

Startswith String matching

List<pizza> retPizzas3 = dtStore.createQuery(Pizza.class).field("name").startsWith("Tom").asList();

No comments:

Post a Comment