Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Search by nested property of collection field with criteria api

I'm trying to find all entities that have some nested elements and nested elemens have collections of elements, and I need to find it by property of those collections.

It would be something like this

class A{
    private B b;
}

class B{
   private Collection<C> cCol;
}

class C{
   private String name;
}

So I want to get all A elements that have B elements that have a C which name matches given parameter.

Not sure how to do it with JPA Critieria API. I know there is in predicate, or MEMEBER OF in JPQL but I need to search by property of element in collection, not a collection member.

Tried things like root.get(a.b.c.name) and also with root.fetch(a.b) or root.fetch(b.c) but always ended up with some exceptions about illegal api usage

like image 611
Nadir Avatar asked Nov 07 '16 09:11

Nadir


2 Answers

I want to get all A elements that have B elements that have a C which name matches given parameter.

When trying to navigate the criteria API I find it immensely helpful to write the JPQL query first. Here it is:

SELECT a
FROM A a
WHERE EXISTS(
    SELECT c FROM a.b b JOIN b.cCol c WHERE c.name = 'condition'
)

Now the criteria API becomes clearer (if that is possible at all):

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> aQuery = cb.createQuery(A.class);
Root<A> a = aQuery.from(A.class);

Subquery<C> cSubquery = aQuery.subquery(C.class);
Root<A> aSubroot = cSubquery.correlate(a);
Join<A, B> b = aSubroot.join("b");  // "b" is the name of the property of A that points to B
Join<B, C> c = b.join("cCol"); // "cCol" is the name of the property of C that holds the related C objects

cSubquery.select(c);
cSubquery.where(cb.equal(c.get("name"), "XXXXXXX"));

aQuery.where(cb.exists(cSubquery));

TypedQuery<A> aTypedQuery = em.createQuery(aQuery);
aTypedQuery.getResultList();

The names of the Java variables are the same as in the JPQL, e.g. Join<A, B> b corresponds to the JPQL FROM a.b b.

like image 180
Nikos Paraskevopoulos Avatar answered Oct 23 '22 20:10

Nikos Paraskevopoulos


The below should work

root.get("a").get("b").get("name")

See

How to create specification using JpaSpecificationExecutor by combining tables?

like image 44
Essex Boy Avatar answered Oct 23 '22 20:10

Essex Boy