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:
parent
742d8be69c
commit
ab98bcbe9b
|
@ -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
|
||||
|
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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<>();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue