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.
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:
[[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.
For example, to find all `Customer` entities
where the country entity belonging to the address attribute is `România`:
For example, to find all `Customer` entities at a given revision
with the country attribute of the address property being `România`:
[[envers-querying-entity-relation-nested-join-restriction]]
.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
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:
Constraints may also be added to the properties of nested joined relations, such as testing for `null`.
[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()
.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() );
include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-join-multiple-restrictions]
----
[source, SQL, indent=0]
----
include::{extrasdir}/envers-querying-entity-relation-join-multiple-restrictions.sql[]
----
====
[NOTE]
====
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
`Car` entities where the owner's age is `20` or that the owner lives at an address where the street number equals `1234`:
Disjunction criterion may also be applied to relation join queries.
[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()
.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() );
include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-nested-join-multiple-restrictions]
----
Lastly, this example illustrates how related entity properties can be compared as a constraint. This query shows how to
find the `Car` entities where the owner's `age` equals the `streetNumber` of where the owner lives:
[source, SQL, indent=0]
----
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()
.forEntitiesAtRevision( Car.class, 1 )
.traverseRelation( "owner", JoinType.INNER, "p" )
.traverseRelation( "address", JoinType.INNER, "a" )
.up()
.up()
.add( AuditEntity.property( "p", "age" ).eqProperty( "a", "streetNumber" ) );
include::{sourcedir}/QueryAuditAdressCountryTest.java[tags=envers-querying-entity-relation-nested-join-multiple-restrictions-combined-entities]
----
====
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]]
=== 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
@GeneratedValue
private Long id;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
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.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.test.legacy.Custom;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Vlad Mihalcea
@ -82,35 +84,104 @@ public class QueryAuditAdressCountryTest extends BaseEntityManagerFunctionalTest
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 -> {
//tag::envers-querying-entity-relation-nested-join-restriction[]
Customer customer = (Customer) AuditReaderFactory
List<Customer> customers = AuditReaderFactory
.get( entityManager )
.createQuery()
.forEntitiesAtRevision( Customer.class, 1 )
.traverseRelation( "address", JoinType.INNER )
.traverseRelation( "country", JoinType.INNER )
.add( AuditEntity.property( "name" ).eq( "România" ) )
.getSingleResult();
.getResultList();
assertEquals( 1, customers.size() );
//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

View File

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