Mapping

Classes

Mapping is achieved through one of two annotations to start: @Entity and @Embedded. In order for Morphia to consider any type for mapping, it must have one of these two annotation. @Entity is used to denote a "top level" type. These types tend to have their own collections whose names can be mapped via the value parameter. Leaving this value blank will leave Morphia free to compute the collection name as defined by the collection naming strategy defined in MapperOptions. That default is currently the camel case value of the class’s simple name. For example, to map a UserProfile entity under this strategy, the collection name would be mapped to userProfile.

Any type annotated with @Entity must have a field annotated with @Id. The type of the field can be any type as long as Morphia, or the driver, has codecs that can map the data to and from mongodb. When mapping an entity, one can also define indexes and schema validations as part of the entity declaration as well.

Constructors

Morphia has traditionally required a 0-argument constructor on any mapped entity. In fact, Morphia still prefers to use such a constructor should one be available. However, as of 2.1, Morphia can also make use another constructor that meets certain criteria

  1. The number of arguments in the constructor must match the number of fields on the class. That is, every field must be represented in the constructor signature.

  2. The names of the arguments must match the names of the fields. Here, there is a bit of flexibility. The argument name can match either the field name as found in the source or the mapped name as determined by any @Property annotation. While this is likely to be the most common case as it is the simplest, for various this won’t always be possible or preferable. If a constructor argument name can’t mirror the field name, using the @Name annotation, an explicit name can be given so that the argument can be properly matched to a field.

Changing the mapping configuration can interfere with Morphia’s ability to map arguments and fields so be careful when making changes to such things as the default field naming strategy or @Property definitions.

For this mapping to take place, the source must be compiled to include the parameters in the generated bytecode. With javac this is typically already configured by default. However, Kotlin users will likely need to configure their builds to include the appropriate option. For maven users, it’s as simple as adding one line to the Kotlin maven plugin <configuration>:

    <javaParameters>true</javaParameters>

External types

Some times persisted types come from external libraries whose source is either unavailable or simply can’t be modified. Using these types would be impossible give the annotation requirements as stated above. Morphia 2.1 introduces a new experimental API that loosens these restrictions a bit. Using Mapper#mapExternal these external types can be passed in for use as embedded types in other entities. An optional instance of @Embedded can created using the EmbeddedBuilder. A null can be passed in to simply accept the default values.

This API is experimental and is likely to shift a bit as it sees usage and feedback from the community.

Versioning

Entities can be versioned to ensure that changes are applied serially and that no other processes are modifying an object in between the time it’s fetched, modified, and written back to the database. To achieve this, simply add a field to a type, it can be a long or a Long and annotation that field with @Version. This field must not be initialized to anything other than zero or null, however. Morphia will take care of the rest. If an object is fetched and another process updates the corresponding document in the database before it can be persisted back, an exception will be thrown when the write is attempted.

Fields

By default, any non-static field on a mapped class will be processed for persistence. If a field is to be excluded from the mapping, it can be decorated with the transient keyword, annotated with @Transient, or with the java.beans.Transient annotation. Otherwise all fields will be included that are defined on the mapped class and any super type. However, Morphia will ignore fields in any super types found in java* packages which includes the standard JDK classes and the Java EE APIs.

There are times when it is necessary to modify a field mapping’s name, e.g. Using the @Property annotation, a new name can be defined that will be used when writing to and reading from the database. During a schema evolution, it is possible to load a field from an old name as well using the @AlsoLoad annotation. Using this annotation, multiple old names can be used to find a field’s value in a returned document from query. However, only the field’s name or the value specified in the @Property annotation will be used when writing documents back to the database. Similarly, if data is only intended to be loaded from the database but never written back, that field can be annotated with @LoadOnly

If you do not specify a name via @Property, the default field naming strategy will be used. The default strategy is to use the field’s name as defined in the source. This strategy can be changed globally via the field naming strategy option on MapperOptions. Simple indexes can be defined on a field if all that is needed for the index is a single field. This can be done via the @Indexed annotation.