Morphia arrays and CRUD example

This is an example for future reference and I'm not going to comment futher on it than what's already in the comments.

Location.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 Location
{
    @Id
    private ObjectId    id;

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

    public Location() {}

    public Location( Integer stars, String street, String city, String state, String zipcode, String county )
    {
        this.stars   = stars;
        this.street  = street;
        this.city    = city;
        this.state   = state;
        this.zipcode = zipcode;
        this.country = county;
    }

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

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

        if( this.stars != 1 )
            string += this.stars + " stars  ";
        else
            string += this.stars + " star  ";

        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 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 Hotel
{
    @Id
    private ObjectId    id;

    private String        name;
    private int            stars;

    public Hotel() {}

    public Hotel( String name, int stars )
    {
        this.name = name;
        this.stars = stars;
    }

    @Embedded
    private ArrayList< Location >    locations;

    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< Location > getLocations() { return locations; }
    public void setLocations( ArrayList< Location > locations ) { this.locations = locations; }

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

        for( Location location : this.locations )
            string += "    " + location.toString() + "\n";

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

import java.util.List;

import util.Implementor;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.dao.BasicDAO;

/**
 * Do database stuff here...
 */
@Implementor( HotelDao.class )
public class HotelDao extends BasicDAO< Hotel, String >
{
    /* The Datastore is a wrapper around the MongoDB Java driver and is used to manage
     * entities in MongoDB.
     */
    Datastore datastore; // inject this manually for now...

    public HotelDao()
    {
        super( HotelTest.getMongo(), HotelTest.getMorphia(), "hotels" );
        this.datastore = HotelTest.getDatastore();
    }

    public Hotel create( Hotel hotel )
    {
        this.datastore.save( hotel );
        return hotel;
    }

    public List< Hotel > read()
    {
        return this.datastore.find( Hotel.class ).asList();
    }

    public List< Hotel > read( String query, String value )
    {
        if( query == null && value == null )
            return this.datastore.find( Hotel.class ).asList();

        return this.datastore.find( Hotel.class, query, value ).asList();
    }

    public List< Hotel > read( String query, Integer value )
    {
        if( query == null && value == null )
            return this.datastore.find( Hotel.class ).asList();

        return this.datastore.find( Hotel.class, query, value ).asList();
    }

    public void update( Hotel hotel )
    {
        this.datastore.merge( hotel );
    }

    public void remove( Hotel hotel )
    {
        this.datastore.delete( hotel );
    }
}
HotelManager.java:
package experiment.morphia;

import java.util.List;

import util.Implementor;
import util.ServiceLocator;

/**
 * Do business logic here...
 */
@Implementor( HotelManager.class )
public class HotelManager
{
    private HotelDao hotelDao = ServiceLocator.get( HotelDao.class );

    public Hotel create( Hotel hotel )
    {
        return hotelDao.create( hotel );
    }

    public List< Hotel > read()
    {
        return hotelDao.read();
    }

    public List< Hotel > read( String query, String value )
    {
        return hotelDao.read( query, value );
    }

    public List< Hotel > read( String query, Integer value )
    {
        return hotelDao.read( query, value );
    }

    public void update( Hotel hotel )
    {
        hotelDao.update( hotel );
    }

    public void delete( Hotel hotel )
    {
        hotelDao.remove( hotel );
    }
}
HotelService.java:
package experiment.morphia;

import java.util.List;

import util.ServiceLocator;

/**
 * Do web service here (except that this isn't a web service, but a squirrel cage)...
 */
public class HotelService
{
    private HotelManager hotelManager = ServiceLocator.get( HotelManager.class );

    public Hotel create( Hotel hotel )
    {
        return this.hotelManager.create( hotel );
    }

    public List< Hotel > read()
    {
        return this.hotelManager.read();
    }

    public List< Hotel > read( String query, String value )
    {
        return this.hotelManager.read( query, value );
    }

    public List< Hotel > read( String query, Integer value )
    {
        return this.hotelManager.read( query, value );
    }

    public void update( Hotel hotel )
    {
        this.hotelManager.update( hotel );
    }

    public void delete( Hotel hotel )
    {
        this.hotelManager.delete( hotel );
    }
}
HotelTest.java:
package experiment.morphia;

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

import org.apache.log4j.Logger;

import util.ServiceLocator;

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

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

    private static Mongo        mongodb;
    private static DB           database;
    private static Morphia      morphia;
    private static Datastore    datastore;

    private static final String DATABASE_NAME   = "hotels";
    private static final String HOTEL_NAME      = "Hotel Hyatt";
    private static final String COLLECTION_NAME = Hotel.class.getSimpleName();

    public static Mongo getMongo() { return mongodb; }
    public static Morphia getMorphia() { return morphia; }
    public static Datastore getDatastore() { return datastore; }

    /**
     * Initialize MongoDB and Morphia for our purposes.
     */
    private static void initializeMongoAndMorphia()
    {
        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( DATABASE_NAME );
        morphia   = new Morphia();
        datastore = morphia.createDatastore( mongodb, DATABASE_NAME );

        database.dropDatabase();    // drop any fodder left in from last time...
        morphia.map( Hotel.class ).map( Location.class );
    }

    /**
     * Clean up the document we put in here last time (works assuming the hotel name hasn't
     * changed) so that we start fresh.
     *
     * @param key field name ("name").
     * @param value hotel name.
     */
    private static void deleteDocumentFromCollection( String key, String value )
    {
        DBCollection collection = database.getCollection( COLLECTION_NAME );

        try
        {
            BasicDBObject dbo = new BasicDBObject();

            dbo.append( key, value );
            collection.remove( dbo );
        }
        catch( MongoException e )
        {
            log.debug( "Ignored: " + e.getMessage() );
        }
    }

    public static void main( String[] args )
    {
        initializeMongoAndMorphia();

        HotelService service = new HotelService();

        // CREATE

        /* Create a random number of hotel chains and put them into the database.
         */
        Long   milliseconds = System.currentTimeMillis();
        Random generator    = new Random( milliseconds );
        int    hotelsCount  = HotelFodder.getHotelCount();

        // always ensure we've got at least one hotel chain...
        int which = generator.nextInt( hotelsCount );

        if( which == 0 )
            which = 1;

        if( which == 1 )
            System.out.println( "Creating a hotel chain..." );
        else
            System.out.println( "Creating " + which + " hotel chains..." );

        while( which > 0 )
        {
            Hotel h = service.create( HotelFodder.newHotel() );
            which--;
        }

        /* If I move the stars to the individual locations, I query this way:
         * > db.Hotel.find( { "locations" : { $elemMatch : { "stars" : { $gt : 2 } } } } ).count();
         * However, this still gives me back the whole Hotel document including the lesser-star
         * hotel locations.
         *
         * This came up as I was assigning stars to locations instead of hotel chains. I stopped
         * short of extending this change to what's going on here since it's isn't relevant and I
         * don't want to continue to play with it.
         */

        // READ
        List< Hotel > fourStarChains = service.read( "stars >", 2 );

        System.out.println( "Looking for 3-star or better hotels..." );

        Hotel h_c = null;

        int count = 0;

        for( Hotel hc : fourStarChains )
        {
            System.out.println( hc );
            count++;
        }

        if( count == 0 )
            System.out.println( "(There were no 3-star or better hotels.)" );
    }
}
HotelTuple.java:
package experiment.morphia;

public class HotelTuple
{
    String hotelName;
    Integer stars;

    public HotelTuple( String name, Integer stars )
    {
        this.hotelName = name;
        this.stars = stars;
    }
}
HotelFodder.java:
package experiment.morphia;

import java.util.ArrayList;
import java.util.Random;

public class HotelFodder
{
    private static boolean                 once = false;
    private static Random                  generator1;
    private static Random                  generator2;
    private static Random                  generator3;
    private static ArrayList< Location >   locations;
    private static ArrayList< HotelTuple > hotelNamesAndStars;

    private static void initializeAddresses()
    {
        locations = new ArrayList< Location >();

        locations.add( new Location( 2, "3222 South 525 West", "Bountiful", "UT", "84101", "US" ) );
        locations.add( new Location( 2, "59, rue Albert Premier", "Bezons", null, "95057", "FR" ) );
        locations.add( new Location( 4, "Miquelallee Straß, 23", "Frankfurt", null, "65936", "DE" ) );
        locations.add( new Location( 1, "1313 Mockingbird Lane", "Mockingbird Heights", "CA", "06660", "US" ) );
        locations.add( new Location( 5, "12345 Bedford Drive", "Beverly Hills", "CA", "90210", "US" ) );
        locations.add( new Location( 4, "1778 Oregon Avenue", "Provo", "UT", "84606", "US" ) );
        locations.add( new Location( 4, "23, rue du Puits", "Pagny-la-Blanche-Côte", null, "55010", "FR" ) );
        locations.add( new Location( 4, "48 Cicada Drive", "Boise", "ID", "83702", "US" ) );
        locations.add( new Location( 5, "5, Boulevard des Capucines", "Paris", null, "75008", "FR" ) );
    }

    private static void initializeHotelNamesAndStars()
    {
        hotelNamesAndStars = new ArrayList< HotelTuple >();

        hotelNamesAndStars.add( new HotelTuple( "Hyatt Hotel",        generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Bayside Arms",       generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Hotel California",   generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Bates Motel",        0 ) );
        hotelNamesAndStars.add( new HotelTuple( "Purple Hives",       generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Sofitel",            generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Novotel",            generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Marriott Courtyard", generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Holiday Inn",        generator3.nextInt( 5 ) + 1 ) );
        hotelNamesAndStars.add( new HotelTuple( "Embassey Suites",    generator3.nextInt( 5 ) + 1 ) );
    }

    public static void initializeFodder()
    {
        Long milliseconds = System.currentTimeMillis();

        if( once )
            return;

        generator1 = new Random( milliseconds );
        generator2 = new Random( milliseconds + 89 );
        generator3 = new Random( milliseconds + 76 );

        initializeAddresses();
        initializeHotelNamesAndStars();
        once = true;
    }

    public static int getLocationCount()
    {
        initializeFodder();

        return locations.size();
    }

    public static int getHotelCount()
    {
        initializeFodder();

        return hotelNamesAndStars.size();
    }

    /**
     * Generate a new hotel chain with a random name, stars and set of locations.
     *
     * @return one new Hotel chain.
     */
    public static Hotel newHotel()
    {
        initializeFodder();

        int locationCount = getLocationCount();
        int hotelCount    = getHotelCount();

        HotelTuple tuple = hotelNamesAndStars.get( generator1.nextInt( hotelCount ) );
        Hotel      hotel = new Hotel( tuple.hotelName, tuple.stars );

        ArrayList< Location > as = new ArrayList< Location >();

        // always ensure we've got at least one location in a hotel chain...
        int howMany = generator2.nextInt( locationCount );

        if( howMany == 0 )
            howMany = 1;

        int next = generator3.nextInt( locationCount );

        while( howMany > 0 )
        {
            if( next < 0 )
                next = locationCount - 1;

            Location location = locations.get( next );
            as.add( location );
            howMany--;
            next--;
        }

        hotel.setLocations( as );

        return hotel;
    }

    public static void main( String[] args )
    {
        Hotel hotel = newHotel();
        System.out.println( hotel.toString() );
    }
}