Jen-Ming Chung

Using MongoDB With Morphia

MongoDB is an extremely useful document-based database in NoSQL field. There are various drivers and client libraries in MongoDB Ecosystem and detailed manual to facilitate the developers to get into it shortly. In the beginning, we use MongoDB Java driver to manipulate CRUD operations on databases. Everything is running well but lots of BasicDBObject cause the code lack of readability and fluency. In addition, we employ POJO classes as intermediates between business logic and databases. However, it also brings many laborious and tedious works for converting POJOs into BasicDBObjects before we want to persist custom objects to the database.

Then we find the Java driver provides a DBObject interface to save custom objects to the database as long as the POJOs implement this interface:

1
2
3
public class CustomClass implements DBObject {
    /* ... */
}

This approach offers an easy way to convert the document we retrieved to a DBobject instance. Then we can perform some operations (e.g, set values to fields) and save it back easily:

collection.save(CustomClass);

However, is there a better way to do this?

Morphia

In my opinion, Morphia is a better alternative library for mapping POJOs to/from MongoDB. We can define our entity by Annotations, including the embedded and reference documents within the database. Most of all, Morphia also provides the Data Access Objects (DAO) mechanism to persist the objects. Here’s an excellent introduction to Morphia provided by Jeff Yemin.

The remaining part of this article will note down some usage or discussions of Morphia.

MongoClient

We use MongoClient to create a database connection with internal connection pool instead of deprecated Mongo class. It’s designed with the Singleton pattern, and the properties are defined as resource in order to easy to modify. Then just call the getMongoClient function to get a MongoClient instance.

MongoConnectionHelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.example.morphia;

import java.net.UnknownHostException;
import java.util.ResourceBundle;
import com.mongodb.MongoClient;

public class MongoConnectionHelper {

    private static MongoClient mongoSingleton = null;

    public static synchronized MongoClient getMongoClient() throws UnknownHostException {

        if (mongoSingleton == null) {

            synchronized (MongoConnectionHelper.class) {
                if (mongoSingleton == null) {

                    ResourceBundle bundle = ResourceBundle.getBundle("mongodb");
                    String host = bundle.getString("host");
                    int port = Integer.parseInt(bundle.getString("port"));

                    mongoSingleton = new MongoClient(host, port);
                }
            }
        }

        return mongoSingleton;

    }
}

Serialization and Deserialization ObjectId with Jackson

In order to serve an object in JSON as a RESTful resource, we can turn on the JSON POJO support in web.xml in Tomcat:

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
...

<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

...

However, we will get something like this formation for ObjectId field in returned JSON,

1
2
3
4
5
6
7
8
9
{
    "id": {
        "machine": 615264403,
        "timeSecond": 1376632233,
        "inc": -1018127256,
        "new": false,
        "time": 1376632233000
    }
}

instead of the following style:

"id": "520dbda924ac3093c3509c68"

Fortunately, Alejandro Ayuso provides a way to solve the Jackson serialization problem for MongoDB’s ObjectId – the ObjectIdSerializer class and your custom class can set JsonSerialize property with it:

1
2
3
4
5
6
7
8
@Entity
public class MyEntity {
    @JsonSerialize(using = ObjectIdSerializer.class)
    @Id
    ObjectId id;

    ...
}

Performance Issue

Scalabiliti conducts an experiment to evaluate the performance between Morphia and MongoDB with Java driver. According to the results, Morphia has slightly difference in the test.

With a few code changes for optimisation, Morphia has now made it to within 99.5% of the performance of the base Mongo install.

Array Operatios

Morphia provides a fluent and comfortable interface to operate arrays. We can remove one or multi objects from an array field through removeAll function.

1
2
3
4
5
6
7
Datastore ds = ...

Query<MyEntity> query = ds.find(MyEntity.class, field, value);
UpdateOperations<MyEntity> ops = ds.createUpdateOperations(
    MyEntity.class).removeAll(field, value);

UpdateResults<MyEntity> results = ds.update(query, ops);

Moreover, you can pull out more than one object by providing List<?> values.

1
2
3
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
UpdateOperations<MyEntity> ops = ds.createUpdateOperations(
    MyEntity.class).removeAll(field, stooges);

Comments