Transcription of Notes on Morphia

An almost full-featured Morphia sample covering embedded array functionality, written as if CRUD, may be found here.


Quick Start

Installation

All that is needed is just one JAR; the latest as I write this is morphia-0.99.1-SNAPSHOT.jar from morphia: A type-safe Java library for MongoDB.

There is no Javadoc solution as yet except to download the sources, then build the Javadoc for yourself.


Miscellaneous comments

In a lot of examples, the name of the database and the collection seem totally random and unimportant. This is true, but for those who are concerned about naming, this code and following hold:

    Mongo        mongodb    = new Mongo( "localhost", 27017 );
    DB           database   = mongodb.getDB( "morphiaexample" );
    Morphia      morphia    = new Morphia();
    Datastore    datastore  = morphia.createDatastore( mongodb, "Scrooge" );
    DBCollection collection = database.getCollection( "Peckerwoodarooney" );
    ...
    public HotelDao( Morphia morphia, Mongo mongo )
    {
        super( mongo, morphia, "Peckerwood" );
    }

Nothing shows up (not database nor collection) until a hotel is created and persisted:

    datastore.save( hotel );

...when the database, collection and document in that collection suddenly appear. The database name depends on what's passed to Morphia.createDatastore() and not, as you'd think, what's passed to mongodb.getDB(). The collection name is affected either by what you pass to the @Entity annotation on the POJO or the name of the POJO itself. What's passed to BasicDAO's constructor (superclass of HotelDao) is immaterial too.

@Entity( value="Scroogerooney" )

Here are the results of the above:


No usable constructor

Every entity POJO must have a default, no-argument constructor or Morphia will croak with a message to the effect that there is no usable constructor, something like "No usable constructor for class").

That there is no such constructor may be inadvertant. By default it exists, but maybe you created a helpful constructor thereby removing the hidden default?


Website example

Here's the example from the web page coded up and very lightly enhanced to help explore issues.

Address.java:
package experiment.morphia;

import org.bson.types.ObjectId;

import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;

@Entity
public class Address
{
    @Id
    private ObjectId    id;

    private String    street;
    private String    city;
    private String    state;
    private String    zipcode;
    private String    country;

    public ObjectId getId() { return this.id; }
    public void setId( ObjectId id ) { this.id = id; }
    public String getStreet() { return this.street; }
    public void setStreet( String street ) { this.street = street; }
    public String getCity() { return this.city; }
    public void setCity( String city ) { this.city = city; }
    public String getState() { return this.state; }
    public void setState( String state ) { this.state = state; }
    public String getZipcode() { return this.zipcode; }
    public void setZipcode( String zipcode ) { this.zipcode = zipcode; }
    public String getCountry() { return this.country; }
    public void setCountry( String country ) { this.country = country; }

    public String toString()
    {
        String string = "";

        if( this.id != null )
            string = "[" + this.id + "] ";

        string += this.street + "  "
                + this.city + " ";

        if( this.state != null )
            string += this.state + "  ";

        string += this.country + "  "
                + this.zipcode;

        return string;
    }
}
Hotel.java:
package experiment.morphia;

import org.bson.types.ObjectId;

import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;

@Entity
public class Hotel
{
    @Id
    private ObjectId    id;

    private String        name;
    private int            stars;

    @Embedded
    private Address        address;

    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 int getStars() { return stars; }
    public void setStars( int stars ) { this.stars = stars; }

    public Address getAddress() { return address; }
    public void setAddress( Address address ) { this.address = address; }

    public String toString()
    {
        String string = "[" + this.id + "] " + this.name + " (" + this.stars + ")";

        return string + ", " + this.address.toString();
    }
}
HotelDao.java:
package experiment.morphia;

import com.google.code.morphia.Morphia;
import com.google.code.morphia.dao.BasicDAO;
import com.mongodb.Mongo;

public class HotelDao extends BasicDAO< Hotel, String >
{
    public HotelDao( Morphia morphia, Mongo mongo )
    {
        super( mongo, morphia, "hotels" );
    }
}
HotelMain.java:
package experiment.morphia;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

@SuppressWarnings( "unused" )
public class HotelMain
{
    public static Logger log = Logger.getLogger( HotelMain.class );

    private static Mongo        mongodb;
    private static DB           database;
    private static DBCollection collection;

    public static void main( String[] args )
    {
        try
        {
            mongodb = new Mongo( "localhost", 27017 );
        }
        catch( UnknownHostException e )
        {
            log.error( "MongoDB host not found" );
        }
        catch( MongoException e )
        {
            log.error( "Error attempting MongoDB connection for hotels", e );
        }

        database   = mongodb.getDB( "morphiaexample" );
        collection = database.getCollection( "hotels" );

        Morphia   morphia   = null;
        Datastore datastore = null;

        morphia = new Morphia();
        morphia.map( Hotel.class ).map( Address.class );
        datastore = morphia.createDatastore( mongodb, "hotels" );

        Hotel hotel = new Hotel();
        hotel.setName( "Cockroach Haven" );
        hotel.setStars( 4 );

        Address address = new Address();
        address.setStreet( "3222 South 525 West" );
        address.setCity( "Bountiful" );
        address.setState( "UT" );
        address.setZipcode( "84010" );
        address.setCountry( "US" );

        // set address
        hotel.setAddress( address );

        // save the POJO
        datastore.save( hotel );

        // --------------------------------------------------------------------------
        String hotelId = "1"; // the ID of the hotel we want to load

        // and then map it to our Hotel object
        hotel = datastore.get( Hotel.class, hotelId );

        // Note: why is hotel now null?

        // --------------------------------------------------------------------------
        // it is easy to get four-star hotels.
        List< Hotel > fourStarHotels;

        fourStarHotels = datastore.find( Hotel.class, "stars >=", 4 ).asList();
        // or
        fourStarHotels = datastore.find( Hotel.class ).field( "stars" ).greaterThanOrEq( 4 ).asList();

        for( Hotel h : fourStarHotels )
        {
            System.out.println( h );
        }
    }
}

Database console

    > use hotels
    switched to db hotels
    > show collections
    Hotel
    system.indexes
    > db.Hotels.find().forEach( printjson );
    {
        "_id" : ObjectId("4fd61d105a378b649e6f3dff"),
        "className" : "experiment.morphia.Hotel",
        "name" : "Cockroach Haven",
        "stars" : 4,
        "address" : {
            "street" : "3222 South 525 West",
            "city" : "Bountiful",
            "state" : "UT",
            "zipcode" : "84010",
            "country" : "US"
        }
    }

Console output

    Jun 11, 2012 10:31:21 AM com.google.code.morphia.logging.MorphiaLoggerFactory chooseLoggerFactory
    INFO: LoggerImplFactory set to com.google.code.morphia.logging.jdk.JDKLoggerFactory
    [4fd61d105a378b649e6f3dff] Cockroach Haven (4), 3222 South 525 West  Bountiful UT  US  84010

Embedded array example

Here's the example augmented to demonstrate the case where Address becomes an embedded array of Address. Address itself doesn't change—you just use the code above.

HotelChain.java:
package experiment.morphia;

import java.util.ArrayList;

import org.bson.types.ObjectId;

import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;

@Entity( noClassnameStored=true )
public class HotelChain
{
    @Id
    private ObjectId    id;

    private String        name;
    private int            stars;

    @Embedded
    private ArrayList< Address >    addresses;

    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 int getStars() { return stars; }
    public void setStars( int stars ) { this.stars = stars; }

    public ArrayList< Address > getAddresses() { return addresses; }
    public void setAddresses( ArrayList< Address > addresses ) { this.addresses = addresses; }
}
HotelChainDao.java:
package experiment.morphia;

import com.google.code.morphia.Morphia;
import com.google.code.morphia.dao.BasicDAO;
import com.mongodb.Mongo;

public class HotelChainDao extends BasicDAO< HotelChain, String >
{
    public HotelChainDao( Morphia morphia, Mongo mongo )
    {
        super( mongo, morphia, "hotelchains" );
    }
}
HotelChainMain.java:

I did some inane stuff at the end of main() in order to inspect the results in the debugger and prove that the resulting POJO did indeed contain the array of addresses.

package experiment.morphia;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

@SuppressWarnings( "unused" )
public class HotelChainMain
{
    public static Logger log = Logger.getLogger( HotelChainMain.class );

    private static Mongo        mongodb;
    private static DB           database;
    private static DBCollection collection;

    public static void main( String[] args )
    {
        try
        {
            mongodb = new Mongo( "localhost", 27017 );
        }
        catch( UnknownHostException e )
        {
            log.error( "MongoDB host not found" );
        }
        catch( MongoException e )
        {
            log.error( "Error attempting MongoDB connection for hotel chains", e );
        }

        database   = mongodb.getDB( "morphiaexample" );
        collection = database.getCollection( "hotels" );

        Morphia   morphia   = null;
        Datastore datastore = null;

        morphia = new Morphia();
        morphia.map( HotelChain.class ).map(  Address.class );
        datastore = morphia.createDatastore( mongodb, "hotelchains" );

        HotelChain hotelchain = new HotelChain();
        hotelchain.setName( "Hotel Hyatt" );
        hotelchain.setStars( 4 );

        ArrayList< Address > addresses = new ArrayList< Address >();
        Address address = new Address();

        address = new Address();
        address.setStreet( "3222 South 525 West" );
        address.setCity( "Bountiful" );
        address.setState( "UT" );
        address.setZipcode( "84101" );
        address.setCountry( "US" );
        addresses.add( address );

        address = new Address();
        address.setStreet( "59, rue Albert Premier" );
        address.setCity( "Bezons" );
        address.setZipcode( "95057" );
        address.setCountry( "FR" );
        addresses.add( address );

        address = new Address();
        address.setStreet( "Miquelallee Straß, 23" );
        address.setCity( "Frankfurt" );
        address.setZipcode( "65936" );
        address.setCountry( "DE" );
        addresses.add( address );

        hotelchain.setAddresses( addresses );

        // save the POJO
        datastore.save( hotelchain );

        String hotelchainId = "1";

        hotelchain = datastore.get( HotelChain.class, hotelchainId );

        List< HotelChain > fourStarChains;

        fourStarChains = datastore.find( HotelChain.class ).field( "stars" ).greaterThanOrEq( 4 ).asList();

        HotelChain h_c = null;

        for( HotelChain hc : fourStarChains )
        {
            h_c = hc;
            System.out.println( hc );
        }

        h_c.hashCode();

        for( Address a : h_c.getAddresses() )
        {
            System.out.println( a );
        }
    }
}

Database console

Beside the obvious concern of an embedded array, compare this output with the previous example to see that the @Entity( noClassnameStored=true ) annotation leads to no classname field in the document.

    > show dbs
    hotelchains    0.203125GB
    hotels    0.203125GB
    > use hotelchains
    switched to db hotelchains
    > show collections
    HotelChain
    system.indexes
    > db.HotelChain.find().forEach( printjson );
    {
        "_id" : ObjectId("4fd61bfb5a37047b69774828"),
        "name" : "Hotel Hyatt",
        "stars" : 4,
        "addresses" : [
            {
                "street" : "3222 South 525 West",
                "city" : "Bountiful",
                "state" : "UT",
                "zipcode" : "84101",
                "country" : "US"
            },
            {
                "street" : "59, rue Albert Premier",
                "city" : "Bezons",
                "zipcode" : "95057",
                "country" : "FR"
            },
            {
                "street" : "Miquelallee Straß, 23",
                "city" : "Frankfurt",
                "zipcode" : "65936",
                "country" : "DE"
            }
        ]
    }

Console output

    Jun 11, 2012 10:25:31 AM com.google.code.morphia.logging.MorphiaLoggerFactory chooseLoggerFactory
    INFO: LoggerImplFactory set to com.google.code.morphia.logging.jdk.JDKLoggerFactory
    experiment.morphia.HotelChain@5ff06dc3
    3222 South 525 West  Bountiful UT  US  84101
    59, rue Albert Premier  Bezons FR  95057
    Miquelallee Straß, 23  Frankfurt DE  65936