Skip to content

Commit

Permalink
HHH-11886 - Elaborate Envers documentation and switch to actual sourc…
Browse files Browse the repository at this point in the history
…e code examples

Move code snippets to actual test cases for entity revision queries
  • Loading branch information
vladmihalcea committed Aug 31, 2017
1 parent 23752fe commit e7c239d
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -787,13 +787,14 @@ include::{extrasdir}/entities-filtering-and-pagination.sql[]

The entry point for this type of queries is:

[source,java]
[[revisions-of-entity-query-example]]
[source, JAVA, indent=0]
----
AuditQuery query = getAuditReader().createQuery()
.forRevisionsOfEntity( MyEntity.class, false, true );
include::{sourcedir}/QueryAuditTest.java[tags=revisions-of-entity-query-example]
----

You can add constraints to this query in the same way as to the previous one.

There are some additional possibilities:

. using `AuditEntity.revisionNumber()` you can specify constraints, projections and order on the revision number, in which the audited entity was modified
Expand All @@ -804,51 +805,53 @@ There are some additional possibilities:
. `AuditEntity.revisionType()` gives you access as above to the type of the revision (`ADD`, `MOD`, `DEL`).

Using these methods, you can order the query results by revision number, set projection or constraint the revision number to be greater or less than a specified value, etc.
For example, the following query will select the smallest revision number, at which entity of class `MyEntity` with id `entityId` has changed, after revision number 42:
For example, the following query will select the smallest revision number, at which entity of class `MyEntity` with id `entityId` has changed, after revision number 2:

[source,java]
[[revisions-of-entity-query-by-revision-number-example]]
[source, JAVA, indent=0]
----
Number revision = (Number) getAuditReader().createQuery()
.forRevisionsOfEntity( MyEntity.class, false, true )
.setProjection( AuditEntity.revisionNumber().min() )
.add( AuditEntity.id().eq( entityId ) )
.add( AuditEntity.revisionNumber().gt( 42 ) )
.getSingleResult();
include::{sourcedir}/QueryAuditTest.java[tags=revisions-of-entity-query-by-revision-number-example]
----

The second additional feature you can use in queries for revisions is the ability to _maximize_/_minimize_ a property.
For example, if you want to select the smallest possibler revision at which the value of the `actualDate` for a given entity was larger then a given value:

[source,java]
For example, if you want to select the smallest possibler revision at which the value of the `createdOn`
attribute was larger then a given value,
you can run the following query:

[[revisions-of-entity-query-minimize-example]]
[source, JAVA, indent=0]
----
Number revision = (Number) getAuditReader().createQuery()
.forRevisionsOfEntity( MyEntity.class, false, true) // We are only interested in the first revision
.setProjection( AuditEntity.revisionNumber().min() )
.add( AuditEntity.property( "actualDate" ).minimize()
.add( AuditEntity.property( "actualDate" ).ge( givenDate ) )
.add( AuditEntity.id().eq( givenEntityId ) )) .getSingleResult();
include::{sourcedir}/QueryAuditTest.java[tags=revisions-of-entity-query-minimize-example]
----

The `minimize()` and `maximize()` methods return a criteria, to which you can add constraints, which must be met by the entities with the _maximized_/_minimized_ properties.
The `minimize()` and `maximize()` methods return a criteria, to which you can add constraints,
which must be met by the entities with the _maximized_/_minimized_ properties.

[NOTE]
====
`AggregatedAuditExpression#computeAggregationInInstanceContext()` enables the possibility to compute aggregated expression in the context of each entity instance separately.
`AggregatedAuditExpression#computeAggregationInInstanceContext()` enables the possibility to compute
aggregated expression in the context of each entity instance separately.
It turns out useful when querying for latest revisions of all entities of a particular type.
====

You probably also noticed that there are two boolean parameters, passed when creating the query.

`selectEntitiesOnly`:: the first parameter is only valid when you don't set an explicit projection.
If true, the result of the query will be a list of entities (which changed at revisions satisfying the specified constraints).
If false, the result will be a list of three element arrays:
`selectEntitiesOnly`:: The first parameter is only valid when you don't set an explicit projection.
+
If true, the result of the query will be a list of entities (which changed at revisions satisfying the specified constraints).
+
If false, the result will be a list of three element arrays:

* the first element will be the changed entity instance.
* the second will be an entity containing revision data (if no custom entity is used, this will be an instance of `DefaultRevisionEntity`).
* the third will be the type of the revision (one of the values of the `RevisionType` enumeration: `ADD`, `MOD`, `DEL`).
* the first element will be the changed entity instance.
* the second will be an entity containing revision data (if no custom entity is used, this will be an instance of `DefaultRevisionEntity`).
* the third will be the type of the revision (one of the values of the `RevisionType` enumeration: `ADD`, `MOD`, `DEL`).

`selectDeletedEntities`:: the second parameter specifies if revisions, in which the entity was deleted should be included in the results.
If yes, such entities will have the revision type `DEL` and all fields, except the id, `null`.
`selectDeletedEntities`:: The second parameter specifies if revisions,
in which the entity was deleted should be included in the results.
+
If yes, such entities will have the revision type `DEL` and all attributes, except the `id`, will be set to `null`.

[[envers-tracking-properties-changes-queries]]
=== Querying for revisions of entity that modified given property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,23 @@ public void test() {

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev1-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();

assertEquals("Doe", customer.getLastName());
//end::envers-audited-rev1-example[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev2-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 1 ) )
.getSingleResult();
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 1 ) )
.getSingleResult();

assertEquals("Doe Jr.", customer.getLastName());
//end::envers-audited-rev2-example[]
Expand All @@ -100,10 +102,11 @@ public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev3-example[]
try {
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 2 ) )
.getSingleResult();
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 2 ) )
.getSingleResult();

fail("The Customer was deleted at this revision: " + revisions.get( 2 ));
}
Expand All @@ -114,14 +117,15 @@ public void test() {

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev4-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
.createQuery()
.forEntitiesAtRevision(
Customer.class,
Customer.class.getName(),
revisions.get( 2 ),
true )
.getSingleResult();
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision(
Customer.class,
Customer.class.getName(),
revisions.get( 2 ),
true )
.getSingleResult();

assertEquals( Long.valueOf( 1L ), customer.getId() );
assertNull( customer.getFirstName() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ public void testLifecycle() {
AvailableSettings.HBM2DDL_AUTO,
"update"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
).build().unwrap( SessionFactoryImplementor.class );
entityManagerFactory = Bootstrap
.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings )
.build()
.unwrap( SessionFactoryImplementor.class );

final EntityManagerFactory emf = entityManagerFactory;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ public void testLifecycle() {
"update"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
).build().unwrap( SessionFactoryImplementor.class );

final EntityManagerFactory emf = entityManagerFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*/
package org.hibernate.userguide.envers;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
Expand All @@ -24,6 +27,7 @@
import org.hibernate.envers.Audited;
import org.hibernate.envers.configuration.EnversSettings;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.query.AuditQuery;
import org.hibernate.envers.strategy.ValidityAuditStrategy;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;

Expand Down Expand Up @@ -94,22 +98,24 @@ public void test() {

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-at-revision-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();

assertEquals("Doe", customer.getLastName());
//end::entities-at-revision-example[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-filtering-example[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.property( "firstName" ).eq( "John" ) )
.getResultList();
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.property( "firstName" ).eq( "John" ) )
.getResultList();

assertEquals(2, customers.size());
assertEquals( "Doe", customers.get( 0 ).getLastName() );
Expand All @@ -121,54 +127,108 @@ public void test() {
//tag::entities-filtering-by-entity-example[]
Address address = entityManager.getReference( Address.class, 1L );

List<Customer> customers = AuditReaderFactory.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.property( "address" ).eq( address ) )
.getResultList();
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.property( "address" ).eq( address ) )
.getResultList();

assertEquals(2, customers.size());
//end::entities-filtering-by-entity-example[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-filtering-by-entity-identifier-example[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.relatedId( "address" ).eq( 1L ) )
.getResultList();
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.relatedId( "address" ).eq( 1L ) )
.getResultList();

assertEquals(2, customers.size());
//end::entities-filtering-by-entity-identifier-example[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-in-clause-filtering-by-entity-identifier-example[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.relatedId( "address" ).in( new Object[] { 1L, 2L } ) )
.getResultList();
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.relatedId( "address" ).in( new Object[] { 1L, 2L } ) )
.getResultList();

assertEquals(2, customers.size());
//end::entities-in-clause-filtering-by-entity-identifier-example[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-filtering-and-pagination[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.addOrder( AuditEntity.property( "lastName" ).desc() )
.add( AuditEntity.relatedId( "address" ).eq( 1L ) )
.setFirstResult( 1 )
.setMaxResults( 2 )
.getResultList();
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.addOrder( AuditEntity.property( "lastName" ).desc() )
.add( AuditEntity.relatedId( "address" ).eq( 1L ) )
.setFirstResult( 1 )
.setMaxResults( 2 )
.getResultList();

assertEquals(1, customers.size());
//end::entities-filtering-and-pagination[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::revisions-of-entity-query-example[]
AuditQuery query = AuditReaderFactory.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, false, true );
//end::revisions-of-entity-query-example[]
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::revisions-of-entity-query-by-revision-number-example[]
Number revision = (Number) AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, false, true )
.addProjection( AuditEntity.revisionNumber().min() )
.add( AuditEntity.id().eq( 1L ) )
.add( AuditEntity.revisionNumber().gt( 2 ) )
.getSingleResult();
//end::revisions-of-entity-query-by-revision-number-example[]

assertEquals( 3, revision );
} );

doInJPA( this::entityManagerFactory, entityManager -> {
//tag::revisions-of-entity-query-minimize-example[]
Number revision = (Number) AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, false, true )
.addProjection( AuditEntity.revisionNumber().min() )
.add( AuditEntity.id().eq( 1L ) )
.add(
AuditEntity.property( "createdOn" )
.minimize()
.add( AuditEntity.property( "createdOn" )
.ge(
Timestamp.from(
LocalDateTime.now()
.minusDays( 1 )
.toInstant( ZoneOffset.UTC )
)
)
)
)
.getSingleResult();
//end::revisions-of-entity-query-minimize-example[]

assertEquals( 1, revision );
} );
}

@Audited
Expand Down
Loading

0 comments on commit e7c239d

Please sign in to comment.