HHH-10762 - Document relation traversal api.

This commit is contained in:
Chris Cranford 2016-05-23 20:12:24 -05:00
parent a59ebb7e53
commit 6be82328b5
1 changed files with 115 additions and 2 deletions

View File

@ -628,6 +628,119 @@ Other queries (also accessible from `org.hibernate.envers.CrossTypeRevisionChang
Note that methods described above can be legally used only when the default mechanism of tracking changed entity names is enabled (see <<envers-tracking-modified-entities-revchanges>>).
[[envers-querying-entity-relation-jobs]]
=== Querying for entities using entity relation joins
Audit queries support the ability to apply constraints, projections, and sort operations based on entity relations. In order
to traverse entity relations through an audit query, you must use the relation traversal API with a join type.
[IMPORTANT]
====
Relation join queries are considered experimental and may change in future releases.
====
[NOTE]
====
Relation joins can only be applied to `*-to-one` mappings and can only be specified using `JoinType.LEFT` or
`JoinType.INNER`.
====
The basis for creating an entity relation join query is as follows:
[source,java]
----
// create an inner join query
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER );
// create a left join query
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.LEFT );
----
Like any other query, constraints may be added to restrict the results. For example, to find all `Car` entities that
have an owner with a name starting with `Joe`, you would use:
[source,java]
----
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER )
.add( AuditEntity.property( "name" ).like( "Joe%" ) );
----
It is also possible to traverse beyond the first relation in an entity graph. For example, to find all `Car` entities
where the owner's address has a street number that equals `1234`:
[source,java]
----
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER )
.traverseRelation( "address", JoinType.INNER )
.add( AuditEntity.property( "streetNumber" ).eq( 1234 ) );
----
Complex constraints may also be added that are applicable to properties of nested relations or the base query entity or
relation state, such as testing for `null`. For example, the following query illustrates how to find all `Car` entities where
the owner's age is `20` or that the car has _no_ owner:
[source,java]
----
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.LEFT, "p" )
.up()
.add(
AuditEntity.or(
AuditEntity.property( "p", "age" ).eq( 20 ),
AuditEntity.relatedId( "owner" ).eq( null )
)
)
.addOrder( AuditEntity.property( "make" ).asc() );
----
[NOTE]
====
Queries can use the `up` method to navigate back up the entity graph.
====
Disjunction criterion may also be applied to relation join queries. For example, the following query will find all
`Car` entities where the owner's age is `20` or that the owner lives at an address where the street number equals `1234`:
[source,java]
----
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER, "p" )
.traverseRelation( "address", JoinType.INNER, "a" )
.up()
.up()
.add(
AuditEntity.disjunction()
.add( AuditEntity.property( "p", "age" ).eq( 20 ) )
.add( AuditEntity.property( "a", "streetNumber" ).eq( 1234 )
)
)
.addOrder( AuditEntity.property( "make" ).asc() );
----
Lastly, this example illustrates how related entity properties can be compared as a constraint. This query shows how to
find the `Car` entities where the owner's `age` equals the `streetNumber` of where the owner lives:
[source,java]
----
AuditQuery query = getAuditReader().createQuery()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER, "p" )
.traverseRelation( "address", JoinType.INNER, "a" )
.up()
.up()
.add( AuditEntity.property( "p", "age" ).eqProperty( "a", "streetNumber" ) );
----
=== Conditional auditing
Envers persists audit data in reaction to various Hibernate events (e.g. `post update`, `post insert`, and so on), using a series of event listeners from the `org.hibernate.envers.event.spi` package.
@ -784,7 +897,7 @@ alter table Person
[[envers-mappingexceptions]]
=== Mapping exceptions
=== What isn't and will not be supported
==== What isn't and will not be supported
Bags are not supported because they can contain non-unique elements.
Persisting, a bag of `String`s violates the relational database principle that each table is a set of tuples.
@ -797,7 +910,7 @@ There are at least two ways out if you need bag semantics:
. use an indexed collection, with the `@javax.persistence.OrderColumn` annotation
. provide a unique id for your elements with the `@CollectionId` annotation.
=== What isn't and _will_ be supported
==== What isn't and _will_ be supported
. Bag style collections with a `@CollectionId` identifier column (see https://hibernate.atlassian.net/browse/HHH-3950[HHH-3950]).