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

Migrate code snippets to test cases for Join relations queries with multiple predicates
This commit is contained in:
Vlad Mihalcea 2017-09-07 11:53:56 +03:00
parent 742d8be69c
commit ab98bcbe9b
7 changed files with 327 additions and 65 deletions

View File

@ -997,7 +997,7 @@ include::{sourcedir}/QueryAuditTest.java[tags=envers-querying-entity-relation-le
Like any other query, constraints may be added to restrict the results. Like any other query, constraints may be added to restrict the results.
For example, to find all `Customers` entities whose addresses are in `România`, For example, to find a `Customers` entities at a given revision whose addresses are in `România`,
you can use the following query: you can use the following query:
[[envers-querying-entity-relation-join-restriction]] [[envers-querying-entity-relation-join-restriction]]
@ -1016,8 +1016,8 @@ include::{extrasdir}/envers-querying-entity-relation-join-restriction.sql[]
It is also possible to traverse beyond the first relation in an entity graph. It is also possible to traverse beyond the first relation in an entity graph.
For example, to find all `Customer` entities For example, to find all `Customer` entities at a given revision
where the country entity belonging to the address attribute is `România`: with the country attribute of the address property being `România`:
[[envers-querying-entity-relation-nested-join-restriction]] [[envers-querying-entity-relation-nested-join-restriction]]
.Filtering a nested join relation with a WHERE clause predicate .Filtering a nested join relation with a WHERE clause predicate
@ -1033,63 +1033,78 @@ include::{extrasdir}/envers-querying-entity-relation-nested-join-restriction.sql
---- ----
==== ====
Complex constraints may also be added that are applicable to properties of nested relations or the base query entity or Constraints may also be added to the properties of nested joined relations, such as testing for `null`.
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] For example, the following query illustrates how to find all `Customer` entities at a given revision
having the `address` in `Cluj-Napoca` or the `address` does _not_ have any country entity reference:
[[envers-querying-entity-relation-join-multiple-restrictions]]
.Filtering a join relation using multiple predicates
====
[source, JAVA, indent=0]
---- ----
AuditQuery query = getAuditReader().createQuery() include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-join-multiple-restrictions]
.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() );
---- ----
[source, SQL, indent=0]
----
include::{extrasdir}/envers-querying-entity-relation-join-multiple-restrictions.sql[]
----
====
[NOTE] [NOTE]
==== ====
Queries can use the `up` method to navigate back up the entity graph. 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 Disjunction criterion may also be applied to relation join queries.
`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] For example, the following query will find all `Customer` entities at a given revision
where the country name is `România` or that the `Customer` lives in `Cluj-Napoca`:
[[envers-querying-entity-relation-nested-join-multiple-restrictions]]
.Filtering a nested join relation using multiple predicates
====
[source, JAVA, indent=0]
---- ----
AuditQuery query = getAuditReader().createQuery() include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-nested-join-multiple-restrictions]
.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 [source, SQL, indent=0]
find the `Car` entities where the owner's `age` equals the `streetNumber` of where the owner lives: ----
include::{extrasdir}/envers-querying-entity-relation-nested-join-multiple-restrictions.sql[]
----
====
[source,java] Lastly, this example illustrates how related entity properties can be compared in a single constraint.
Assuming, the `Customer` and the `Address` were previously changed as follows:
[[envers-querying-entity-relation-nested-join-multiple-restrictions-combined-entities]]
.Changing the `Address` to match the `Country` name
====
[source, JAVA, indent=0]
---- ----
AuditQuery query = getAuditReader().createQuery() include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-nested-join-multiple-restrictions-combined-entities]
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER, "p" )
.traverseRelation( "address", JoinType.INNER, "a" )
.up()
.up()
.add( AuditEntity.property( "p", "age" ).eqProperty( "a", "streetNumber" ) );
---- ----
====
The following query shows how to find the `Customer` entities
where the `city` property of the `address` attribute equals the `name` of the associated `country` attribute.
[[envers-querying-entity-relation-nested-join-multiple-restrictions-combined]]
.Filtering a nested join relation using multiple predicates
====
[source, JAVA, indent=0]
----
include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-nested-join-multiple-restrictions-combined]
----
[source, SQL, indent=0]
----
include::{extrasdir}/envers-querying-entity-relation-nested-join-multiple-restrictions-combined.sql[]
----
====
[[envers-conditional-auditing]] [[envers-conditional-auditing]]
=== Conditional auditing === Conditional auditing

View File

@ -0,0 +1,48 @@
select
c.id as id1_5_,
c.REV as REV2_5_,
c.REVTYPE as REVTYPE3_5_,
c.REVEND as REVEND4_5_,
c.created_on as created_5_5_,
c.firstName as firstNam6_5_,
c.lastName as lastName7_5_,
c.address_id as address_8_5_
from
Customer_AUD c
left outer join
Address_AUD a
on (
c.address_id=a.id
or (
c.address_id is null
)
and (
a.id is null
)
)
where
c.REV<=?
and c.REVTYPE<>?
and (
c.REVEND>?
or c.REVEND is null
)
and (
a.REV is null
or a.REV<=?
and (
a.REVEND>?
or a.REVEND is null
)
)
and (
a.city=?
or a.country_id is null
)
-- binding parameter [1] as [INTEGER] - [1]
-- binding parameter [2] as [INTEGER] - [2]
-- binding parameter [3] as [INTEGER] - [1]
-- binding parameter [4] as [INTEGER] - [1]
-- binding parameter [5] as [INTEGER] - [1]
-- binding parameter [6] as [VARCHAR] - [Cluj-Napoca]

View File

@ -0,0 +1,59 @@
select
cu.id as id1_5_,
cu.REV as REV2_5_,
cu.REVTYPE as REVTYPE3_5_,
cu.REVEND as REVEND4_5_,
cu.created_on as created_5_5_,
cu.firstName as firstNam6_5_,
cu.lastName as lastName7_5_,
cu.address_id as address_8_5_
from
Customer_AUD cu
inner join
Address_AUD a
on (
cu.address_id=a.id
or (
cu.address_id is null
)
and (
a.id is null
)
)
inner join
Country_AUD cr
on (
a.country_id=cr.id
or (
a.country_id is null
)
and (
cr.id is null
)
)
where
cu.REV<=?
and cu.REVTYPE<>?
and a.city=cr.name
and (
cu.REVEND>?
or cu.REVEND is null
)
and a.REV<=?
and (
a.REVEND>?
or a.REVEND is null
)
and cr.REV<=?
and (
cr.REVEND>?
or cr.REVEND is null
)
-- binding parameter [1] as [INTEGER] - [2]
-- binding parameter [2] as [INTEGER] - [2]
-- binding parameter [3] as [INTEGER] - [2]
-- binding parameter [4] as [INTEGER] - [2]
-- binding parameter [5] as [INTEGER] - [2]
-- binding parameter [6] as [INTEGER] - [2]
-- binding parameter [7] as [INTEGER] - [2]

View File

@ -0,0 +1,66 @@
select
cu.id as id1_5_,
cu.REV as REV2_5_,
cu.REVTYPE as REVTYPE3_5_,
cu.REVEND as REVEND4_5_,
cu.created_on as created_5_5_,
cu.firstName as firstNam6_5_,
cu.lastName as lastName7_5_,
cu.address_id as address_8_5_
from
Customer_AUD cu
inner join
Address_AUD a
on (
cu.address_id=a.id
or (
cu.address_id is null
)
and (
a.id is null
)
)
inner join
Country_AUD co
on (
a.country_id=co.id
or (
a.country_id is null
)
and (
co.id is null
)
)
where
cu.REV<=?
and cu.REVTYPE<>?
and (
cu.REVEND>?
or cu.REVEND is null
)
and (
a.city=?
or co.name=?
)
and a.REV<=?
and (
a.REVEND>?
or a.REVEND is null
)
and co.REV<=?
and (
co.REVEND>?
or co.REVEND is null
)
order by
cu.created_on asc
-- binding parameter [1] as [INTEGER] - [1]
-- binding parameter [2] as [INTEGER] - [2]
-- binding parameter [3] as [INTEGER] - [1]
-- binding parameter [4] as [VARCHAR] - [Cluj-Napoca]
-- binding parameter [5] as [VARCHAR] - [România]
-- binding parameter [6] as [INTEGER] - [1]
-- binding parameter [7] as [INTEGER] - [1]
-- binding parameter [8] as [INTEGER] - [1]
-- binding parameter [9] as [INTEGER] - [1]

View File

@ -98,6 +98,7 @@ public class ManyToManyUnidirectionalTest extends BaseEntityManagerFunctionalTes
@Id @Id
@GeneratedValue @GeneratedValue
private Long id; private Long id;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Address> addresses = new ArrayList<>(); private List<Address> addresses = new ArrayList<>();

View File

@ -30,10 +30,12 @@ import org.hibernate.envers.query.AuditQuery;
import org.hibernate.envers.strategy.ValidityAuditStrategy; import org.hibernate.envers.strategy.ValidityAuditStrategy;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.test.legacy.Custom;
import org.junit.Test; import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/** /**
* @author Vlad Mihalcea * @author Vlad Mihalcea
@ -82,35 +84,104 @@ public class QueryAuditAdressCountryTest extends BaseEntityManagerFunctionalTest
entityManager.persist( customer ); entityManager.persist( customer );
} ); } );
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = entityManager.find( Customer.class, 1L );
customer.setLastName( "Doe Jr." );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = entityManager.getReference( Customer.class, 1L );
entityManager.remove( customer );
} );
List<Number> revisions = doInJPA( this::entityManagerFactory, entityManager -> {
return AuditReaderFactory.get( entityManager ).getRevisions(
Customer.class,
1L
);
} );
doInJPA( this::entityManagerFactory, entityManager -> { doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-querying-entity-relation-nested-join-restriction[] //tag::envers-querying-entity-relation-nested-join-restriction[]
Customer customer = (Customer) AuditReaderFactory List<Customer> customers = AuditReaderFactory
.get( entityManager ) .get( entityManager )
.createQuery() .createQuery()
.forEntitiesAtRevision( Customer.class, 1 ) .forEntitiesAtRevision( Customer.class, 1 )
.traverseRelation( "address", JoinType.INNER ) .traverseRelation( "address", JoinType.INNER )
.traverseRelation( "country", JoinType.INNER ) .traverseRelation( "country", JoinType.INNER )
.add( AuditEntity.property( "name" ).eq( "România" ) ) .add( AuditEntity.property( "name" ).eq( "România" ) )
.getSingleResult(); .getResultList();
assertEquals( 1, customers.size() );
//end::envers-querying-entity-relation-nested-join-restriction[] //end::envers-querying-entity-relation-nested-join-restriction[]
} ); } );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-querying-entity-relation-join-multiple-restrictions[]
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, 1 )
.traverseRelation( "address", JoinType.LEFT, "a" )
.add(
AuditEntity.or(
AuditEntity.property( "a", "city" ).eq( "Cluj-Napoca" ),
AuditEntity.relatedId( "country" ).eq( null )
)
)
.getResultList();
//end::envers-querying-entity-relation-join-multiple-restrictions[]
assertEquals( 1, customers.size() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-querying-entity-relation-nested-join-multiple-restrictions[]
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, 1 )
.traverseRelation( "address", JoinType.INNER, "a" )
.traverseRelation( "country", JoinType.INNER, "cn" )
.up()
.up()
.add(
AuditEntity.disjunction()
.add( AuditEntity.property( "a", "city" ).eq( "Cluj-Napoca" ) )
.add( AuditEntity.property( "cn", "name" ).eq( "România" ) )
)
.addOrder( AuditEntity.property( "createdOn" ).asc() )
.getResultList();
//end::envers-querying-entity-relation-nested-join-multiple-restrictions[]
assertEquals( 1, customers.size() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-querying-entity-relation-nested-join-multiple-restrictions-combined-entities[]
Customer customer = entityManager.createQuery(
"select c " +
"from Customer c " +
"join fetch c.address a " +
"join fetch a.country " +
"where c.id = :id", Customer.class )
.setParameter( "id", 1L )
.getSingleResult();
customer.setLastName( "Doe Sr." );
customer.getAddress().setCity(
customer.getAddress().getCountry().getName()
);
//end::envers-querying-entity-relation-nested-join-multiple-restrictions-combined-entities[]
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-querying-entity-relation-nested-join-multiple-restrictions-combined[]
List<Number> revisions = AuditReaderFactory.get( entityManager ).getRevisions(
Customer.class,
1L
);
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, revisions.get( revisions.size() - 1 ) )
.traverseRelation( "address", JoinType.INNER, "a" )
.traverseRelation( "country", JoinType.INNER, "cn" )
.up()
.up()
.add( AuditEntity.property( "a", "city" ).eqProperty( "cn", "name" ) )
.getResultList();
//end::envers-querying-entity-relation-nested-join-multiple-restrictions-combined[]
assertEquals( 1, customers.size() );
} );
} }
@Audited @Audited

View File

@ -250,14 +250,16 @@ public class QueryAuditTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> { doInJPA( this::entityManagerFactory, entityManager -> {
//tag::envers-querying-entity-relation-join-restriction[] //tag::envers-querying-entity-relation-join-restriction[]
Customer customer = (Customer) AuditReaderFactory List<Customer> customers = AuditReaderFactory
.get( entityManager ) .get( entityManager )
.createQuery() .createQuery()
.forEntitiesAtRevision( Customer.class, 1 ) .forEntitiesAtRevision( Customer.class, 1 )
.traverseRelation( "address", JoinType.INNER ) .traverseRelation( "address", JoinType.INNER )
.add( AuditEntity.property( "country" ).eq( "România" ) ) .add( AuditEntity.property( "country" ).eq( "România" ) )
.getSingleResult(); .getResultList();
//end::envers-querying-entity-relation-join-restriction[] //end::envers-querying-entity-relation-join-restriction[]
assertEquals( 1, customers.size() );
} ); } );
} }