I want to execute a query matching a specific subclass property, so I'm trying to use treat()
.
In this example I want:
all subjects with name starting with 'a',
or all subjects, which are persons, with last name starting with 'a'
private List<Subject> q1()
{
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Subject> q = b.createQuery(Subject.class);
Root<Subject> r = q.from(Subject.class);
q.select(r);
q.distinct(true);
q.where(
b.or(
b.like(r.get(Subject_.name), "a%"),
b.like(b.treat(r, Person.class).get(Person_.lastName), "a%")));
return em.createQuery(q).getResultList();
}
Obviously, Person
extends Subject
, Subject
is abstract, inheritance is SINGLE_TABLE
, and (non influent).Subject
has @DiscriminatorOptions(force = true)
for other reasons
But the generated SQL is this:
select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_
where subject0_.DTYPE='Person' and (subject0_.name like 'a%' or subject0_.lastName like 'a%')
while I'm expecting:
select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_
where subject0_.name like 'a%' or (subject0_.DTYPE='Person' and subject0_.lastName like 'a%')
Is there a way to produce the expected query using criteria builder?
Note that
q.from(Person.class)
q.subquery(Person.class)
are not acceptable.
I'm interested in something which can be declared and used directly in WHERE clause (produced only from CriteriaBuilder and/or the single Root, just like the treat()
clause), if it does exist.
The solution is, with Hibernate and in this specific scenario, very simple:
private List<Subject> q1()
{
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Subject> q = b.createQuery(Subject.class);
Root<Subject> r = q.from(Subject.class);
q.select(r);
q.distinct(true);
q.where(
b.or(
b.like(r.get(Subject_.name), "a%"),
b.and(
b.equal(r.type(), Person.class),
b.like(((Root<Person>) (Root<?>) r).get(Person_.lastName), "a%"))));
return em.createQuery(q).getResultList();
}
Note the double cast, which avoids compilation error, and allows to perform the query clause on the same table. This generates:
select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_
where subject0_.DTYPE in ('Office', 'Team', 'Role', 'Person', ...)
and (subject0_.name like 'a%'
or subject0_.DTYPE='Person' and (subject0_.lastName like 'a%'))
There's no need to change the model or anything else.