Really dynamic JPA CriteriaBuilder
criteria-api, java, jpa, jpa-2.0
Solution
You can pass an array of predicates to the `CriteriaBuilder`, deciding on `equal` or `like` as you go. For this, build a list and pack the contents of the list into an array in a single `and` statement. Like this:
final List<Predicate> predicates = new ArrayList<Predicate>();
for (final Entry<String, String> e : myPredicateMap.entrySet()) {
final String key = e.getKey();
final String value = e.getValue();
if ((key != null) && (value != null)) {
if (value.contains("%")) {
predicates.add(criteriaBuilder.like(root.<String> get(key), value));
} else {
predicates.add(criteriaBuilder.equal(root.get(key), value));
}
}
}
query.where(criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])));
query.select(count);
In case you need to distingiush between `and` and `or`, use two lists.
Problem
I need to create a "real" dynamic JPA `CriteriaBuilder`. I get an `Map<String, String>` with the statements. It looks like: ``` name : John surname : Smith email : email@email.de ...more pairs possible ``` Here is what i implement: ``` CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<User> query = cb.createQuery(User.class); Root<User> userRoot = query.from(User.class); query.select(userRoot); List<Predicate> predicates = new ArrayList<Predicate>(); Iterator<String> column = statements.keySet().iterator(); while (column.hasNext()) { // get the pairs String colIndex = column.next(); String colValue = statements.get(colIndex); // create the statement Predicate pAnd = cb.conjunction(); pAnd = cb.and(pAnd, cb.equal(userRoot.get(colIndex), colValue)); predicates.add(pAnd); } // doesn't work, i don't know how many predicates i have -> can not address them query.where(predicates.get(0), predicates.get(1), ...); // doesn't work, because it is a list of predicates query.where(predicates); // doesn't work, because the actual predicate overwrites the old predicate for (Predicate pre : predicates) { query.where(pre) } ``` I tried to build a big `Predicate`, which contains all other predicates and add this to the `query.where()`, but again the predicates overwrites old values. Looks like there is no possibility to add a `Predicate` instead of change a Predicate :-( The real project is even more complicated, because some pairs requires an `equal` and some other a `like`. And that is not even enough: There could a extra statement with `or` included like `type : 1;4;7`. Here the value have to split up and create a statement like: `<rest of statement> AND (type = 1 OR type = 4 OR type = 7)` UPDATE and SOLUTION Got two lists, first List for AND works well. Second list contains OR statements like exspected: ``` final List<Predicate> andPredicates = new ArrayList<Predicate>(); final List<Predicate> orPredicates = new ArrayList<Predicate>(); for (final Entry<String, String> entry : statements.entrySet()) { final String colIndex = entry.getKey(); final String colValue = entry.getValue(); if (colIndex != null && colValue != null) { if (!colValue.contains(";")) { if (equals) { andPredicates.add(cb.equal(userRoot.get(colIndex), colValue)); } else { andPredicates.add(cb.like(userRoot.<String> get(colIndex), "%" + colValue + "%")); } } else { String[] values = colValue.split(";"); for (String value : values) { orPredicates.add(cb.or(cb.equal(userRoot.get(colIndex), value))); } } } } // Here goes the magic to combine both lists if (andPredicates.size() > 0 && orPredicates.size() == 0) { // no need to make new predicate, it is already a conjunction query.where(andPredicates.toArray(new Predicate[andPredicates.size()])); } else if (andPredicates.size() == 0 && orPredicates.size() > 0) { // make a disjunction, this part is missing above Predicate p = cb.disjunction(); p = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()])); query.where(p); } else { // both types of statements combined Predicate o = cb.and(andPredicates.toArray(new Predicate[andPredicates.size()])); Predicate p = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()])); query.where(o, p); } query.where(predicates.toArray(new Predicate[predicates.size()])); users = em.createQuery(query).getResultList(); ```