HHH-11886 - Elaborate Envers documentation and switch to actual source code examples

Move code snippets to actual test cases for entity revision queries
This commit is contained in:
Vlad Mihalcea 2017-08-31 15:44:45 +03:00
parent 23752fe169
commit e7c239d57f
6 changed files with 180 additions and 107 deletions

View File

@ -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
@ -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

View File

@ -77,7 +77,8 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev1-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();
@ -88,7 +89,8 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev2-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 1 ) )
.getSingleResult();
@ -100,7 +102,8 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev3-example[]
try {
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 2 ) )
.getSingleResult();
@ -114,7 +117,8 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-audited-rev4-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision(
Customer.class,

View File

@ -69,10 +69,12 @@ public class EntityTypeChangeAuditDefaultTrackingTest extends BaseEntityManagerF
AvailableSettings.HBM2DDL_AUTO,
"update"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
entityManagerFactory = Bootstrap
.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
).build().unwrap( SessionFactoryImplementor.class );
settings )
.build()
.unwrap( SessionFactoryImplementor.class );
final EntityManagerFactory emf = entityManagerFactory;

View File

@ -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;
@ -24,6 +27,7 @@ import org.hibernate.envers.AuditReaderFactory;
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;
@ -94,7 +98,8 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-at-revision-example[]
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();
@ -105,7 +110,8 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-filtering-example[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.property( "firstName" ).eq( "John" ) )
@ -121,7 +127,8 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
//tag::entities-filtering-by-entity-example[]
Address address = entityManager.getReference( Address.class, 1L );
List<Customer> customers = AuditReaderFactory.get( entityManager )
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.property( "address" ).eq( address ) )
@ -133,7 +140,8 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-filtering-by-entity-identifier-example[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.relatedId( "address" ).eq( 1L ) )
@ -145,7 +153,8 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-in-clause-filtering-by-entity-identifier-example[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.add( AuditEntity.relatedId( "address" ).in( new Object[] { 1L, 2L } ) )
@ -157,7 +166,8 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::entities-filtering-and-pagination[]
List<Customer> customers = AuditReaderFactory.get( entityManager )
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forRevisionsOfEntity( Customer.class, true, true )
.addOrder( AuditEntity.property( "lastName" ).desc() )
@ -169,6 +179,56 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
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

View File

@ -82,7 +82,8 @@ public class ValidityStrategyAuditTest extends BaseEntityManagerFunctionalTestCa
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 0 ) )
.getSingleResult();
@ -91,7 +92,8 @@ public class ValidityStrategyAuditTest extends BaseEntityManagerFunctionalTestCa
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 1 ) )
.getSingleResult();
@ -101,7 +103,8 @@ public class ValidityStrategyAuditTest extends BaseEntityManagerFunctionalTestCa
doInJPA( this::entityManagerFactory, entityManager -> {
try {
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( 2 ) )
.getSingleResult();
@ -113,7 +116,8 @@ public class ValidityStrategyAuditTest extends BaseEntityManagerFunctionalTestCa
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = (Customer) AuditReaderFactory.get( entityManager )
Customer customer = (Customer) AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision(
Customer.class,