Hibernate.orgCommunity Documentation

Chapter 13. CouchDB (Experimental)

13.1. Configuring CouchDB
13.1.1. Annotation based configuration
13.1.2. Programmatic configuration
13.2. Storage principles
13.2.1. Properties and built-in types
13.2.2. Entities
13.2.3. Associations
13.3. Transactions
13.4. Queries

CouchDB is a document-oriented datastore which stores your data in form of JSON documents and exposes its API via HTTP based on REST principles. It is thus very easy to access from a wide range of languages and applications.

Note

Support for CouchDB is considered an EXPERIMENTAL feature as of this release. In particular you should be prepared for possible changes to the persistent representation of mapped objects in future releases.

Also be aware of the fact that partial updates are unsupported at the moment (OGM-388). Instead always the entire document will be replaced during updates. This means that fields possibly written by other applications but not mapped to properties in your domain model will get lost.

The ASSOCIATION_DOCUMENT mode for storing associations should be used with care as there is potential for lost updates (OGM-461). It is recommended to use the IN_ENTITY mode (which is the default).

Should you find any bugs or have feature requests for this dialect, then please open a ticket in the OGM issue tracker.

Hibernate OGM uses the excellent RESTEasy library to talk to CouchDB stores, so there is no need to include any of the Java client libraries for CouchDB in your classpath.

The following properties are available to configure CouchDB support in Hibernate OGM:

CouchDB datastore configuration properties

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

The hostname and port of the CouchDB instance. The optional port is concatenated to the host and separated by a colon. Let’s see a few valid examples:

  • couchdb.example.com
  • couchdb.example.com:5985
  • 2001:db8::ff00:42:8329 (IPv6)
  • [2001:db8::ff00:42:8329]:5985 (IPv6 with port requires the IPv6 to be surrounded by square brackets)

    Listing multiple initial hosts for fault tolerance is not supported. The default value is 127.0.0.1:5984. If left undefined, the default port is 5984.

hibernate.ogm.datastore.port
Deprecated: use hibernate.ogm.datastore.host. The port used by the CouchDB instance. The default value is 5984.
hibernate.ogm.datastore.database
The database to connect to. This property has no default value.
hibernate.ogm.datastore.create_database
Whether to create the specified database in case it does not exist or not. Can be true or false (default). Note that the specified user must have the right to create databases if set to true.
hibernate.ogm.datastore.username
The username used when connecting to the CouchDB server. Note that this user must have the right to create design documents in the chosen database. This property has no default value. Hibernate OGM currently does not support accessing CouchDB via HTTPS; if you’re interested in such functionality, let us know.
hibernate.ogm.datastore.password
The password used to connect to the CouchDB 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.datastore.document.association_storage
Defines the way OGM stores association information in CouchDB. The following two strategies exist (values of the org.hibernate.ogm.datastore.document.options.AssociationStorageType enum): IN_ENTITY (store association information within the entity) and 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.

Note

When bootstrapping a session factory or entity manager factory programmatically, you should use the constants accessible via CouchDBProperties when specifying the configuration properties listed above. Common properties shared between (document) stores are declared on OgmProperties and DocumentStoreProperties, respectively. To ease migration between stores, it is recommended to reference these constants directly from there.

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 CouchDB, you can currently configure the following options using the API:

To set this option 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 CouchDB, provides the entry point into the API. Following the fluent API pattern, you then can configure global options and navigate to single entities or properties to apply options specific to these.

Options given on the property level precede entity-level options. So e.g. the visitors 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. The following describe how entities and associations are mapped to CouchDB documents by Hibernate OGM.

Entities are stored as CouchDB documents and not as BLOBs which means each entity property will be translated into a document field. You can use the name property of the @Table and @Column annotations to rename the collections and the document’s fields if you need to.

CouchDB provides a built-in mechanism for detecting concurrent updates to one and the same document. For that purpose each document has an attribute named _rev (for "revision") which is to be passed back to the store when doing an update. So when writing back a document and the document’s revision has been altered by another writer in parallel, CouchDB will raise an optimistic locking error (you could then e.g. re-read the current document version and try another update).

For this mechanism to work, you need to declare a property for the _rev attribute in all your entity types and mark it with the @Version and @Generated annotations. The first marks it as a property used for optimistic locking, while the latter advices Hibernate OGM to refresh that property after writes since its value is managed by the datastore.

The following shows an example of an entity and its persistent representation in CouchDB.


Note that CouchDB doesn’t have a concept of "tables" or "collections" as e.g. MongoDB does; Instead all documents are stored in one large bucket. Thus Hibernate OGM needs to add two additional attributes: $type which contains the type of a document (entity vs. association documents) and $table which specifies the entity name as derived from the type or given via the @Table annotation.

Note

Attributes whose name starts with the "$" character are managed by Hibernate OGM and thus should not be modified manually. Also it is not recommended to start the names of your attributes with the "$" character to avoid collisions with attributes possibly introduced by Hibernate OGM in future releases.


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

Two main strategies are supported:

Both strategy will create a new document containg the next value to use for the id, the difference between the two strategies is the name of the field containing the values.

Hibernate OGM goes not support the IDENTITY strategy and an exception is thrown at startup when it is used. The AUTO strategy is the same as the SEQUENCE one.

1) TABLE generation strategy



2) SEQUENCE generation 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.


Hibernate OGM CouchDB provides two strategies to store navigation information for associations:

You can switch between the two strategies using:

With this strategy, Hibernate OGM directly stores the id(s) of the other side of the association into a field or an embedded document depending if the mapping concerns a single object or a collection. The field that stores the relationship information is named like the entity property.





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:





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.






With this strategy, Hibernate OGM uses separate association documents (with $type set to "association") to store all navigation information. Each assocation document 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:


Note

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


Using the annotation @JoinTable it is possible to change the value of the document containing the association.




CouchDB 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 CouchDB 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.

Hibernate OGM is a work in progress and we are actively working on JP-QL query support.

In the mean time, you have two strategies to query entities stored by Hibernate OGM:

Because Hibernate OGM stores data in CouchDB in a natural way, you can the HTTP client or REST library of your choice and execute queries (using CouchDB views) on the datastore directly without involving Hibernate OGM. The benefit of this approach is to use the query capabilities of CouchDB. The drawback is that raw CouchDB documents will be returned and not managed entities.

The alternative approach is to index your entities with Hibernate Search. That way, a set of secondary indexes independent of CouchDB is maintained by Hibernate Search and you can write queries on top of them. The benefit of this approach is an nice integration at the JPA / Hibernate API level (managed entities are returned by the queries). The drawback is that you need to store the Lucene indexes somewhere (file system, infinispan grid etc). Have a look at the Infinispan section for more info on how to use Hibernate Search.