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,10 +77,11 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
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[]
@ -88,10 +89,11 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
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[]
@ -100,10 +102,11 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
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 ));
}
@ -114,14 +117,15 @@ public class DefaultAuditTest extends BaseEntityManagerFunctionalTestCase {
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() );

View File

@ -69,10 +69,12 @@ public class EntityTypeChangeAuditDefaultTrackingTest extends BaseEntityManagerF
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;

View File

@ -76,8 +76,8 @@ public class EntityTypeChangeAuditTest extends BaseEntityManagerFunctionalTestCa
"update"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
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,10 +98,11 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
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[]
@ -105,11 +110,12 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
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() );
@ -121,11 +127,12 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
//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[]
@ -133,11 +140,12 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
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[]
@ -145,11 +153,12 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
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[]
@ -157,18 +166,69 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
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

View File

@ -82,29 +82,32 @@ public class ValidityStrategyAuditTest extends BaseEntityManagerFunctionalTestCa
} );
doInJPA( this::entityManagerFactory, entityManager -> {
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());
} );
doInJPA( this::entityManagerFactory, entityManager -> {
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());
} );
doInJPA( this::entityManagerFactory, entityManager -> {
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 ));
}
@ -113,14 +116,15 @@ public class ValidityStrategyAuditTest extends BaseEntityManagerFunctionalTestCa
} );
doInJPA( this::entityManagerFactory, entityManager -> {
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() );