@Override public final List<Product> findByFeatures(final Map<Integer, List<String>> featureValues) { CriteriaBuilder builder = manager.getCriteriaBuilder(); CriteriaQuery<Product> criteriaQuery = builder.createQuery(Product.class); Root<Product> product = criteriaQuery.from(Product.class); Path<ProductFeature> feature = product.join("features"); CriteriaQuery<Product> select = criteriaQuery.select(product); Predicate featurePredicate = builder.disjunction(); for (final Map.Entry<Integer, List<String>> fValue : featureValues.entrySet()) { Predicate equalFeatureId = builder.equal(feature.get("featureId"), fValue.getKey()); List<String> values = fValue.getValue(); Predicate equalsValues = builder.disjunction(); for (String value : values) { Predicate equalFeatureVal = builder.equal(feature.get("value"), value); equalsValues = builder.or(equalsValues, equalFeatureVal); } featurePredicate = builder.or(featurePredicate, builder.and(equalFeatureId, equalsValues)); } select.where(featurePredicate); select.groupBy(product.get("id")); select.having(builder.equal(builder.count(product), featureValues.size())); TypedQuery<Product> query = manager.createQuery(criteriaQuery); return query.getResultList(); }