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 protected] ...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();
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.
One option is to use the fact that method with variable number of arguments can take an array:
query.where(predicates.toArray(new Predicate[predicates.size()]));
Alternatively, you can combine them into a single predicate (note that if you don't do it, you don't need to create a conjunction as in your example);:
Predicate where = cb.conjunction(); while (column.hasNext()) { ... where = cb.and(where, cb.equal(userRoot.get(colIndex), colValue)); } query.where(where);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With