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:
parent
23752fe169
commit
e7c239d57f
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
|
|
Loading…
Reference in New Issue