Using the MongoDB module

MongoDB is one of the many rising stars on the NoSQL horizon. MongoDB outperforms other databases in development speed once you get used to thinking in data structures again instead of somewhat more or less arbitrary split rows. If you do not use MongoDB, this recipe will not help you at all.

You can find the source code of this example in the chapter3/booking-mongodb directory.

Getting ready

As there is already a wonderful and well known booking application, which is also used in many other frameworks (Seam, Spring, ZK) as an example, we will take it in this example and convert it to use MongoDB as its persistence backend. So, in order to get ready, copy it from the samples-and-tests/booking directory of your Play installation directory to somewhere else, and follow the steps outlined here.

After copying the application, you should install the Morphia module by adding it to the dependencies.yml file and rerun play deps. Then you are ready to convert the application to store data into MongoDB using Morphia instead of using the native SQL storage of Play.

Of course, you should have an up and running MongoDB instance. You can find some help installing it at http://www.mongodb.org/display/DOCS/Quickstart.

How to do it...

The first part is to convert the models of the application to use Morphia instead of JPA annotations. The simplest model is the user entity, which should look like this:

import play.modules.morphia.Model;
import com.google.code.morphia.annotations.Entity;

@Entity
public class User extends Model {

    @Required
    @MaxSize(15)
    @MinSize(4)
    @Match(value="^\w*$", message="Not a valid username")
    public String username;

    @Required
    @MaxSize(15)
    @MinSize(5)
    public String password;

    @Required
    @MaxSize(100)
    public String name;

    public User(String name, String password, String username) {
        this.name = name;
        this.password = password;
        this.username = username;
    }

    public String toString()  {
        return "User(" + username + ")";
    }

}

In order to keep the recipe short, only the required changes will be outlined in the other entities instead of posting them completely. No JPA annotations should be present in your models following these changes. Always make sure you are correctly checking your imports as the annotations' names are often the same.

Remove the @Temporal, @Table, @Column, @ManyToOne, @Entity JPA annotations from the entities. You can replace @ManyToOne with @Reference in the Booking entity.

One last important point is to set the BigDecimal typed price to a Float type. This means losing precision. You should not do this in live applications if you need exact precision scale numbers. Currently Morphia does not support BigDecimal types. If you need precision arithmetic, you could create an own data type for such a task. Then replace this code from the original Hotel entity:

    @Column(precision=6, scale=2)
    public BigDecimal price;

By removing the annotation and setting the price as a float as in:

    public Float price;

The next step is to replace some code in the Hotel controller. Whenever an ID is referenced in the routes file, it is not a Long but an ObjectId from MongoDB represented as a String, which consists of alphanumeric characters. This needs to be done in the signature of the show(), book(), confirmBooking(), and cancelBooking() methods. You can also set the ID field to be a Long type instead of an ObjectID via the morphia.id.type=Long parameter in your application configuration, if you want.

Whenever find() is called on a mongo entity, the fetch() method does not return a list, but an iterable. This iterable does not get the full data from the database at once. In order to keep this example simple, we will return all bookings by a user at once. So the index() methods need to replace the following:

List<Booking> bookings = Booking.find("byUser", connected()).fetch();

With the following:

List<Booking> bookings = Booking.find("byUser", connected()).asList();

The last change is the call of booking.id, which has to be changed to booking.getId() because there is no direct ID property in the Model class based on Morphia. This needs to be changed in the confirmBooking() and cancelBooking() methods.

How it works...

The functionality stays the same, as only the persistence layer and some controller code is changed. If you wanted to separate controller code even further, you could make sure that finders are always only constructed in the model class.

If you click through the example now and compare it to the database version, you will not see any difference. You can also use the mongo command line client to check whether everything you did was actually persisted. When checking a booking, you will see it looks like this:

>db.Booking.findOne()
{
    "_id" : ObjectId("4d1dceb3b301127c3fc745c6"),
    "className" : "models.Booking",
    "user" : {
      "$ref" : "User",
      "$id" : ObjectId("4d1dcd6eb301127c2ac745c6")
    },
    "hotel" : {
      "$ref" : "Hotel",
      "$id" : ObjectId("4d1dcd6eb301127c2dc745c6")
    },
    "checkinDate" : ISODate("2010-12-06T23:00:00Z"),
    "checkoutDate" : ISODate("2010-12-29T23:00:00Z"),
    "creditCard" : "1234567890123456",
    "creditCardName" : "VISA",
    "creditCardExpiryMonth" : 1,
    "creditCardExpiryYear" : 2011,
    "smoking" : false,
    "beds" : 1
}

As you can see, there is one booking. A specialty of Morphia is to store the class, where it was mapped from into the data as well, with the property className. If needed, this behavior can be disabled. The user and hotel properties are references to the specific collections and reference a certain object ID there. Think of this as a foreign key, when coming from the SQL world.

There's more...

This has only scratched the surface of what is possible. The Morphia module is especially interesting, because it also supports embedded data, even collections. You can, for example, map comments to a blog post inside of this post instead of putting it into your own collection. You should read the documentation of Morphia and the play-specific Morphia module very carefully though, if you want to be sure that you can easily convert an already started project to persist into MongoDB.

Check out the Yabe example in the Morphia directory

Morphia already includes a port of the Yabe example, which uses some of the more advanced features like map reduce.

Use long based data types as unique IDs

The Morphia module also offers to use a long value as ID instead of an object ID. This would have saved changing the controller code.

Aggregation and grouping via map reduce

As there is no join support in MongoDB, you will need to use map-reduce algorithms. There is no really good support of map-reduce in the java drivers, as you have to write JavaScript code as your map-reduce algorithms. For more information about that you might want to check the MongoDB documentation at http://www.mongodb.org/display/DOCS/MapReduce.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.30.19