Hibernate.orgCommunity Documentation
Redis is a key-value datastore which stores your data in a variety of data structures. Although there is no one-and-only style how to map data between data structures and Redis, two major styles are are most common:
Support for Redis 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 using the JSON dialect are unsupported at the moment 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 JSON dialect supports two modes for storing associations:
The ASSOCIATION_DOCUMENT
mode 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 lettuce library to talk to Redis, so you need the client libraries to use Hibernate OGM Redis.
The following properties are available to configure Redis support in Hibernate OGM:
Redis datastore configuration properties
redis_experimental
org.hibernate.ogm.datastore.redis.RedisJsonDialect
(store entities as JSON documents within Redis keys.
Associations are mapped to Redis lists/sets) and
org.hibernate.ogm.datastore.redis.RedisHashDialect
(store entities as key-value pairs within Redis hashes.
Associations are mapped as JSON documents to Redis lists/sets)The hostname and port of the Redis instance. The optional port is concatenated to the host and separated by a colon. Let’s see a few valid examples:
redis.example.com
redis.example.com:6379
Listing multiple initial hosts for fault tolerance is not supported.
The default value is 127.0.0.1:6379
. If left undefined, the default port is 6379
.
0
.
Note that Redis databases are identified with a number from 0
to 16
.5000
.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. The Redis hash dialect supports only the ASSOCIATION_DOCUMENT
strategy.Defines the way OGM stores the contents of map-typed associations in Redis.
The following two strategies exist (values of the org.hibernate.ogm.datastore.document.options.MapStorageType
enum):
BY_KEY
: map-typed associations with a single key column which is of type String
will be stored as a sub-document,
organized by the given key; Not applicable for other types of key columns, in which case always AS_LIST
will be usedAS_LIST
: map-typed associations will be stored as an array containing a sub-document for each map entry.
All key and value columns will be contained within the array elements
The default value is BY_KEY
.When bootstrapping a session factory or entity manager factory programmatically,
you should use the constants accessible via RedisProperties
when specifying the configuration properties listed above.
Common properties shared between stores are declared on OgmProperties
.
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 Redis backend, you can specify how associations should be stored
using the AssociationStorage
annotation.
A strategy for storing the contents of map-typed associations can be defined using the @MapStorage
annotation.
(refer to Section 15.2, “Storage principles” to learn more about association storage strategies in general).
The following shows an example:
Example 15.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;
//...
}
Example 15.2. Configuring the TTL/expiry using annotations
@Entity
@TTL(value = 7, unit = TimeUnit.DAYS)
public class Zoo {
@OneToMany
private Set<Animal> animals;
@OneToMany
private Set<Person> employees;
//...
}
Redis supports a native TTL/expiry mechanism. Keys can expire at a date or after a certain period. Hibernate OGM allows to specify a TTL value on entities and associations. The TTL is set after persisting the entity using the PEXPIRE command. Every write to Redis will set a new TTL.
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.
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. Hibernate OGM facilitates two styles of data mapping within Redis:
The following describe how entities and associations are mapped to Redis data structures by Hibernate OGM.
The JSON mapping dialect maps entities to JSON documents and stores the JSON data within Redis keys. A document is self-contained and does not support partial updates.
Hibernate OGM doesn’t store null values in Redis, setting a value to null will be the same as removing the field in the corresponding object in the db.
Hibernate OGM supports 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 JSON 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.
Redis has no built-in mechanism for detecting concurrent updates to one and the same document.
The following shows an example of an entity and its persistent representation in Redis.
Example 15.3. Example of an entity and its representation in Redis
@Entity
public class News {
@Id
private String id;
@Version
@Column(name="version")
private int version;
private String title;
private String description;
//getters, setters ...
}
{
"version": 1,
"title": "On the merits of NoSQL",
"description": "This paper discuss why NoSQL will save the world for good"
}
Redis doesn’t have a concept of "tables"; Instead all values are stored in a unique key. Thus Hibernate OGM needs to add two additional attributes:
Example 15.4. Rename field and collection using @Table and @Column
@Entity
@Table(name="Article")
public class News {
@Id
@Column(name="code")
private String id;
@Version
@Column(name="revision")
private int revision;
private String title;
@Column(name="desc")
private String description;
//getters, setters ...
}
{
"revision": 1,
"title": "On the merits of NoSQL",
"desc": "This paper discuss why NoSQL will save the world for good"
}
Hibernate OGM stores elements annotated with @Embedded
or @ElementCollection
as nested documents of the owning entity.
Example 15.5. 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 ...
}
Key: News:939c892d-1129-4aff-abf8-e6c26e59dcb
{
"title": "On the merits of NoSQL",
"paper": {
"name": "NoSQL journal of prophecies",
"owner": "Delphy"
}
}
Example 15.6. @ElementCollection with primitive types
@Entity
public class AccountWithPhone {
@Id
private String id;
@ElementCollection
private List<String> mobileNumbers;
// getters, setters ...
}
AccountWithPhone collection
Key: AccountWithPhone:2
{
"mobileNumbers": [
"+1-222-555-0222",
"+1-202-555-0333"
]
}
Example 15.7. @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 ...
}
Key: GrandMother: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 15.8. @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 ...
}
Key: GrandMother:86ada718-f2a2-4299-b6ac-3d90b1ef2331
{
"grandChildren" : [
{
"birthorder" : 0
"name" : "luke",
},
{
"birthorder" : 1
"name" : "leia",
}
]
}
The Redis Hash mapping dialect maps entities to key-value pairs. It stores the data within Redis hashes. Hashes support partial updates. While the JSON dialect discards not mapped fields in the entity model the hash dialect does not touch fields that are not mapped to the entity model.
Hibernate OGM doesn’t store null values in Redis, setting a value to null will be the same as removing the field in the corresponding object in the db.
Hibernate OGM supports 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=Rg==
Redis Hash stores byte data as base64-encoded string
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 key-value pairs within Redis hashes.
Each entity property will be translated into a hash field and is represented as java.lang.String
.
The Redis hash dialect supports only flat data structures hence nested entities and associations
are represented as JSON documents within the association documents in Redis lists/sets.
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.
Redis has no built-in mechanism for detecting concurrent updates to one and the same document.
The following shows an example of an entity and its persistent representation in Redis.
Example 15.9. Example of an entity and its representation in Redis
@Entity
public class News {
@Id
private String id;
@Version
@Column(name="version")
private int version;
private String title;
private String description;
//getters, setters ...
}
version=1
title=On the merits of NoSQL
description=This paper discuss why NoSQL will save the world for good
Redis doesn’t have a concept of "tables"; Instead all values are stored in a hash as key values. The hash key contains the table name and the primary key of the entity. Thus Hibernate OGM needs to add two additional attributes:
Example 15.10. Rename field and collection using @Table and @Column
@Entity
@Table(name="Article")
public class News {
@Id
@Column(name="code")
private String id;
@Version
@Column(name="revision")
private int revision;
private String title;
@Column(name="desc")
private String description;
//getters, setters ...
}
revision=1
title=On the merits of NoSQL
desc=This paper discuss why NoSQL will save the world for good
Hibernate OGM stores elements annotated with @Embedded
or @ElementCollection
as nested documents of the owning entity.
Example 15.11. 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 ...
}
Key: News:939c892d-1129-4aff-abf8-e6c26e59dcb
paper.name=NoSQL journal of prophecies
paper.owner=Delphy
Example 15.12. @ElementCollection with primitive types
@Entity
public class AccountWithPhone {
@Id
private String id;
@ElementCollection
private List<String> mobileNumbers;
// getters, setters ...
}
AccountWithPhone collection
Key: AccountWithPhone:2
id=2
Key: Associations:AccountWithPhone:2:mobileNumbers
[
"+1-222-555-0222",
"+1-202-555-0333"
]
Example 15.13. @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 ...
}
Key: Associations:GrandMother_grandChildren: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 15.14. @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 ...
}
Key: Associations:GrandMother_grandChildren:86ada718-f2a2-4299-b6ac-3d90b1ef2331:grandChildren
[
"{\"name\":\"Luke\",\"birthorder\":\"0\"}",
"{\"name\":\"Leia\",\"birthorder\":\"1\"}"
]
Hibernate OGM Redis 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 15.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. This strategy is supported only using the JSON dialect.
You can use @JoinColumn
to change the name of the field that stores the foreign key (as an example, see
Example 15.18, “Unidirectional one-to-one with @JoinColumn”).
Example 15.15. Java entity
@Entity
public class AccountOwner {
@Id
private String id;
@ManyToMany
public Set<BankAccount> bankAccounts;
// getters, setters, ...
Example 15.16. JSON representation
Key: AccountOwner:owner0001
{
"bankAccounts" : [
"accountABC",
"accountXYZ"
]
}
Example 15.17. 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 ...
}
Key: Vehicule:V001
{
"brand": "Mercedes"
}
Key: Wheel:W1
{
"diameter" : 0.0,
"vehicule_id" : "V001"
}
Example 15.18. 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 ...
}
Key: Vehicule:V001
{
"brand": "Mercedes"
}
Key: Wheel:W1
{
"diameter" : 0.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 15.19. 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 ...
}
Key: Vehicule:V001
{
"brand": "Mercedes"
}
Wheel:vehicule:V001
{
"diameter" : 0.0,
"vehicule_id" : "V001"
}
Example 15.20. 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 ...
}
Key: Husband:alex
{
"name" : "Alex",
"wife" : "bea"
}
Key: Wife:bea
{
"name" : "Bea",
"husband" : "alex"
}
Example 15.21. 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
Key: Basket:davide_basket
{
"owner" : "Davide",
"products" : [ "Beer", "Pretzel" ]
}
Product collection
Key: Product:Beer
{
"name" : "Beer",
"description" : "Tactical nuclear penguin"
}
Key: Product:Pretzel
{
"name" : "Pretzel",
"description" : "Glutino Pretzel Sticks"
}
Example 15.22. 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
Key: Basket:davide_basket
{
"owner" : "Davide"
}
Product collection
Key: Product:Pretzel
{
"description" : "Glutino Pretzel Sticks"
}
Key: Product:Beer
{
"description" : "Tactical nuclear penguin"
}
Redis List Associations:davide_basket:Basket_Product
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 15.23. 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 ...
}
Key: User:user_001
{
"addresses" : {
"work" : "address_001",
"home" : "address_002"
}
}
Key: Address:address_001
{
"city" : "Rome"
}
Key: Address:address_002
{
"city" : "Paris"
}
If the map value cannot be represented by a single field (e.g. when referencing a type with a composite id or using an embeddable type as map value type), a sub-document containing all the required fields will be stored as value.
If the map key either is not of type String
or it is made up of several columns (composite map key),
the optimized structure shown in the example above cannot be used.
In that case the association will be represented by a list of sub-documents, also containing the map key column(s).
You can use @MapKeyColumn
to rename the field containing the key of the map,
otherwise it will default to "<%COLLECTION_ROLE%>_KEY", e.g. "addresses_KEY".
In case you want to enforce the list-style represention also for maps with a single key column of type String
you can use the option hibernate.ogm.datastore.document.map_storage
to do so.
Example 15.24. 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 ...
}
Key: User:user_001
{
"addresses" : [
{
"addressType" : "work",
"addresses_id" : "address_001"
},
{
"addressType" : "home",
"addresses_id" : "address_002"
}
]
}
Key: Address:address_001
{
"city" : "Rome"
}
Key: Address:address_002
{
"city" : "Paris"
}
Example 15.25. 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 ...
}
Key: JavaUserGroups:summer_camp
{
"name" : "JUG Summer Camp"
}
Key: Member:jerome
{
"name" : "Jerome"
"memberOf_jugId" : "summer_camp"
}
Key: Member:emmanuel
{
"name" : "Emmanuel Bernard"
"memberOf_jugId" : "summer_camp"
}
Example 15.26. 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 ...
}
Key: SalesForce:red_hat
{
"corporation": "Red Hat",
"salesGuys": [ "eric", "simon" ]
}
Key: SalesGuy:eric
{
"name": "Eric"
"salesForce_id": "red_hat",
}
Key: SalesGuy:simon
{
"name": "Simon",
"salesForce_id": "red_hat"
}
Example 15.27. 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 ...
}
Key: ClassRoom:1
{
"students": [
"mario",
"john"
],
"name": "Math"
}
Key: ClassRoom:2
{
"students": [
"kate",
"mario"
],
"name": "English"
}
Key: Student:john
{
"name": "John Doe"
}
Key: Student:kate
{
"name": "Kate Doe"
}
Key: Student:mario
{
"name": "Mario Rossi"
}
Example 15.28. 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 ...
}
Key: AccountOwner:owner_1
{
"SSN": "0123456",
"bankAccounts": [
"account_1",
"account_2"
]
}
Key: BankAccount:account_1
{
"accountNumber": "X2345000",
"owners": [
"owner_1"
]
}
Key: BankAccount:account_2
{
"accountNumber": "ZZZ-009",
"owners": [
"owner_1"
]
}
With this strategy, Hibernate OGM uses separate association data structures to store all navigation information. The association data structure depends on the type of the association:
Each association has 2 parts. The first is the key. The key consists of a prefix, the identifier information of the association owner, and the name of the association table.
→ → , e.g. "Associations:AccountOwner:4f5b48ad-f074-4a64-8cf4-1f9c54a33f76:BankAccount".
The second part is the rows
field which stores (into an embedded collection) all ids
that the current instance is related to.
Example 15.29. Unidirectional relationship
Key: Associations:AccountOwner:4f5b48ad-f074-4a64-8cf4-1f9c54a33f76:BankAccount
[
"7873a2a7-c77c-447c-b000-890f0a4dfa9a"
]
For a bidirectional relationship, another list is created where ids are reversed. Don’t worry, Hibernate OGM takes care of keeping them in sync:
Example 15.30. Bidirectional relationship
Key: Associations:AccountOwner:4f5b48ad-f074-4a64-8cf4-1f9c54a33f76:BankAccount
[
"7873a2a7-c77c-447c-b000-890f0a4dfa9a"
]
Key: Associations:AccountOwner:bankAccounts:7873a2a7-c77c-447c-b000-890f0a4dfa9a
[
"4f5b48ad-f074-4a64-8cf4-1f9c54a33f76"
]
This strategy won’t affect *-to-one associations or embedded collections.
Example 15.31. 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 ...
}
Key: Basket:davide_basket
{
"owner": "Davide"
}
Key: Basket:davide_basket
{
"owner": "Davide"
}
Key: Product:Pretzel
{
"description": "Glutino Pretzel Sticks",
}
Key: Associations:Basket_Product:davide_basket:products
[
"Beer",
"Pretzel"
]
Using the annotation @JoinTable
it is possible to change the value of
the document containing the association.
Example 15.32. 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 ...
}
Key: Basket:davide_basket
{
"owner": "Davide"
}
Key: Basket:davide_basket
{
"owner": "Davide"
}
Key: Product:Pretzel
{
"description": "Glutino Pretzel Sticks",
}
Key: Association:BasketContent:Basket:davide_basket
[
"Beer",
"Pretzel"
]
Example 15.33. 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 ...
}
Key: ClassRoom:1
{
"name": "Math"
}
Key: ClassRoom:2
{
"name": "English"
}
Key: ClassStudent:john
{
"name": "John Doe"
}
Key: ClassStudent:kate
{
"name": "Kate Doe"
}
Key: ClassStudent:mario
{
"name": "Mario Rossi"
}
Key: Association:ClassRoom:Student:ClassRoom:1
[
"mario",
"john"
]
Key: Association:ClassRoom:Student:ClassRoom:2
[
"kate"
]
Example 15.34. 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 ...
}
Key: AccountOwner:owner_1
{
"SSN": "0123456",
}
Key: BankAccount:account_1
{
"accountNumber": "X2345000",
}
Key: BankAccount:account_2
{
"accountNumber": "ZZZ-009",
}
Key: Association:AccountOwner:BankAccount:account_1
[
"owner_1"
]
Key: Association:AccountOwner:BankAccount:bankAccounts:account_2
[
"owner_1"
]
Key: Association:AccountOwner:BankAccount:owners:account_1
[
"account_1",
"account_2"
]
Redis keys are derived from the Entity name and its Id separated by a colon (:
).
String-based Id’s are used directly within the key, non-string keys are encoded to JSON.
You can use any persistable Java type as identifier type, e.g. String
or long
.
Hibernate OGM will convert the @Id
property into a part of the key name
so you can name the entity id like you want.
@Entity
public class News {
@Id
@Column
private long id;
// fields, getters, setters ...
}
Key-Scheme for News
entity with an Id of 42
News:42
@Entity
@Table(name="Article")
public class News {
@Id
@Column(name="code")
private String id;
// fields, getters, setters ...
}
Key-Scheme for News
entity with an Id of breaking-news
Article:breaking-news
Note that you also can work with embedded ids (via @EmbeddedId
),
Composite Id’s are mapped to a JSON object containing keys and values.
Hibernate OGM thus will create a concatenated representation of the embedded id’s properties in this case.
The columns are sorted in alphabetical order to guarantee the same order.
Example 15.35. 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 ...
}
Resulting key:
News:{"newsId.author": "Guillaume, "newsId.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 strategies will operate in the keys starting with Identifiers
containing the last value of the id. The difference
between the two strategies is the name of the key containing the values.
The AUTO
strategy is the same as the SEQUENCE one.
The next value is obtained using Redis' HINCRBY command that guarantees to create atomic updates to the underlying data structure.
Example 15.36. Id generation strategy TABLE using default values
@Entity
public class Video {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
private String name
// getters, setters, ...
}
Key: Video:1
{
"name": "Scream",
"director": "Wes Craven"
}
Key: Identifiers:hibernate_sequences:default
Value: 1
Example 15.37. 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, ...
}
Key: Identifiers:sequences:video
Value: 2
2) SEQUENCE generation strategy
Example 15.38. SEQUENCE id generation strategy using default values
@Entity
public class Song {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String title;
// getters, setters ...
}
Key: Song:2
{
"title": "Ave Maria",
"singer": "Charlotte Church"
}
Key: Identifiers:hibernate_sequences
Value: 2
Example 15.39. 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 ...
}
Key: Song:2
{
"title": "Ave Maria",
"singer": "Charlotte Church"
}
Key: Identifiers:song_sequence
Value: 21
The value stored in Redis for the sequence is not the one the dialect will return as next number in the sequence. Instead, Hibernate OGM will use it as seed to generate the correct value in the sequence.
The Redis dialect does not support transactions for now. 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 Redis 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 can use Hibernate Search to query entities stored by Hibernate OGM.
Using Hibernate OGM with Redis Cluster is a matter of enabling Redis Cluster mode (see Section 15.1, “Configuring Redis”). Data stored with Hibernate OGM Redis is distributed across Redis Cluster nodes according to the Redis key distribution model based on the hash slot of the key of each entity.