MongoDB With Morphia

Jun 13, 2016
MongoDB With Morphia
  • Using MongoDB with Morphia

    How to use Morphia in your plugins



    Note: Before editing this article, please ask in the discussion section for a second opinion – personal opinions and views should be kept out of wiki articles. Thank you!


    Into(top)


    Before digging into this Wiki, I would recommend checking out the very beginning of the MongoDB wiki. It goes into detail about what NoSQL is and how it used. Read up until when it says how to connect to MongoDB, because that is when these tutorials diverge. It can be found here.

    What is Morphia?(top)


    Morphia is an API developed by MongoDB to allow developers to easily map their objects into MongoDB without much effort. You are able to create a class, create fields, and then directly save your object into the database without any more effort on your own part. It makes using MongoDB a very streamline process, and means that you do not need to give yourself a headache which the early set up.

    Adding Morphia To Your Project(top)


    In order to start using Morphia in your project, I use Maven in order to both build the resources into the final compiled project, as well as handle my dependencies. I might add a non-maven way to do this later, but here is it with Maven.

    The first thing you need to do is add the dependencies into your project. You will need to add both Maven and Morphia, so put the following in your pom.xml:
    HTML:
    <dependency>
        <groupId>org.mongodb.morphia</groupId>
        <artifactId>morphia</artifactId>
        <version>1.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>mongo-java-driver</artifactId>
        <version>3.2.2</version>
    </dependency>
    Once that is added, you need to make sure that when your plugin compiles, Bukkit knows where to look for the resources. There are few ways to do this, but I choose to just build it into one of my plugins. Do this in whatever plugin actually connects to the database. Put this in the pom.xml:
    HTML:
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <includes>
                                    <include>org.mongodb</include>
                                    <include>org.mongodb.morphia</include>
                                </includes>
                            </artifactSet>
                            <createDependencyReducedPom>false</createDependencyReducedPom>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    Now that is all done, you just need to connect!

    Connecting to Mongo and Morphia(top)


    There are two different ways to connect to the MongoClient and Morphia, one with credentials, and one without. If you are on localhost it would be easier to just disable credentials, and then just disallow remote connections. I will illustrate both methods below:

    With Credentials:
    Code (Java):
        private MongoClient mc;
        private Morphia morphia;

        public DatabaseHandler(int i)
        {
            ServerAddress addr = new ServerAddress("hostname", port);
            List<MongoCredential> credentials = new ArrayList<>();
            credentials.add(MongoCredential.createCredential("username", "database", "password".toCharArray()));
            mc = new MongoClient(addr, credentials);

            morphia = new Morphia();
        }
    Without Credentials:
    Code (Java):
        private MongoClient mc;
        private Morphia morphia;

        public DatabaseHandler(int i)
        {
            mc = new MongoClient();

            morphia = new Morphia();
        }
    For the rest of the code snippets I am going to be putting code in that assumes your MongoClient has already been initialized.

    Creating Your Object(top)


    We are going to come back to your DatabaseHandler later, but for now we need to create our object to be mapped. In this example I am going to create a User class to be mapped and saved. The code is below, and I will explain what each annotation means.

    Code (Java):
    import java.util.ArrayList;
    import java.util.List;

    import org.mongodb.morphia.annotations.Entity;
    import org.mongodb.morphia.annotations.Id;
    import org.mongodb.morphia.annotations.IndexOptions;
    import org.mongodb.morphia.annotations.Indexed;
    import org.mongodb.morphia.annotations.Property;

    @Entity(value = "Users", noClassnameStored = true)
    public class User
    {

        @Id
        public int id;

        @Indexed(options = @IndexOptions(unique = true))
        public String uuid;

        @Indexed
        public String username;

        public int ip;

        public long connectionTime;

        @Property("ip_history")
        public List<Integer> ipHistory = new ArrayList<>();

        @Property("name_history")
        public List<String> nameHistory = new ArrayList<>();

    }

    Annotations(top)

    The following annotations are used and should be used whenever appropriate.

    @Entity
    : This annotation tells Morphia that this object is going to be stored in a collection. The "value" parameter declares what the name of the collection will be. The "noClassnameStored" parameter just tells Morphia that if you change the name of your class later down the line, it still will allow you to save into the same collection. This is not required, but highly recommended.

    @Id: This is the unique ID that identifies each individual Object. You do not have to declare this yourself, Morphia will make one behind the scenes if you do not, but I like to have an integer represent my objects.

    @Indexed: This tells Morphia that you are going to search through the collection with this value and you want to do it quickly. Although this is not required, it significantly increase the search time. My parameter with @IndexOptions(unique = true) just tells Morphia that there is only ever one of Object with this value for a UUID in the database at a time.

    @Property: By default, Morphia names fields in the database the same as you make them in your class. If you want it to be different, name it using @Property.

    @Transient: This annotation tells Morphia that you do not want to save this field in the database. In a more technical sense, transient means that this field will not be serialized.

    @Embedded: If you want to create an object to be stored in the database that is not one of the supported types posted in the next section, you can create an Embedded object. Simply create your field with the object type you want, and annotate it with @Embedded. Then, go into your other class and mark that object above the class declaration with @Embedded.

    @Reference: For those of you who are coming from SQL, you may be familiar with foreign keys and joins. Basically what this says is that this object is stored in a different collection. This can come in handy if for instance you have a Town object, and you want to mark which users are in a specific town, without just storing all of their IDs.

    To read more in detail about the annotations, look here.

    Data Types(top)

    Morphia does not allow every datatype in to be stored, but it allows most of them. The following are all acceptable:
    • All primitives
    • Enums (Stored as a String)
    • java.util.Date
    • java.util.Locale
    If you want to read more in detail about the datatypes, look here.

    Creating Your DAO(top)


    This step is not mandatory, but it will make this entire process a lot easier, and I will be continuing my tutorial assuming you do this.

    You will want to create a DAO for each of your different Objects. To do this, use the following. I am continuing with my example from User.

    Code (Java):
    import org.mongodb.morphia.Datastore;
    import org.mongodb.morphia.dao.BasicDAO;

    public class UserDAO extends BasicDAO<User, String>
    {

        public UserDAO(Class<User> entityClass, Datastore ds)
        {
            super(entityClass, ds);
        }

    }
    And that is it! I will explain how to construct your DAO with the Datastore in the next section. Just make sure your type parameters are the top are <MyClass, String> and the parameters for the DAO are (Class<MyClass>, Datastore).

    Datastores and Mapping(top)


    Now that you have your class, your DAO, and your MongoClient/Morphia objects, it is time to create your datastore. The datastore is the connection you have to your particular database.

    Code (Java):
        private MongoClient mc;
        private Morphia morphia;
        private Datastore datastore;
        private UserDAO userDAO;

        public DatabaseHandler(int i)
        {
            mc = ...;
            morphia = new Morphia();

            morphia.map(User.class);

            datastore = morphia.createDatastore(mc, "dbName");
            datastore.ensureIndexes();

            userDAO = new UserDAO(User.class, datastore);
        }
    And you're ready to go! You can create methods within this class to save your objects based on your DAO. I put some code snippets below based on my example that you can use to get started.

    Code Snippets(top)

    Get a User:
    Code (Java):
        public DUser getUserByPlayer(Player player)
        {
            DUser du = userDAO.findOne("uuid", player.getUniqueId().toString());
            if (du == null)
            {
                du = new DUser();
                du.setUUID(player.getUniqueId().toString());
                du.setIp(PlayerUtils.inetAddressAsInteger(player.getAddress().getAddress()));
                du.setUsername(player.getName());
                userDAO.save(du);
            }
            return du;
        }
    Save a User:
    Code (Java):
        public void saveUser(DUser user)
        {
            userDAO.save(user);
        }
    Get all Users:
    Code (Java):
        public List<User> getAllUsers()
        {
            return userDAO.find().asList();
        }
  • Loading...
  • Loading...