In this tutorial, we will continue implementing an extension for the Criteria API using the Builder pattern and JPA Static Metamodel Generator. Please check the previous parts: Improving your experience with Criteria API using Builder pattern and JPA Static Metamodel - Part I Improving your experience with Criteria API using Builder pattern and JPA Static Metamodel - Part II In this chapter, we provide the (subquery) operator, for example: IN List<Order> ordersToSpainWithActiveMembership = criteriaApiHelper.select(Order.class) .in(Order_.customer, subquery(CustomerMembership.class) .isTrue(CustomerMembership_.isActive) .select(CustomerMembership_.customer), subquery(CustomerAddress.class) .equal(CustomerAddress_.country, Country_.name, "Spain") .select(CustomerAddress_.customer) ) .findAll(); This query should fetch orders for the customers matching two subqueries: the customers with an active membership and the customers who live in Spain. 1. SubqueryPredicate First of all, to store the details, we define the interface. It will help us to construct the the same way as , which we created earlier to construct the : javax.persistence.criteria.Subquery SubqueryPredicate javax.persistence.criteria.Subquery QueryPredicate javax.persistence.criteria.Predicate @FunctionalInterface public interface SubqueryPredicate<P> { Subquery<P> apply(CommonAbstractCriteria criteria, CriteriaBuilder cb); } 2. SubQuery In this step, we create the interface to be able to filter the subquery using predicates methods from the inheriting interface: SubQuery BaseQuery public interface SubQuery<R, Q extends SubQuery<R, Q>> extends BaseQuery<R, Q> { } Then after we provided the necessary filters with methods, in order to finish a subquery, we may need to specify the custom subquery’s path to select that will be used for the operator. BaseQuery IN For example, with the following method, we can select the entity from the root entity type: select() Customer CustomerAddress subquery(CustomerAddress.class) .equal(CustomerAddress_.country, Country_.name, "Spain") .equal(CustomerAddress_.city, City_.name, "Madrid") .select(CustomerAddress_.customer) Let’s provide the necessary methods with a different set of a parent-child relations chain, returning the instance, which we can use later to construct the : select() SubqueryPredicate javax.persistence.criteria.Subquery public interface SubQuery<R, Q extends SubQuery<R, Q>> extends BaseQuery<R, Q> { SubqueryPredicate<R> select(); <P> SubqueryPredicate<P> select(SingularAttribute<R, P> attribute); <P1, P2> SubqueryPredicate<P2> select(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2); <P1, P2, P3> SubqueryPredicate<P3> select(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2, SingularAttribute<P2, P3> attribute3); } Next, let’s create the implementation of the interface, and implement the defined methods: SubQuery select() public class SubQueryImpl<R> extends BaseQueryImpl<R, SubQueryImpl<R>> implements SubQuery<R, SubQueryImpl<R>> { } When we construct a subquery, first we specify the Root type as the entity that we use to start a query from. Therefore, the constructor should accept the Root type: public class SubQueryImpl<R> extends BaseQueryImpl<R, SubQueryImpl<R>> implements SubQuery<R, SubQueryImpl<R>> { private final Class<R> type; public SubQueryImpl(Class<R> type) { this.type = type; } @Override protected SubQueryImpl<R> self() { return this; } } Next, we implement the defined methods. Each method should return an instance of : select() SubqueryPredicate new SubqueryPredicate<R>() { @Override public Subquery<R> apply(CommonAbstractCriteria criteria, CriteriaBuilder cb) { Subquery<R> subquery = criteria.subquery(type); Root<R> subqueryRoot = subquery.from(type); subquery .select(subqueryRoot) .where(buildPredicates(subquery, predicates, cb, subqueryRoot)); return subquery; } }; The complete implementation of all the defined methods: select() public class SubQueryImpl<R> extends BaseQueryImpl<R, SubQueryImpl<R>> implements SubQuery<R, SubQueryImpl<R>> { ... @Override public SubqueryPredicate<R> select() { return (query, cb) -> { Subquery<R> subquery = query.subquery(type); Root<R> subqueryRoot = subquery.from(type); subquery .select(subqueryRoot) .where(buildPredicates(subquery, predicates, cb, subqueryRoot)); return subquery; }; } @Override public <P> SubqueryPredicate<P> select(SingularAttribute<R, P> attribute) { return (query, cb) -> { Subquery<P> subquery = query.subquery(attribute.getJavaType()); Root<R> subqueryRoot = subquery.from(type); subquery .select(subqueryRoot.get(attribute)) .where(buildPredicates(subquery, predicates, cb, subqueryRoot)); return subquery; }; } @Override public <P1, P2> SubqueryPredicate<P2> select(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2) { return (query, cb) -> { Subquery<P2> subquery = query.subquery(attribute2.getJavaType()); Root<R> subqueryRoot = subquery.from(type); subquery .select(subqueryRoot.get(attribute1).get(attribute2)) .where(buildPredicates(subquery, predicates, cb, subqueryRoot)); return subquery; }; } @Override public <P1, P2, P3> SubqueryPredicate<P3> select(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2, SingularAttribute<P2, P3> attribute3) { return (query, cb) -> { Subquery<P3> subquery = query.subquery(attribute3.getJavaType()); Root<R> subqueryRoot = subquery.from(type); subquery .select(subqueryRoot.get(attribute1).get(attribute2).get(attribute3)) .where(buildPredicates(subquery, predicates, cb, subqueryRoot)); return subquery; }; } } Now, we can compose a subquery using the and build an instance of the . But to apply the operator, we should define the filter in the interface. SubQueryImpl SubqueryPredicate IN in() BaseQuery 3. IN Operator In this section, we will define the absent filter in the interface. This method should accept the varargs argument as the selected value of : IN BaseQuery SubqueryPredicate<R> SubQuery public interface BaseQuery<R, Q extends BaseQuery<R, Q>> { ... Q in(SubqueryPredicate<R>... values); } Next, we can specify additional methods accepting parameters to reach the nested relation which should be used in the operator. in() SingularAttribute IN In the following example, we use the filter to match the entity with the subquery, returning the entity: in() Address’s city Location City .in(Address_.city, subquery(Location.class) .isTrue(Location_.hasPriority) .select(Location_.city) ) Let’s define these additional methods accepting a different set of parent-child arguments, allowing us to use nested relations: in() SingularAttribute public interface BaseQuery<R, Q extends BaseQuery<R, Q>> { ... <V> Q in(SingularAttribute<R, V> attribute, SubqueryPredicate<V>... values); <P, V> Q in(SingularAttribute<R, P> attribute1, SingularAttribute<P, V> attribute2, SubqueryPredicate<V>... values); <P1, P2, V> Q in(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2, SingularAttribute<P2, V> attribute3, SubqueryPredicate<V>... values); } Next, let’s implement defined methods in the . Each method should first map the varargs parameters to the instances using method and, after that, create a predicate with the operator for the path provided by parameters: in() BaseQueryImpl SubqueryPredicate javax.persistence.criteria.Subquery SubqueryPredicate#apply() IN SingularAttribute Predicate[] predicates = Arrays.stream(subqueryPredicates) .map(t -> t.apply(CommonAbstractCriteria, CriteriaBuilder)) .map(subquery -> root.get(attribute).in(subquery)) .toArray(Predicate[]::new); Now we can apply the to make a conjunction of the obtained predicates and finally, construct an instance of the : CriteriaBuilder#and() QueryPredicate new QueryPredicate<R>() { @Override public Predicate apply(CommonAbstractCriteria criteria, CriteriaBuilder cb, Path<R> root) { Predicate[] predicates = Arrays.stream(subqueryPredicates) .map(t -> t.apply(criteria, cb)) .map(subquery -> root.get(attribute).in(subquery)) .toArray(Predicate[]::new); return cb.and(predicates); } }; There is the complete implementation of all methods: in() public abstract class BaseQueryImpl<R, Q extends BaseQueryImpl<R, Q>> implements BaseQuery<R, Q> { ... @SafeVarargs @Override public final Q in(SubqueryPredicate<R>... values) { predicates.add((criteria, cb, root) -> cb.and( Arrays.stream(values) .map(t -> t.apply(criteria, cb)) .map(root::in) .toArray(Predicate[]::new) ) ); return self(); } @SafeVarargs @Override public final <V> Q in(SingularAttribute<R, V> attribute, SubqueryPredicate<V>... values) { predicates.add((criteria, cb, root) -> cb.and( Arrays.stream(values) .map(t -> t.apply(criteria, cb)) .map(subquery -> root.get(attribute).in(subquery)) .toArray(Predicate[]::new) ) ); return self(); } @SafeVarargs @Override public final <P, V> Q in(SingularAttribute<R, P> attribute1, SingularAttribute<P, V> attribute2, SubqueryPredicate<V>... values) { predicates.add((criteria, cb, root) -> cb.and( Arrays.stream(values) .map(t -> t.apply(criteria, cb)) .map(subquery -> root.get(attribute1).get(attribute2).in(subquery)) .toArray(Predicate[]::new) ) ); return self(); } @SafeVarargs @Override public final <P1, P2, V> Q in(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2, SingularAttribute<P2, V> attribute3, SubqueryPredicate<V>... values) { predicates.add((criteria, cb, root) -> cb.and( Arrays.stream(values) .map(t -> t.apply(criteria, cb)) .map(subquery -> root.get(attribute1).get(attribute2).get(attribute3).in(subquery)) .toArray(Predicate[]::new) ) ); return self(); } } Lastly, to finish the implementation, we can provide static methods, allowing us to create nested predicates within the filters. in() IN and/or For example, the following query will retrieve the orders with a customer matching any of the provided subqueries: List<Order> ordersToSpainOrWithActiveMembership = criteriaApiHelper.select(Order.class) .or(Order_.customer, in( subquery(CustomerDetails.class) .isTrue(CustomerDetails_.isActiveMembership) .select(CustomerDetails_.customer) ), in(Customer_.address, subquery(AddressDetails.class) .equal(AddressDetails_.country, Country_.name, "Spain") .select(AddressDetails_.address) ) ) .findAll(); There is the implementation for all the static methods in the class: in() CriteriaApiHelper public class CriteriaApiHelper { ... @SafeVarargs public static <R> QueryPart<R> in(SubqueryPredicate<R>... values) { return new QueryPart<R>() .in(values); } @SafeVarargs public static <R, V> QueryPart<R> in(SingularAttribute<R, V> attribute, SubqueryPredicate<V>... values) { return new QueryPart<R>() .in(attribute, values); } @SafeVarargs public static <R, P, V> QueryPart<R> in(SingularAttribute<R, P> attribute1, SingularAttribute<P, V> attribute2, SubqueryPredicate<V>... values) { return new QueryPart<R>() .in(attribute1, attribute2, values); } @SafeVarargs public static <R, P1, P2, V> QueryPart<R> in(SingularAttribute<R, P1> attribute1, SingularAttribute<P1, P2> attribute2, SingularAttribute<P2, V> attribute3, SubqueryPredicate<V>... values) { return new QueryPart<R>() .in(attribute1, attribute2, attribute3, values); } } 4. Wrapping Up In this part, we slightly improved extension for the Criteria API. Now we can perform the operator. IN