Reputation: 4273
Say I have an entity
@Entity
public class Test {
@ManyToMany
@JoinTable(..etc..)
private List<Subject> subjects; // School subjects this test is associated with
....
And an entity
@Entity
public class Exam extends Test {
// Inherits subjects from test
// Does some things specific to exams
...
And I want to write a criteria query (with metamodels) that gives me only the Exam
s associated with a certain Subject
. My question is: how do I write this query?
What I've tried is the following:
If I write
CriteriaBuilder cb = em.getCriteriaBuilder(); // em is the EntityManager
CriteriaQuery<Exam> cq = cb.createQuery(Exam.class);
Root<Exam> root = cq.from(Exam.class);
cq.where(cb.isMember(subject, root.get(Exam_.subjects)));
return em.createQuery(cq);
the compiler won't compile it, saying error: no suitable method found for get(ListAttribute<Test,Subject>)
. Intuitively it feels as if this should be the solution, but inheritance won't go far enough. It won't work either if I omit the metamodel reference in the query and replace it with root.get("subjects")
.
If I write
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Exam> cq = cb.createQuery(Exam.class);
Root<Test> root = cq.from(Test.class);
cq.where(cb.isMember(subject, root.get(Exam_.subjects)));
return em.createQuery(cq);
This feels wrong, but it does compile. However, upon actually executing the code I am presented with an exception: IllegalStateException: No explicit selection and an implicit one could not be determined
which I interpret as a consequence of juggling around the types for the Root
. Trying root.get(Test_.subjects)
yields the same result.
I use Hibernate as my JPA implementation, but I try to stick to JPA Criteria Queries.
Upvotes: 0
Views: 2905
Reputation: 73
I finally found it is just a bug and will be fix in the jakartaee next version.
https://github.com/jakartaee/persistence/pull/173
the bug of root.get
method.
So you can use like this temporarily
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Exam> cq = cb.createQuery(Exam.class);
Root<Exam> root = cq.from(Exam.class);
cq.where(cb.isMember(subject, root.get((PluralAttribute)Exam_.subjects)));
return em.createQuery(cq);
Upvotes: 0
Reputation: 691625
In JPQL (which I really advise you to use when you don't need a dynamically generated query), you would write it like this:
select e from Exam e
inner join e.students student
where student.id = :studentId
If you really want to use the Criteria API to write this query, do the same thing: create a join and check that the ID of the joined entity is equal to the given student ID. It should look like this:
Join<Exam, Student> studentJoin = root.join(Exam_.students);
cq.where(qb.equal(studentJoin.get(Student_.id), student.getId());
Upvotes: 0