Hibernate.orgCommunity Documentation
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.
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
couchdb_experimental
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.host
.
The port used by the CouchDB instance.
The default value is 5984
.true
or false
(default). Note that the specified user must have the right to
create databases if set to true
.ErrorHandler
to get notified upon errors during flushes (see Section 6.3.1, “Acting upon errors during application of changes”)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.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.
Hibernate OGM allows to configure store-specific options via Java annotations.
When working with the CouchDB backend, you can specify how associations should be stored
using the AssociationStorage
annotation
(refer to Section 13.2, “Storage principles” to learn more about association storage strategies in general).
The following shows an example:
Example 13.1. Configuring the association storage strategy using annotations
@Entity
@AssociationStorage(AssociationStorageType.ASSOCIATION_DOCUMENT)
public class Zoo {
@OneToMany
private Set<Animal> animals;
@OneToMany
private Set<Person> employees;
@OneToMany
@AssociationStorage(AssociationStorageType.IN_ENTITY)
private Set<Person> visitors;
//...
}
The annotation on the entity level expresses that all associations of the Zoo
class should be stored in separate assocation documents.
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 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:
Example 13.2. Example of an option configurator
public class MyOptionConfigurator extends OptionConfigurator {
@Override
public void configure(Configurable configurable) {
configurable.configureOptionsFor( CouchDB.class )
.associationStorage( AssociationStorageType.ASSOCIATION_DOCUMENT )
.entity( Zoo.class )
.property( "visitors", ElementType.FIELD )
.associationStorage( AssociationStorageType.IN_ENTITY )
.entity( Animal.class )
.associationStorage( AssociationStorageType.ASSOCIATION_DOCUMENT );
}
}
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.
Hibernate OGM doesn’t store null values in CouchDB, setting a value to null will be the same as removing the field in the corresponding object in the db.
Hibernate OGM support by default the following types:
java.lang.String
{ "text" : "Hello world!" }
java.lang.Character
(or char primitive) { "delimiter" : "/" }
java.lang.Boolean
(or boolean primitive) { "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
java.lang.Byte
(or byte primitive) { "display_mask" : "70" }
java.lang.Short
(or short primitive) { "urlPort" : 80 }
java.lang.Integer
(or int primitive) { "stockCount" : 12309 }
java.lang.Long
(or long primitive) { "userId" : "-6718902786625749549" }
java.lang.Float
(or float primitive) { "visitRatio" : 10.4 }
java.lang.Double
(or double primitive) { "tax_percentage" : 12.34 }
java.math.BigDecimal
{ "site_weight" : "21.77" }
java.math.BigInteger
{ "site_weight" : "444" }
java.util.Calendar
{ "creation" : "2014-11-18T15:51:26.252Z" }
java.util.Date
{ "last_update" : "2014-11-18T15:51:26.252Z" }
java.util.UUID
{ "serialNumber" : "71f5713d-69c4-4b62-ad15-aed8ce8d10e0" }
java.util.URL
{ "url" : "http://www.hibernate.org/" }
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.
Not mapping the _rev
attribute may cause lost updates,
as Hibernate OGM needs to re-read the current revision before doing an update in this case.
Thus a warning will be issued during initialization for each entity type which fails to map that property.
The following shows an example of an entity and its persistent representation in CouchDB.
Example 13.3. Example of an entity and its representation in CouchDB
@Entity
public class News {
@Id
private String id;
@Version
@Generated
@Column(name="_rev")
private String revision;
private String title;
private String description;
//getters, setters ...
}
{
"_id": "News:id_:news-1_",
"_rev": "1-d1cd3b00a677a2e31cd0480a796e8480",
"$type": "entity",
"$table": "News",
"title": "On the merits of NoSQL",
"description": "This paper discuss why NoSQL will save the world for good"
}
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.
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.
Example 13.4. Rename field and collection using @Table and @Column
@Entity
@Table(name="Article")
public class News {
@Id
@Column(name="code")
private String id;
@Version
@Generated
@Column(name="_rev")
private String revision;
private String title;
@Column(name="desc")
private String description;
//getters, setters ...
}
{
"_id": "Article:code_:news-1_",
"_rev": "1-d1cd3b00a677a2e31cd0480a796e8480",
"$type": "entity",
"$table": "Article",
"title": "On the merits of NoSQL",
"desc": "This paper discuss why NoSQL will save the world for good"
}
The _id
field of a CouchDB document is directly used
to store the identifier columns mapped in the entities.
You can use any persistable Java type as identifier type, e.g. String
or long
.
Hibernate OGM will convert the @Id
property into a _id
document field
so you can name the entity id like you want, it will always be stored into _id
.
Note that you also can work with embedded ids (via @EmbeddedId
),
but be aware of the fact that CouchDB doesn’t support storing embedded structures in the _id
attribute.
Hibernate OGM thus will create a concatenated representation of the embedded id’s properties in this case.
Example 13.5. Entity with @EmbeddedId
@Entity
public class News {
@EmbeddedId
private NewsID newsId;
// getters, setters ...
}
@Embeddable
public class NewsID implements Serializable {
private String title;
private String author;
// getters, setters ...
}
{
"_id": "News:newsId.author_newsId.title_:Guillaume_How to use Hibernate OGM ?_",
"_rev": "2-1f02af4fabba7b4fa7394f1167244226",
"$type": "entity",
"$table": "News",
"newsId": {
"author": "Guillaume",
"title": "How to use Hibernate OGM ?"
}
}
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.
Example 13.6. Id generation strategy TABLE using default values
@Entity
public class Video {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
private String name
// getters, setters, ...
}
{
"_id": "Video:id_:1_",
"_rev": "1-b4c16b6cd8a083f2173f8df19bd24750",
"$type": "entity",
"$table": "Video",
"id": 1,
"name": "Scream",
"director": "Wes Craven"
}
{
"_id": "hibernate_sequences:sequence_name:default",
"_rev": "1-ebb82f1cea26d57f47a290fb0c1cc58f",
"$type": "sequence",
"next_val": "2"
}
Example 13.7. Id generation strategy TABLE using a custom table
@Entity
public class Video {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "video")
@TableGenerator(
name = "video",
table = "sequences",
pkColumnName = "key",
pkColumnValue = "video",
valueColumnName = "seed"
)
private Integer id;
private String name;
// getter, setters, ...
}
@Entity
public class Video {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "video")
@TableGenerator(
name = "video",
table = "sequences",
pkColumnName = "key",
pkColumnValue = "video",
valueColumnName = "seed"
)
private Integer id;
private String name
// getters, setters, ...
}
{
"_id": "sequences:key:video",
"_rev": "2-78b3450e0658743164828c4076e06a49",
"$type": "sequence",
"seed": "101"
}
2) SEQUENCE generation strategy
Example 13.8. SEQUENCE id generation strategy using default values
@Entity
public class Song {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String title;
// getters, setters ...
}
{
"_id": "Song:id_:2_",
"_rev": "1-63bc100449fb2840067028c3825ed784",
"$type": "entity",
"$table": "Song",
"id": "2",
"title": "Ave Maria",
"singer": "Charlotte Church"
}
{
"_id": "hibernate_sequences:sequence_name:hibernate_sequence",
"_rev": "2-dcc622bcb1389ad18829dcfc8b812c87",
"$type": "sequence",
"next_val": "3"
}
Example 13.9. SEQUENCE id generation strategy using custom values
@Entity
public class Song {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "songSequenceGenerator")
@SequenceGenerator(
name = "songSequenceGenerator",
sequenceName = "song_sequence",
initialValue = 2,
allocationSize = 20
)
private Long id;
private String title;
// getters, setters ...
}
{
"_id": "Song:id_:2_",
"_rev": "1-63bc100449fb2840067028c3825ed784",
"$type": "entity",
"$table": "Song",
"id": "2",
"title": "Ave Maria",
"singer": "Charlotte Church"
}
{
"_id": "hibernate_sequences:sequence_name:song_sequence",
"_rev": "2-df47883f076c84cb953f9184de7aa82a",
"$type": "sequence",
"next_val": "21"
}
Hibernate OGM stores elements annotated with @Embedded
or @ElementCollection
as nested documents of the owning entity.
Example 13.10. Embedded object
@Entity
public class News {
@Id
private String id;
private String title;
@Embedded
private NewsPaper paper;
// getters, setters ...
}
@Embeddable
public class NewsPaper {
private String name;
private String owner;
// getters, setters ...
}
{
"_id": "News:id_:939c892d-1129-4aff-abf8-e6c26e59dcb_",
"_rev": "2-1f02af4fabba7b4fa7394f1167244226",
"$type": "entity",
"$table": "News",
"id": "939c892d-1129-4aff-abf8-e6c26e59dcb",
"paper": {
"name": "NoSQL journal of prophecies",
"owner": "Delphy"
}
}
Example 13.11. @ElementCollection with primitive types
@Entity
public class AccountWithPhone {
@Id
private String id;
@ElementCollection
private List<String> mobileNumbers;
// getters, setters ...
}
AccountWithPhone collection
{
"_id": "AccountWithPhone:id_:2_",
"_rev": "2-a71f7c0d621a08232568f9840bff05ce",
"$type": "entity",
"$table": "AccountWithPhone",
"id": "2",
"mobileNumbers": [
"+1-222-555-0222",
"+1-202-555-0333"
]
}
Example 13.12. @ElementCollection with one attribute
@Entity
public class GrandMother {
@Id
private String id;
@ElementCollection
private List<GrandChild> grandChildren = new ArrayList<GrandChild>();
// getters, setters ...
}
@Embeddable
public class GrandChild {
private String name;
// getters, setters ...
}
{
"_id": "grandmother:id_:86ada718-f2a2-4299-b6ac-3d90b1ef2331_",
"_rev": "2-1f02af4fabba7b4fa7394f1167244226",
"$type": "entity",
"$table": "grandmother",
"id": "86ada718-f2a2-4299-b6ac-3d90b1ef2331",
"grandChildren" : [ "Luke", "Leia" ]
}
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.
Example 13.13. @ElementCollection with @OrderColumn
@Entity
public class GrandMother {
@Id
private String id;
@ElementCollection
@OrderColumn( name = "birth_order" )
private List<GrandChild> grandChildren = new ArrayList<GrandChild>();
// getters, setters ...
}
@Embeddable
public class GrandChild {
private String name;
// getters, setters ...
}
{
"_id": "GrandMother:id_:86ada718-f2a2-4299-b6ac-3d90b1ef2331_",
"_rev": "2-1f02af4fabba7b4fa7394f1167244226",
"$type": "entity",
"$table": "GrandMother",
"grandChildren" : [
{
"name" : "luke",
"birth_order" : 0
},
{
"name" : "leia",
"birthorder" : 1
}
]
}
Hibernate OGM CouchDB provides two strategies to store navigation information for associations:
IN_ENTITY
(default)ASSOCIATION_DOCUMENT
You can switch between the two strategies using:
@AssociationStorage
annotation (see Section 13.1.1, “Annotation based configuration”)hibernate.ogm.datastore.document.association_storage
configuration propertyWith 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.
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 13.14. Java entity
@Entity
public class AccountOwner {
@Id
private String id;
@ManyToMany
public Set<BankAccount> bankAccounts;
// getters, setters, ...
Example 13.15. JSON representation
{
"_id": "AccountOwner:id_:owner0001_",
"_rev": "1-d1cd3b00a677a2e31cd0480a796e8480",
"$type": "entity",
"$table": "AccountOwner",
"bankAccounts" : [
"accountABC",
"accountXYZ"
]
}
Example 13.16. Unidirectional one-to-one
@Entity
public class Vehicule {
@Id
private String id;
private String brand;
// getters, setters ...
}
@Entity
public class Wheel {
@Id
private String id;
private double diameter;
@OneToOne
private Vehicule vehicule;
// getters, setters ...
}
{
"_id": "Vehicule:id_:V001_",
"_rev": "1-41dc2d2fd68ce2fc683241a60e59a676",
"$type": "entity",
"$table": "Vehicule",
"id": "V001",
"brand": "Mercedes",
}
{
"_id": "Wheel:id_:W1_",
"_rev": "1-30430d67174484f6b647480dbf781f55",
"$type": "entity",
"$table": "Wheel",
"id": "W1",
"diameter" : 0,
"vehicule_id" : "V001"
}
Example 13.17. Unidirectional one-to-one with @JoinColumn
@Entity
public class Vehicule {
@Id
private String id;
private String brand;
// getters, setters ...
}
@Entity
public class Wheel {
@Id
private String id;
private double diameter;
@OneToOne
@JoinColumn( name = "part_of" )
private Vehicule vehicule;
// getters, setters ...
}
{
"_id": "Vehicule:id_:V001_",
"_rev": "1-41dc2d2fd68ce2fc683241a60e59a676",
"$type": "entity",
"$table": "Vehicule",
"id": "V001",
"brand": "Mercedes",
}
{
"_id": "Wheel:id_:W1_",
"_rev": "1-30430d67174484f6b647480dbf781f55",
"$type": "entity",
"$table": "Wheel",
"id": "W1",
"diameter" : 0,
"part_of" : "V001"
}
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:
Example 13.18. Unidirectional one-to-one with @MapsId and @PrimaryKeyJoinColumn
@Entity
public class Vehicule {
@Id
private String id;
private String brand;
// getters, setters ...
}
@Entity
public class Wheel {
@Id
private String id;
private double diameter;
@OneToOne
@PrimaryKeyJoinColumn
@MapsId
private Vehicule vehicule;
// getters, setters ...
}
{
"_id": "Vehicule:id_:V001_",
"_rev": "1-41dc2d2fd68ce2fc683241a60e59a676",
"$type": "entity",
"$table": "Vehicule",
"id": "V001",
"brand": "Mercedes",
}
{
"_id": "Wheel:vehicule/_id_:V001_",
"_rev": "1-30430d67174484f6b647480dbf781f55",
"$type": "entity",
"$table": "Wheel",
"diameter" : 0,
"vehicule_id" : "V001"
}
Example 13.19. Bidirectional one-to-one
@Entity
public class Husband {
@Id
private String id;
private String name;
@OneToOne
private Wife wife;
// getters, setters ...
}
@Entity
public class Wife {
@Id
private String id;
private String name;
@OneToOne
private Husband husband;
// getters, setters ...
}
{
"_id": "Husband:id_:alex_",
"_rev": "2-8f976fc216130fb40144b000910b9c1d",
"$type": "entity",
"$table": "Husband",
"id" : "alex",
"name" : "Alex",
"wife" : "bea"
}
{
"_id": "Wife:id_:bea_",
"_rev": "2-69130cc082958becbdf4154a3d19c2e6",
"$type": "entity",
"$table": "Wife",
"id" : "bea",
"name" : "Bea",
"husband" : "alex"
}
Example 13.20. Unidirectional one-to-many
@Entity
public class Basket {
@Id
private String id;
private String owner;
@OneToMany
private List<Product> products = new ArrayList<Product>();
// getters, setters ...
}
@Entity
public class Product {
@Id
private String name;
private String description;
// getters, setters ...
}
Basket collection
{
"_id": "Basket:id_:davide/_basket_",
"_rev": "2-8f976fc216130fb40144b000910b9c1d",
"$type": "entity",
"$table": "Basket",
"id" : "davide_basket",
"owner" : "Davide",
"products" : [ "Beer", "Pretzel" ]
}
Product collection
{
"_id": "Product:name_:Beer_",
"_rev": "1-e2a51de970f3e5a0e1118989eef1cf7b",
"$type": "entity",
"$table": "Product",
"name" : "Beer",
"description" : "Tactical nuclear penguin"
}
{
"_id": "Product:name_:Pretzel_",
"_rev": "1-b78ce2687db2fb550d9e8753423db3f3",
"$type": "entity",
"$table": "Product",
"name" : "Pretzel",
"description" : "Glutino Pretzel Sticks"
}
Example 13.21. Unidirectional one-to-many using one collection per strategy with @OrderColumn
@Entity
public class Basket {
@Id
private String id;
private String owner;
@OneToMany
private List<Product> products = new ArrayList<Product>();
// getters, setters ...
}
@Entity
public class Product {
@Id
private String name;
private String description;
// getters, setters ...
}
Basket collection
{
"_id" : "davide_basket",
"owner" : "Davide"
}
Product collection
{
"_id" : "Pretzel",
"description" : "Glutino Pretzel Sticks"
}
{
"_id" : "Beer",
"description" : "Tactical nuclear penguin"
}
associations_Basket_Product collection
{
"_id" : { "Basket_id" : "davide_basket" },
"rows" : [
{
"products_name" : "Pretzel",
"products_ORDER" : 1
},
{
"products_name" : "Beer",
"products_ORDER" : 0
}
]
}
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.
Example 13.22. Unidirectional one-to-many using maps with defaults
@Entity
public class User {
@Id
private String id;
@OneToMany
private Map<String, Address> addresses = new HashMap<String, Address>();
// getters, setters ...
}
@Entity
public class Address {
@Id
private String id;
private String city;
// getters, setters ...
}
{
"_id": "User:id_:user/_001",
"_rev": "3-77de96250380a79a20a38e78826bf4f7",
"$type": "entity",
"$table": "User",
"id" : "user_001",
"addresses" : [
{
"addresses_KEY" : "work",
"addresses_id" : "address_001"
},
{
"addresses_KEY" : "home",
"addresses_id" : "address_002"
}
]
}
{
"_id": "Address:id_:address/_001",
"_rev": "1-dd366cd017f87548956dc55d3b12fefd",
"$type": "entity",
"$table": "Address",
"id" : "address_001",
"city" : "Rome"
}
{
"_id": "Address:id_:address/_001",
"_rev": "1-04f13666a62473ac951dd039c7cdc780",
"$type": "entity",
"$table": "Address",
"id" : "address_002",
"city" : "Paris"
}
You can use @MapKeyColumn to rename the column containing the key of the map.
Example 13.23. Unidirectional one-to-many using maps with @MapKeyColumn
@Entity
public class User {
@Id
private String id;
@OneToMany
@MapKeyColumn(name = "addressType")
private Map<String, Address> addresses = new HashMap<String, Address>();
// getters, setters ...
}
@Entity
public class Address {
@Id
private String id;
private String city;
// getters, setters ...
}
{
"_id": "User:id_:user/_001",
"_rev": "3-77de96250380a79a20a38e78826bf4f7",
"$type": "entity",
"$table": "User",
"id" : "user_001",
"addresses" : [
{
"addressType" : "work",
"addresses_id" : "address_001"
},
{
"addressType" : "home",
"addresses_id" : "address_002"
}
]
}
{
"_id": "Address:id_:address/_001",
"_rev": "1-dd366cd017f87548956dc55d3b12fefd",
"$type": "entity",
"$table": "Address",
"id" : "address_001",
"city" : "Rome"
}
{
"_id": "Address:id_:address/_001",
"_rev": "1-04f13666a62473ac951dd039c7cdc780",
"$type": "entity",
"$table": "Address",
"id" : "address_002",
"city" : "Paris"
}
Example 13.24. Unidirectional many-to-one
@Entity
public class JavaUserGroup {
@Id
private String jugId;
private String name;
// getters, setters ...
}
@Entity
public class Member {
@Id
private String id;
private String name;
@ManyToOne
private JavaUserGroup memberOf;
// getters, setters ...
}
{
"_id": "JavaUserGroups:id_:summer/_camp",
"_rev": "1-04f13666a62473ac951dd039c7cdc780",
"$type": "entity",
"$table": "JavaUserGroup",
"id" : "summer_camp",
"name" : "JUG Summer Camp"
}
{
"_id": "Member:id_:jerome",
"_rev": "1-880bf595c39a965dec0216d9d990ebd1",
"$type": "entity",
"$table": "Member",
"id" : "jerome",
"name" : "Jerome"
"memberOf_jugId" : "summer_camp"
}
{
"_id": "Member:id_:emmanuel",
"_rev": "1-18e83ce9774a769814c401c49a5afcf3",
"$type": "entity",
"$table": "Member",
"id" : "emmanuel",
"name" : "Emmanuel Bernard"
"memberOf_jugId" : "summer_camp"
}
Example 13.25. Bidirectional many-to-one
@Entity
public class SalesForce {
@Id
private String id;
private String corporation;
@OneToMany(mappedBy = "salesForce")
private Set<SalesGuy> salesGuys = new HashSet<SalesGuy>();
// getters, setters ...
}
@Entity
public class SalesGuy {
private String id;
private String name;
@ManyToOne
private SalesForce salesForce;
// getters, setters ...
}
{
"_id": "SalesForce:id_:red/_hat",
"_rev": "1-04f13666a62473ac951dd039c7cdc780",
"$type": "entity",
"$table": "SalesForce",
"_id": "red_hat",
"corporation": "Red Hat",
"salesGuys": [ "eric", "simon" ]
}
{
"_id": "SalesGuy:id_:eric",
"_rev": "1-18e83ce9774a769814c401c49a5afcf3",
"$type": "entity",
"$table": "SalesGuy",
"id": "eric",
"name": "Eric"
"salesForce_id": "red_hat",
}
{
"_id": "SalesGuy:id_:eric",
"_rev": "1-18e83ce9774a769814c401c49a5afcf3",
"$type": "entity",
"$table": "SalesGuy",
"id": "simon",
"name": "Simon",
"salesForce_id": "red_hat"
}
Example 13.26. Unidirectional many-to-many using in entity strategy
@Entity
public class Student {
@Id
private String id;
private String name;
// getters, setters ...
}
@Entity
public class ClassRoom {
@Id
private Long id;
private String lesson;
@ManyToMany
private List<Student> students = new ArrayList<Student>();
// getters, setters ...
}
{
"_id": "ClassRoom:id_:1_",
"_rev": "2-ae1d9748a84af991615fa842a7e796ea",
"$type": "entity",
"$table": "ClassRoom",
"id": "1",
"students": [
"mario",
"john"
],
"name": "Math"
}
{
"_id": "ClassRoom:id_:2_",
"_rev": "2-0e58f03f518c5c1982bb7936308604e4",
"$type": "entity",
"$table": "ClassRoom",
"id": "2",
"students": [
"kate",
"mario"
],
"name": "English"
}
{
"_id": "Student:id_:john_",
"_rev": "1-60b642619f0e62e079da8a6521ea9750",
"$type": "entity",
"$table": "Student",
"id": "john",
"name": "John Doe"
}
{
"_id": "Student:id_:kate_",
"_rev": "1-911bb5cbc9b16c6d90f1e91e856a9224",
"$type": "entity",
"$table": "Student",
"id": "kate",
"name": "Kate Doe"
}
{
"_id": "Student:id_:mario_",
"_rev": "1-7dc611e3c627a837033e7eb5e244f7f8",
"$type": "entity",
"$table": "Student",
"id": "mario",
"name": "Mario Rossi"
}
Example 13.27. Bidirectional many-to-many
@Entity
public class AccountOwner {
@Id
private String id;
private String SSN;
@ManyToMany
private Set<BankAccount> bankAccounts;
// getters, setters ...
}
@Entity
public class BankAccount {
@Id
private String id;
private String accountNumber;
@ManyToMany( mappedBy = "bankAccounts" )
private Set<AccountOwner> owners = new HashSet<AccountOwner>();
// getters, setters ...
}
{
"_id": "AccountOwner:id_:owner/_1_",
"_rev": "3-07eb9959eac966afedd0547aa74a59a7",
"$type": "entity",
"$table": "AccountOwner",
"id": "owner_1",
"SSN": "0123456",
"bankAccounts": [
"account_1",
"account_2"
]
}
{
"_id": "BankAccount:id_:account/_1_",
"_rev": "2-87252fffa4ab443485f55504215fbed3",
"$type": "entity",
"$table": "BankAccount",
"id": "account_1",
"accountNumber": "X2345000",
"owners": [
"owner_1"
]
}
{
"_id": "BankAccount:id_:account/_2_",
"_rev": "2-15bdfeda927dd10fa10aa19ceee4ea34",
"$type": "entity",
"$table": "BankAccount",
"id": "account_2",
"accountNumber": "ZZZ-009",
"owners": [
"owner_1"
]
}
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.
Example 13.28. Unidirectional relationship
{
"_id": "AccountOwner_BankAccount:owners/_id_:4f5b48ad-f074-4a64-8cf4-1f9c54a33f76_",
"_rev": "1-18ef25ec73c1942c45c868aa92f24f2c",
"$type": "association",
"rows": [
7873a2a7-c77c-447c-b000-890f0a4dfa9a
]
}
For a bidirectional relationship, another document is created where ids are reversed. Don’t worry, Hibernate OGM takes care of keeping them in sync:
Example 13.29. Bidirectional relationship
{
"_id": "AccountOwner_BankAccount:owners/_id_:4f5b48ad-f074-4a64-8cf4-1f9c54a33f76_",
"_rev": "1-18ef25ec73c1942c45c868aa92f24f2c",
"$type": "association",
"rows": [
"7873a2a7-c77c-447c-b000-890f0a4dfa9a"
]
}
{
"_id": "AccountOwner_BankAccount:bankAccounts/_id_:7873a2a7-c77c-447c-b000-890f0a4dfa9a_",
"_rev": "1-78e92f980745941a779abb914da65a6c",
"$type": "association",
"rows": [
"4f5b48ad-f074-4a64-8cf4-1f9c54a33f76"
]
}
This strategy won’t affect *-to-one associations or embedded collections.
Example 13.30. Unidirectional one-to-many using document strategy
@Entity
public class Basket {
@Id
private String id;
private String owner;
@OneToMany
private List<Product> products = new ArrayList<Product>();
// getters, setters ...
}
@Entity
public class Product {
@Id
private String name;
private String description;
// getters, setters ...
}
{
"_id": "Basket:id_:davide/_basket_",
"_rev": "1-ba920ac3d1ed5544a71d6c6c5f2ee286",
"$type": "entity",
"$table": "Basket",
"id": "davide_basket",
"owner": "Davide"
}
{
"_id": "Basket:id_:davide/_basket_",
"_rev": "1-ba920ac3d1ed5544a71d6c6c5f2ee286",
"$type": "entity",
"$table": "Basket",
"id": "davide_basket",
"owner": "Davide"
}
{
"_id": "Product:name_:Pretzel_",
"_rev": "1-b78ce2687db2fb550d9e8753423db3f3",
"$type": "entity",
"$table": "Product",
"description": "Glutino Pretzel Sticks",
"name": "Pretzel"
}
{
"_id": "Basket_Product:Basket/_id_:davide/_basket_",
"_rev": "1-f6d9aa44a7ca4f01b68c94b1f5599956",
"$type": "association",
"rows": [
"Beer",
"Pretzel"
]
}
Using the annotation @JoinTable
it is possible to change the value of
the document containing the association.
Example 13.31. Unidirectional one-to-many using document strategy with @JoinTable
@Entity
public class Basket {
@Id
private String id;
private String owner;
@OneToMany
@JoinTable( name = "BasketContent" )
private List<Product> products = new ArrayList<Product>();
// getters, setters ...
}
@Entity
public class Product {
@Id
private String name;
private String description;
// getters, setters ...
}
{
"_id": "Basket:id_:davide/_basket_",
"_rev": "1-ba920ac3d1ed5544a71d6c6c5f2ee286",
"$type": "entity",
"$table": "Basket",
"id": "davide_basket",
"owner": "Davide"
}
{
"_id": "Basket:id_:davide/_basket_",
"_rev": "1-ba920ac3d1ed5544a71d6c6c5f2ee286",
"$type": "entity",
"$table": "Basket",
"id": "davide_basket",
"owner": "Davide"
}
{
"_id": "Product:name_:Pretzel_",
"_rev": "1-b78ce2687db2fb550d9e8753423db3f3",
"$type": "entity",
"$table": "Product",
"description": "Glutino Pretzel Sticks",
"name": "Pretzel"
}
{
"_id": "BasketContent:Basket/_id_:davide/_basket_",
"_rev": "1-f6d9aa44a7ca4f01b68c94b1f5599956",
"$type": "association",
"rows": [
"Beer",
"Pretzel"
]
}
Example 13.32. Unidirectional many-to-many using document strategy
@Entity
public class Student {
@Id
private String id;
private String name;
// getters, setters ...
}
@Entity
public class ClassRoom {
@Id
private Long id;
private String lesson;
@ManyToMany
private List<Student> students = new ArrayList<Student>();
// getters, setters ...
}
{
"_id": "ClassRoom:id_:1_",
"_rev": "2-ae1d9748a84af991615fa842a7e796ea",
"$type": "entity",
"$table": "ClassRoom",
"id": "1",
"name": "Math"
}
{
"_id": "ClassRoom:id_:2_",
"_rev": "2-0e58f03f518c5c1982bb7936308604e4",
"$type": "entity",
"$table": "ClassRoom",
"id": "2",
"name": "English"
}
{
"_id": "Student:id_:john_",
"_rev": "1-60b642619f0e62e079da8a6521ea9750",
"$type": "entity",
"$table": "Student",
"id": "john",
"name": "John Doe"
}
{
"_id": "Student:id_:kate_",
"_rev": "1-911bb5cbc9b16c6d90f1e91e856a9224",
"$type": "entity",
"$table": "Student",
"id": "kate",
"name": "Kate Doe"
}
{
"_id": "Student:id_:mario_",
"_rev": "1-7dc611e3c627a837033e7eb5e244f7f8",
"$type": "entity",
"$table": "Student",
"id": "mario",
"name": "Mario Rossi"
}
{
"_id": "ClassRoom_Student:ClassRoom/_id_:1_",
"_rev": "1-351e470a8c134a084d9ad282796a7464",
"$type": "association",
"rows": [
"mario",
"john"
]
}
{
"_id": "ClassRoom_Student:ClassRoom/_id_:2_",
"_rev": "1-825d1900ec216dc73e0152564de8e975",
"$type": "association",
"rows": [
"kate"
]
}
Example 13.33. Bidirectional many-to-many using document strategy
@Entity
public class AccountOwner {
@Id
private String id;
private String SSN;
@ManyToMany
private Set<BankAccount> bankAccounts;
// getters, setters ...
}
@Entity
public class BankAccount {
@Id
private String id;
private String accountNumber;
@ManyToMany(mappedBy = "bankAccounts")
private Set<AccountOwner> owners = new HashSet<AccountOwner>();
// getters, setters ...
}
{
"_id": "AccountOwner:id_:owner/_1_",
"_rev": "3-07eb9959eac966afedd0547aa74a59a7",
"$type": "entity",
"$table": "AccountOwner",
"id": "owner_1",
"SSN": "0123456",
}
{
"_id": "BankAccount:id_:account/_1_",
"_rev": "2-87252fffa4ab443485f55504215fbed3",
"$type": "entity",
"$table": "BankAccount",
"id": "account_1",
"accountNumber": "X2345000",
}
{
"_id": "BankAccount:id_:account/_2_",
"_rev": "2-15bdfeda927dd10fa10aa19ceee4ea34",
"$type": "entity",
"$table": "BankAccount",
"id": "account_2",
"accountNumber": "ZZZ-009",
}
{
"_id": "AccountOwner_BankAccount:bankAccounts/_id_:account/_1_",
"_rev": "1-34ecb6bcadae6e51112de0cf50387521",
"$type": "association",
"rows": [
"owner_1"
]
}
{
"_id": "AccountOwner_BankAccount:bankAccounts/_id_:account/_2_",
"_rev": "1-34ecb6bcadae6e51112de0cf50387521",
"$type": "association",
"rows": [
"owner_1"
]
}
{
"_id": "AccountOwner_BankAccount:owners/_id_:owner/_1_",
"_rev": "2-d2cc7816eae5498a0829a3cdae0b208e",
"$type": "association",
"rows": [
"account_1",
"account_2"
]
}
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.