Search code examples
jpacriteriacriteria-api

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


Solution

  • 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.