Corrections to the Criteria chapter and add a multi-from example

This commit is contained in:
Vlad Mihalcea 2016-02-09 10:08:40 +02:00
parent 1e470f9e4d
commit 500d74c35d
3 changed files with 61 additions and 22 deletions

View File

@ -64,12 +64,12 @@ The example uses `createQuery()` passing in the `Person` class reference as the
[NOTE]
====
The call to the `CriteriaQuery#select` method in this example is unnecessary because _personRoot_ will be the implied selection since we have only a single query root.
The call to the `CriteriaQuery#select` method in this example is unnecessary because _root_ will be the implied selection since we have only a single query root.
It was done here only for completeness of an example.
The `Person_.eyeColor` reference is an example of the static form of JPA Metamodel reference.
The `Person_.name` reference is an example of the static form of JPA Metamodel reference.
We will use that form exclusively in this chapter.
See the documentation for the Hibernate JPA Metamodel Generator for additional details on the JPA static Metamodel.
See the documentation for the https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html[Hibernate JPA Metamodel Generator] for additional details on the JPA static Metamodel.
====
[[criteria-typedquery-expression]]
@ -87,7 +87,7 @@ include::{sourcedir}/CriteriaTest.java[tags=criteria-typedquery-expression-examp
----
====
In this example, the query is typed as `java.lang.Integer` because that is the anticipated type of the results (the type of the `Person#age` attribute is `java.lang.Integer`).
In this example, the query is typed as `java.lang.String` because that is the anticipated type of the results (the type of the `Person#nickName` attribute is `java.lang.String`).
Because a query might contain multiple references to the `Person` entity, attribute references always need to be qualified.
This is accomplished by the `Root#get` method call.
@ -130,7 +130,7 @@ but in this case it says to select and return an __Object[]__.
=== Selecting a wrapper
Another alternative to <<criteria-typedquery-multiselect>> is to instead select an object that will "wrap" the multiple values.
Going back to the example query there, rather than returning an array of _[Person#id, Person#age]_, instead declare a class that holds these values and use that as a return object.
Going back to the example query there, rather than returning an array of _[Person#id, Person#nickName]_, instead declare a class that holds these values and use that as a return object.
[[criteria-typedquery-wrapper-example]]
.Selecting a wrapper
@ -175,7 +175,7 @@ The difference here is that the type of the `javax.persistence.criteria.Criteria
The javax.persistence.Tuple contract provides three forms of access to the underlying elements:
typed::
The <<criteria-tuple-example>> example illustrates this form of access in the `tuple.get( idPath )` and `tuple.get( agePath )` calls.
The <<criteria-tuple-example>> example illustrates this form of access in the `tuple.get( idPath )` and `tuple.get( nickNamePath )` calls.
This allows typed access to the underlying tuple values based on the `javax.persistence.TupleElement` expressions used to build the criteria.
positional::
Allows access to the underlying tuple values based on the position.
@ -229,7 +229,16 @@ include::{sourcedir}/CriteriaTest.java[tags=criteria-from-root-example]
====
Criteria queries may define multiple roots, the effect of which is to create a cartesian product between the newly added root and the others.
Here is an example matching all single men and all single women:
Here is an example of cross joining `Person` and `Partner` entities;
[[criteria-from-multiple-root-example]]
.Adding a root example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CriteriaTest.java[tags=criteria-from-multiple-root-example]
----
====
[[criteria-from-join]]
=== Joins
@ -264,7 +273,7 @@ include::{sourcedir}/CriteriaTest.java[tags=criteria-from-fetch-example]
[NOTE]
====
Technically speaking, embedded attributes are always fetched with their owner.
However in order to define the fetching of _Phone#addresses_ we needed a `javax.persistence.criteria.Fetch`, because element collections are `LAZY` by default.
However in order to define the fetching of _Phone#addresses_ we needed a `javax.persistence.criteria.Fetch` because element collections are `LAZY` by default.
====
[[criteria-path]]

View File

@ -1,14 +0,0 @@
CriteriaQuery query = builder.createQuery();
Root<Person> men = query.from( Person.class );
Root<Person> women = query.from( Person.class );
Predicate menRestriction = builder.and(
builder.equal( men.get( Person_.gender ), Gender.MALE ),
builder.equal( men.get( Person_.relationshipStatus ), RelationshipStatus.SINGLE )
);
Predicate womenRestriction = builder.and(
builder.equal( women.get( Person_.gender ), Gender.FEMALE ),
builder.equal( women.get( Person_.relationshipStatus ), RelationshipStatus.SINGLE )
);
query.where( builder.and( menRestriction, womenRestriction ) );

View File

@ -19,12 +19,15 @@ import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.userguide.model.AddressType;
import org.hibernate.userguide.model.Call;
import org.hibernate.userguide.model.CreditCardPayment;
import org.hibernate.userguide.model.Partner;
import org.hibernate.userguide.model.Partner_;
import org.hibernate.userguide.model.Person;
import org.hibernate.userguide.model.Person_;
import org.hibernate.userguide.model.Phone;
@ -47,6 +50,7 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Partner.class,
Phone.class,
Call.class,
CreditCardPayment.class,
@ -114,6 +118,9 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
entityManager.persist( creditCardPayment );
entityManager.persist( wireTransferPayment );
Partner partner = new Partner( "John Doe" );
entityManager.persist( partner );
});
}
@ -238,6 +245,12 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
List<Tuple> tuples = entityManager.createQuery( criteria ).getResultList();
for ( Tuple tuple : tuples ) {
Long id = tuple.get( idPath );
String nickName = tuple.get( nickNamePath );
}
//or using indices
for ( Tuple tuple : tuples ) {
Long id = (Long) tuple.get( 0 );
String nickName = (String) tuple.get( 1 );
@ -260,6 +273,37 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
});
}
@Test
public void test_criteria_from_multiple_root_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
String address = "Earth";
String prefix = "J%";
//tag::criteria-from-multiple-root-example[]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createQuery( Tuple.class );
Root<Person> personRoot = criteria.from( Person.class );
Root<Partner> partnerRoot = criteria.from( Partner.class );
criteria.multiselect( personRoot, partnerRoot );
Predicate personRestriction = builder.and(
builder.equal( personRoot.get( Person_.address ), address ),
builder.isNotEmpty( personRoot.get( Person_.phones ) )
);
Predicate partnerRestriction = builder.and(
builder.like( partnerRoot.get( Partner_.name ), prefix ),
builder.equal( partnerRoot.get( Partner_.version ), 0 )
);
criteria.where( builder.and( personRestriction, partnerRestriction ) );
List<Tuple> tuples = entityManager.createQuery( criteria ).getResultList();
//end::criteria-from-multiple-root-example[]
assertEquals(2, tuples.size());
});
}
@Test
public void test_criteria_from_join_example() {