Skip to content

QuickStart Tutorial

GermanGeekN edited this page Sep 10, 2020 · 6 revisions

Tutorial

This short tutorial should show you the way how to use Morphium in the easiest of ways. Just read through and you will be able to use morphium in your project.

Configuring Morphium

Morphium is designed to be as flexible as possible and as easy to use as possible:_

    //Configure your connection to Mongo
    MorphiumConfig cfg = new MorphiumConfig();
    cfg.setDatabase("morphium_test");
    cfg.setGlobalCacheValidTime(5000);
    cfg.addAddress("localhost", 27017);
    cfg.setWriteCacheTimeout(100);
    Morphium morphium=new Morphium(cfg);   

This configures Morphium to access a Single MongoDB Node (no ReplicaSet) with at maximum 5 connections in the pool. The "globalCacheValidTime" is 50000 ms, which means, objects with in cache will be removed after 50seconds, if not specified otherwise.

Definition of Entities

Now - the morphium is configured, let's create an Entity:

@NoCache
@Entity
public class UncachedObject {
    private String value;

    private int counter;

    private
    @Id
    ObjectId mongoId;

    public int getCounter() {
        return counter;
    }

    public void setCounter(int counter) {
        this.counter = counter;
    }

    @UpdatingField("value")
    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }


    public ObjectId getMongoId() {
        return mongoId;
    }

    public void setMongoId(ObjectId mongoId) {
        this.mongoId = mongoId;
    }

    public String toString() {
        return "Counter: " + counter + " Value: " + value + " MongoId: " + mongoId;
    }
}

This represents an uncached Object - specified by @NoCache. This means, accessing those objects through morphium always creates a query in Mongodb - those objects won't be held in Memory. All entities need the @Entity annotation for Morphium to know that this POJO is to be stored. By Default, morphium takes the field names and the class name of this object to create the corresponding entries in mongodb. If not specified otherwise, all names will be converted from camel case to lower case (using as delimiter). This means, this objects of type UncachedObject will be stored in a collection called uncached_object. All field names will be treated alike, except for the field, which has the @Id annotation. This field will always be named _idin mongo. If the type is ObjectId, mongo takes care of creating a new value for it. If you want to use something else as primary key, you have to take care, that it's unique. If unsure, use ObjectId.

If you wanted to have your objects cached automatically be Morphium, you just need to add the @Cache Annotation:

@Cache(clearOnWrite = true, maxEntries = 20000, readCache = true, writeCache = true, strategy = Cache.ClearStrategy.LRU, timeout = 1000)
@Entity
public class CachedObject {
    private String value;
    private int counter;

    @Id
    private ObjectId id;

    public int getCounter() {
        return counter;
    }

    public ObjectId getId() {
        return id;
    }

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

    public void setCounter(int counter) {
        this.counter = counter;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }


    public String toString() {
        return "Counter: " + counter + " Value: " + value + " MongoId: " + id;
    }
}

This object is rather similar to the uncached one, but the @Cache-Annotation tells morphium to have all write and read requests cached. For more information, have a look at the JavaDoc or the Annotation-Wiki-Page. Just some hints: if writeCache set to true, the write will be done asynchronously, this might interesting for large objects. if readCache is set to true, every query to morphium will cache it's result. Here at maximum 20000 entries. If there are more entries to be stored, the Strategy defines, which objects are to removed first. LRU is best practice here. Timeout specified the aging timeout for those objects. Objects are only held in cache if younger than Timeout.

Storing Objects

That is quite easy a task. Just create your objects like any other POJO and call store() in the Morphium-Ojbect:

   UncachedObject o=new UncachedObject();
   o.setValue("Test");
   o.setCounter(7);
   morphium.store(o);

If this is a newly created entry, the field with the @Idannotation will contain the ObjectId of the newly created entry. Attention: you can also use store to do updates on those Elements! It does not matter, whether or not the object is cached or not, if it is new or just an update to an existing one - always call store!

Querying objects

Querying in Morphium is quite easy, there is a simple access method, which is more or less fluent. Have a look:

   Query<UncachedObject> q=morphium.createQueryFor(UncachedObject.class);
   q=q.f("counter").eq(7);
   List<UncachedObject> list=q.asList();

This creates a query for the collection the UncachedObject entries are held, where the value of the field "counter" equals 7. As Field name you can either specify the name of the variable in the entity or the name as it would be called in Mongodb. If the field does not exist, a RuntimeException is thrown here. Unfortunately there is no way yet to create a list of field names automatically.

You can also define enums for each field in your POJO and use that in query:

  @Entity
  public class PoJo {
     private String name;
     private int counter;
     @Id private ObjectId id;
     .... //getter and setter
     public enum Fields { name, counter, id }
  }

now you can create queries to this object using the enums:

   Query<Pojo> q=morphium.createQueryFor(Pojo.class).f(Pojo.Fields.counter).eq(7331);
   List<Pojo> pLst=q.asList();

This is the easiest way to read in a list of objects. Please keep in mind that this would work exactly the same way, if you had a cached Object to read!

If you only want to have the first element of such a query, you should use get()instead of asList() as this would limit the request to 1. But you can skip, limit and sort your query as you like:

   Query<CachedObject> q=morphium.createQueryFor(CachedObject.class);
   q=q.f("counter").lt(10).limit(5).skip(2).sort("-counter");
   List<CachedObject> lst=q.asList();

This would result in a list of max 5 entries (limit(5)) skipping the first 2 results (skip(2)) and sorting by field counter in reverse order. Please keep in mind that every different query to morphium will create a cache entry! Even if the objects would be the same!

Complete Examples

These examples are from the JUnit-Tests - have a look at the Test-Package in the source-code for more examples. The tests are subclasses of MorphiumTestBase and inherit a configured morphium instance.

     morphium.clearCollection(UncachedObject.class);
     log.info("Preparing a list...");

     long start = System.currentTimeMillis();
     List<UncachedObject> lst = new ArrayList<UncachedObject>();
     for (int i = 1; i <= NO_OBJECTS; i++) {
            UncachedObject o = new UncachedObject();
            o.setCounter(i);
            o.setValue("Uncached " + i);
            lst.add(o);
     }
     morphium.storeList(lst);
     long dur = System.currentTimeMillis() - start;
     log.info("Storing a list  took " + dur + " ms");
     assert (dur < NO_OBJECTS * 1.5) : "Storing took way too long";

     start = System.currentTimeMillis();
     for (int i = 1; i <= NO_OBJECTS; i++) {
         Query<UncachedObject> q = morphium.createQueryFor(UncachedObject.class);
         q.f("counter").eq(i);
         List<UncachedObject> l = q.asList();
         assert (l != null && l.size() > 0) : "Nothing found!?!?!?!? Value: " + i;
         UncachedObject fnd = l.get(0);
         assert (fnd != null) : "Error finding element with id " + i;
            assert (fnd.getCounter() == i) : "Counter not equal: " + fnd.getCounter() + " vs. " + i;
            assert (fnd.getValue().equals("Uncached " + i)) : "value not equal: " + fnd.getCounter() + "(" + fnd.getValue() + ") vs. " + i;
        }
        dur = System.currentTimeMillis() - start;
        log.info("Searching  took " + dur + " ms");

#Annotations im Morphium Morphium supports a long list of annotations and here a list of the more common ones, you'd probably use more often:

  • @Entity - needs to be added to a class in order to be able to store it with morphium. A vary similar annotation is @Embedded, which means that this object is only used Embedded. The main difference between the two is, that @Entity marked classes must define an @Id property, @Embedded marked classes do not have to.
  • @Id - put to a field. This is the main ID of this Entity
  • @Index - one of the most important annotations at all. Define indices for fields or the class. This annotation also marks the Entity for use of Indices. So if you want to have indices at a certain property, you need to add that annotation to the class and the property - for more information take a look at the Indexes page in the wiki - Example:
@Index
@Entity
public class MyEntity {
     @Id private String myId;
     @Index private String name;
}
  • @CreationTime - if you want to keep a timestamp of creation time in your entities, this helps. Here also, the Annotation is needed both on the class level and the property that will contain the data.
  • @LifeCycle - this is the marker on class level, that you want to use the lifecycle callback methods. You need to specify the methods with the corresponding callback, e.g. @PreDelete, @PreStore, @PostStore ...
  • @Cache - define cache settings for this Entity - see Caching. If you want explicitly to disable caching for this type, add @NoCache to the class
  • @WriteSafety - this is the corresponding annotation to the MongoDrivers' WriteConcern - here you can specify, how secure your data needs to be stored, like in this example (timeout 0 means wait forever), which represents maximum security.
@WriteSafety(timeout = 0,level = SafetyLevel.WAIT_FOR_ALL_SLAVES,waitForJournalCommit = true)
@Entity
public class MyClass { ... }
//Hint: WAIT_FOR_ALL_SLAVES waits for all AVAILABLE (current online) slaves
  • @ReadPreference - define the read preference level for this entity. Overrides the default value set in config. Means, you can specify per type whether or not slaves are ok to read from.
  • All annotations can be inherited, have a closer look in the wiki at Annotation inheritance