Hibernate.orgCommunity Documentation

Chapter 11. MongoDB

11.1. Configuring MongoDB
11.1.1. Adding MongoDB dependencies
11.1.2. MongoDB specific configuration properties
11.1.3. FongoDB Provider
11.1.4. Annotation based configuration
11.1.5. Programmatic configuration
11.2. Storage principles
11.2.1. Properties and built-in types
11.2.2. Entities
11.2.3. Associations
11.3. Transactions
11.4. Optimistic Locking
11.5. Queries
11.5.1. JP-QL queries
11.5.2. Native MongoDB queries
11.5.3. Hibernate Search

MongoDB is a document oriented datastore written in C++ with strong emphasis on ease of use. The nested nature of documents make it a particularly natural fit for most object representations.

This implementation is based upon the MongoDB Java driver. The currently supported version is 2.13.1.

Configuring Hibernate OGM to use MongoDb is easy:

To get started quickly, pay attention to the following options:

And we should have you running. The following properties are available to configure MongoDB support:

MongoDB datastore configuration properties

hibernate.ogm.datastore.provider
To use MongoDB as a datastore provider, this property must be set to mongodb
hibernate.ogm.option.configurator
The fully-qualified class name or an instance of a programmatic option configurator (see Section 11.1.5, “Programmatic configuration”)
hibernate.ogm.datastore.host

The hostname and port of the MongoDB instance. The optional port is concatenated to the host and separated by a colon. When using replica sets, you can define the various servers in a comma separated list of hosts and ports. Let’s see a few valid examples:

  • mongodb.example.com
  • mongodb.example.com:27018
  • 2001:db8::ff00:42:8329 (IPv6)
  • [2001:db8::ff00:42:8329]:27018 (IPv6 with port requires the IPv6 to be surrounded by square brackets)
  • www.example.com, www2.example.com:123, 192.0.2.1, 192.0.2.2:123, 2001:db8::ff00:42:8329, [2001:db8::ff00:42:8329]:123 (replica set)

    The default value is 127.0.0.1:27017. If left undefined, the default port is 27017.

hibernate.ogm.datastore.port
Deprecated: use hibernate.ogm.datastore.host. The port used by the MongoDB instance. Ignored when multiple hosts are defined. The default value is 27017.
hibernate.ogm.datastore.database
The database to connect to. This property has no default value.
hibernate.ogm.datastore.create_database
If set to true, the database will be created if it doesn’t exist. This property default value is false.
hibernate.ogm.datastore.username
The username used when connecting to the MongoDB server. This property has no default value.
hibernate.ogm.datastore.password
The password used to connect to the MongoDB server. This property has no default value. This property is ignored if the username isn’t specified.
hibernate.ogm.error_handler
The fully-qualified class name, class object or an instance of ErrorHandler to get notified upon errors during flushes (see Section 6.3.1, “Acting upon errors during application of changes”)
hibernate.ogm.mongodb.connection_timeout
Defines the timeout used by the driver when the connection to the MongoDB instance is initiated. This configuration is expressed in milliseconds. The default value is 5000.
hibernate.ogm.mongodb.authentication_mechanism

Define the authentication mechanism to use. Possible values are:

  • BEST: Handshakes with the server to find the best authentication mechanism.
  • SCRAM_SHA_1: The SCRAM SHA 1 Challenge Response mechanism as described in this RFC.
  • MONGODB_CR: The MongoDB Challenge Response mechanism (deprecated since MongoDB 3)
  • GSSAPI: The GSSAPI mechanism. See the RFC
  • MONGODB_X509: The MongoDB X.509
  • PLAIN: The PLAIN mechanism. See the RFC
hibernate.ogm.datastore.document.association_storage

Defines the way OGM stores association information in MongoDB. The following two strategies exist (values of the org.hibernate.ogm.datastore.document.options.AssociationStorageType enum):

  • IN_ENTITY: store association information within the entity
  • ASSOCIATION_DOCUMENT: store association information in a dedicated document per association

IN_ENTITY is the default and recommended option unless the association navigation data is much bigger than the core of the document and leads to performance degradation.

hibernate.ogm.mongodb.association_document_storage

Defines how to store assocation documents (applies only if the ASSOCIATION_DOCUMENT association storage strategy is used). Possible strategies are (values of the org.hibernate.ogm.datastore.mongodb.options.AssociationDocumentStorageType enum):

  • GLOBAL_COLLECTION (default): stores the association information in a unique MongoDB collection for all associations
  • COLLECTION_PER_ASSOCIATION stores the association in a dedicated MongoDB collection per association
hibernate.ogm.mongodb.write_concern

Defines the write concern setting to be applied when issuing writes against the MongoDB datastore. Possible settings are (values of the WriteConcernType enum): ACKNOWLEDGED, UNACKNOWLEDGED, FSYNCED, JOURNALED, REPLICA_ACKNOWLEDGED, MAJORITY and CUSTOM. When set to CUSTOM, a custom WriteConcern implementation type has to be specified.

This option is case insensitive and the default value is ACKNOWLEDGED.

hibernate.ogm.mongodb.write_concern_type
Specifies a custom WriteConcern implementation type (fully-qualified name, class object or instance). This is useful in cases where the pre-defined configurations are not sufficient, e.g. if you want to ensure that writes are propagated to a specific number of replicas or given "tag set". Only takes effect if hibernate.ogm.mongodb.write_concern is set to CUSTOM.
hibernate.ogm.mongodb.read_preference
Specifies the ReadPreference to be applied when issuing reads against the MongoDB datastore. Possible settings are (values of the ReadPreferenceType enum): PRIMARY, PRIMARY_PREFERRED, SECONDARY, SECONDARY_PREFERRED and NEAREST. It’s currently not possible to plug in custom read preference types. If you’re interested in such a feature, please let us know.

For more information, please refer to the official documentation.

Note

When bootstrapping a session factory or entity manager factory programmatically, you should use the constants accessible via MongoDBProperties when specifying the configuration properties listed above.

Common properties shared between stores are declared on OgmProperties (a super interface of MongoDBProperties).

For maximum portability between stores, use the most generic interface possible.

Fongo is an in-memory java implementation of MongoDB. It intercepts calls to the standard mongo-java-driver for finds, updates, inserts, removes and other methods. The primary use is for lightweight unit testing where you don’t want to spin up a mongod process.

Hibernate OGM provides a FongoDB provider so during tests it can be used instead of MongoDB driver. Note that you don’t need to change your business code to adapt to FongoDB because all adaptations are done under the cover by Hibernate OGM.

To start using FongoDB provider, you should do two things:

The first one is register the provider by using hibernate.ogm.datastore.provider and setting to fongodb.


The second one is adding FongoDB and SLF4J dependencies in your project.

<dependency>
    <groupId>com.github.fakemongo</groupId>
    <artifactId>fongo</artifactId>
    <scope>test</scope>
    <version>1.6.2</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.5</version>
    <scope>test</scope>
</dependency>

You can read more about FongoDB project and its limitations at https://github.com/fakemongo/fongo

Hibernate OGM allows to configure store-specific options via Java annotations. You can override global configurations for a specific entity or even a specify property by virtue of the location where you place that annotation.

When working with the MongoDB backend, you can specify the following settings:

The following shows an example:


The @WriteConcern annotation on the entity level expresses that all writes should be done using the JOURNALED setting. Similarly, the @ReadPreference annotation advices the engine to preferably read that entity from the primary node if possible. The other two annotations on the type-level specify that all associations of the Zoo class should be stored in separate assocation documents, using a dedicated collection per association. This setting applies to the animals and employees associations. Only the elements of the visitors association will be stored in the document of the corresponding Zoo entity as per the configuration of that specific property which takes precedence over the entity-level configuration.

In addition to the annotation mechanism, Hibernate OGM also provides a programmatic API for applying store-specific configuration options. This can be useful if you can’t modify certain entity types or don’t want to add store-specific configuration annotations to them. The API allows set options in a type-safe fashion on the global, entity and property levels.

When working with MongoDB, you can currently configure the following options using the API:

To set these options via the API, you need to create an OptionConfigurator implementation as shown in the following example:


The call to configureOptionsFor(), passing the store-specific identifier type MongoDB, provides the entry point into the API. Following the fluent API pattern, you then can configure global options (writeConcern(), readPreference()) and navigate to single entities or properties to apply options specific to these (associationStorage() etc.). The call to writeConcern() for the Animal entity shows how a specific write concern type can be used. Here RequiringReplicaCountOf is a custom implementation of WriteConcern which ensures that writes are propagated to a given number of replicas before a write is acknowledged.

Options given on the property level precede entity-level options. So e.g. the animals association of the Zoo class would be stored using the in entity strategy, while all other associations of the Zoo entity would be stored using separate association documents.

Similarly, entity-level options take precedence over options given on the global level. Global-level options specified via the API complement the settings given via configuration properties. In case a setting is given via a configuration property and the API at the same time, the latter takes precedence.

Note that for a given level (property, entity, global), an option set via annotations is overridden by the same option set programmatically. This allows you to change settings in a more flexible way if required.

To register an option configurator, specify its class name using the hibernate.ogm.option.configurator property. When bootstrapping a session factory or entity manager factory programmatically, you also can pass in an OptionConfigurator instance or the class object representing the configurator type.

Hibernate OGM tries to make the mapping to the underlying datastore as natural as possible so that third party applications not using Hibernate OGM can still read and update the same datastore. We worked particularly hard on the MongoDB model to offer various classic mappings between your object model and the MongoDB documents.

To describe things simply, each entity is stored as a MongoDB document. This document is stored in a MongoDB collection named after the entity type. The navigational information for each association from one entity to (a set of) entity is stored in the document representing the entity we are departing from.

Each entity is represented by a document. Each property or more precisely column is represented by a field in this document, the field name being the column name.

Hibernate OGM supports by default the following property types:

  { "text" : "Hello world!" }
  { "delimiter" : "/" }
  { "favorite" : true } # default mapping
  { "favorite" : "T" } # if @Type(type = "true_false") is given
  { "favorite" : "Y" } # if @Type(type = "yes_no") is given
  { "favorite" : 1 } # if @Type(type = "numeric_boolean") is given
  { "display_mask" : "70" }
  { "pdfAsBytes" : BinData(0,"MTIzNDU=") }
  { "urlPort" : 80 }
  { "stockCount" : 12309 }
  { "userId" : NumberLong("-6718902786625749549") }
  { "visitRatio" : 10.39 }
  { "tax_percentage" : 12.34 }
  { "site_weight" : "21.77" }
  { "site_weight" : "444" }
  { "creation" : "2014/11/03 16:19:49:283 +0000" }
  { "last_update" : ISODate("2014-11-03T16:19:49.283Z") }
  { "serialNumber" : "71f5713d-69c4-4b62-ad15-aed8ce8d10e0" }
  { "url" : "http://www.hibernate.org/" }
  { "object_id" : ObjectId("547d9b40e62048750f25ef77") }

Entities are stored as MongoDB documents and not as BLOBs: each entity property will be translated into a document field. You can use @Table and @Column annotations to rename respectively the collection the document is stored in and the document’s field a property is persisted in.



An identifier type may be one of the built-in types or a more complex type represented by an embedded class. When you use a built-in type, the identifier is mapped like a regular property. When you use an embedded class, then the _id is representing a nested document containing the embedded class properties.



Generally, it is recommended though to work with MongoDB’s object id data type. This will facilitate the integration with other applications expecting that common MongoDB id type. To do so, you have two options:

  • Define your id property as org.bson.types.ObjectId
  • Define your id property as String and annotate it with @Type(type="objectid")

In both cases the id will be stored as native ObjectId in the datastore.



You can assign id values yourself or let Hibernate OGM generate the value using the @GeneratedValue annotation.

There are 4 different strategies:

1) IDENTITY generation strategy

The preferable strategy, Hibernate OGM will create the identifier upon insertion. To apply this strategy the id must be one of the following:

like in the following examples:



2) TABLE generation strategy



3) SEQUENCE generation strategy



4) AUTO generation strategy

If the property hibernate.id.new_generator_mappings is set to false, AUTO will behave as the IDENTITY strategy.

If the property hibernate.id.new_generator_mappings is set to true, AUTO will behave as the SEQUENCE strategy.




Hibernate OGM stores elements annotated with @Embedded or @ElementCollection as nested documents of the owning entity.




The class GrandChild has only one attribute name, this means that Hibernate OGM doesn’t need to store the name of the attribute.

If the nested document has two or more fields, like in the following example, Hibernate OGM will store the name of the fields as well.


Note

You can override the column name used for a property of an embedded object. But you need to know that the default column name is the concatenation of the embedding property, a . (dot) and the embedded property (recursively for several levels of embedded objects).

The MongoDB datastore treats dots specifically as it transforms them into nested documents. If you want to override one column name and still keep the nested structure, don’t forget the dots.

That’s a bit abstract, so let’s use an example.

@Entity
class Order {
    @Id String number;
    User user;
    Address shipping;
    @AttributeOverrides({
        @AttributeOverride(name="name", column=@Column(name="delivery.provider"),
        @AttributeOverride(name="expectedDelaysInDays", column=@Column(name="delivery.delays")
    })
    DeliveryProvider deliveryProvider;
    CreditCardType cardType;
}

// default columns
@Embedded
class User {
    String firstname;
    String lastname;
}

// override one column
@Embeddable
public Address {
    String street;
    @Column(name="shipping.dest_city")
    String city;
}

// both columns overridden from the embedding side
@Embeddable
public DeliveryProvider {
    String name;
    Integer expectedDelaysInDays;
}

// do not use dots in the overriding
// and mix levels (bad form)
@Embedded
class CreditCardType {
    String merchant;
    @Column(name="network")
    String network;
}
{
    "_id": "123RF33",
    "user": {
        "firstname": "Emmanuel",
        "lastname": "Bernard"
    },
    "shipping": {
        "street": "1 av des Champs Elysées",
        "dest_city": "Paris"
    },
    "delivery": {
        "provider": "Santa Claus Inc.",
        "delays": "1"
    }
    "network": "VISA",
    "cardType: {
        "merchant": "Amazon"
    }
}

If you share the same embeddable in different places, you can use JPA’s @AttributeOverride to override columns from the embedding side. This is the case of DeliveryProvider in our example.

If you omit the dot in one of the columns, this column will not be part of the nested document. This is demonstrated by the CreditCardType. We advise you against it. Like crossing streams, it is bad form. This approach might not be supported in the future.

Hibernate OGM MongoDB proposes three strategies to store navigation information for associations. The three possible strategies are:

To switch between these strategies, use of the three approaches to options:

In this strategy, Hibernate OGM stores the id(s) of the associated entity(ies) into the entity document itself. This field stores the id value for to-one associations and an array of id values for to-many associations. An embedded id will be represented by a nested document. For indexed collections (i.e. List or Map), the index will be stored along the id.

Note

When using this strategy the annotations @JoinTable will be ignored because no collection is created for associations.

You can use @JoinColumn to change the name of the field that stores the foreign key (as an example, see Example 11.24, “Unidirectional one-to-one with @JoinColumn”).



In a true one-to-one association, it is possible to share the same id between the two entities and therefore a foreign key is not required. You can see how to map this type of association in the following example:






Here we see that the embedded id is represented as a nested document and directly referenced by the associations.



A map can be used to represents an association, in this case Hibernate OGM will store the key of the map and the associated id.


You can use @MapKeyColumn to rename the column containing the key of the map.





In this strategy, Hibernate OGM creates a MongoDB collection per association in which it will store all navigation information for that particular association.

This is the strategy closest to the relational model. If an entity A is related to B and C, 2 collections will be created. The name of this collection is made of the association table concatenated with associations_.

For example, if the BankAccount and Owner are related, the collection used to store will be named associations_Owner_BankAccount. You can rename The prefix is useful to quickly identify the association collections from the entity collections. You can also decide to rename the collection representing the association using @JoinTable (see an example)

Each document of an association collection has the following structure:

  • _id contains the id of the owner of relationship
  • rows contains all the id of the related entities

Note

The preferred approach is to use the in-entity strategy but this approach can alleviate the problem of having documents that are too big.



Note

This strategy won’t affect *-to-one associations or embedded collections.


The order of the element in the list might be preserved using @OrderColumn. Hibernate OGM will store the order adding an additional fieldd to the document containing the association.




You can change the name of the collection containing the association using the @JoinTable annotation. In the following example, the name of the collection containing the association is OwnerBankAccounts (instead of the default associations_AccountOwner_BankAccount)


With this strategy, Hibernate OGM creates a single collection named Associations in which it will store all navigation information for all associations. Each document of this collection is structured in 2 parts. The first is the _id field which contains the identifier information of the association owner and the name of the association table. The second part is the rows field which stores (into an embedded collection) all ids that the current instance is related to.


For a bidirectional relationship, another document is created where ids are reversed. Don’t worry, Hibernate OGM takes care of keeping them in sync:






MongoDB does not support transactions. Only changes applied to the same document are done atomically. A change applied to more than one document will not be applied atomically. This problem is slightly mitigated by the fact that Hibernate OGM queues all changes before applying them during flush time. So the window of time used to write to MongoDB is smaller than what you would have done manually.

We recommend that you still use transaction demarcations with Hibernate OGM to trigger the flush operation transparently (on commit). But do not consider rollback as a possibility, this won’t work.

MongoDB does not provide a built-in mechanism for detecting concurrent updates to the same document but it provides a way to execute atomic find and update operations. By exploiting this commands Hibernate OGM can detect concurrent modifications to the same document.

You can enable optimistic locking detection using the annotation @Version:


The @Version annotation define which attribute will keep track of the version of the document, Hibernate OGM will update the field when required and if two changes from two different sessions (for example) are applied to the same document a org.hibernate.StaleObjectStateException is thrown.

You can use @Column to change the name of the field created on MongoDB:


You can express queries in a few different ways:

While you can use JP-QL for simple queries, you might hit limitations. The current recommended approach is to use native MongoQL if your query involves nested (list of) elements.

Hibernate OGM also supports certain forms of native queries for MongoDB. Currently two forms of native queries are available via the MongoDB backend:

The former always maps results to entity types. The latter either maps results to entity types or to certain supported forms of projection. Note that parameterized queries are not supported by MongoDB, so don’t expect Query#setParameter() to work.

Warning

Specifying native MongoDB queries using the CLI syntax is an EXPERIMENTAL feature for the time being. Currently only find() and count() queries are supported via the CLI syntax. Further query types (including updating queries) may be supported in future revisions.

No cursor operations such as sort() are supported. Instead use the corresponding MongoDB query modifiers such as $orderby within the criteria parameter.

JSON parameters passed via the CLI syntax must be specified using the strict mode. Specifically, keys need to be given within quotes, e.g. use db.WILDE_POEM.find({ \"name\" : \"Athanasia\" }) instead of db.WILDE_POEM.find({ name : \"Athanasia\" }). The only relaxation of this is that single quotes may be used when specifying attribute names/values to facilitate embedding queries within Java strings, e.g. db.WILDE_POEM.find({ 'name' : 'Athanasia' }).

Note that results of projections are returned as retrieved from the MongoDB driver at the moment and are not (yet) converted using suitable Hibernate OGM type implementations.

You can execute native queries as shown in the following example:


The result of a query is a managed entity (or a list thereof) or a projection of attributes in form of an object array, just like you would get from a JP-QL query.


Native queries can also be created using the @NamedNativeQuery annotation:


Hibernate OGM stores data in a natural way so you can still execute queries using the MongoDB driver, the main drawback is that the results are going to be raw MongoDB documents and not managed entities.