Víctor Gómez
Víctor Gómez

Reputation: 722

Criteria query combine and predicates and or predicates in where method

The where method of CriteriBuilder

restricts the query result according to the conjunction of the specified restriction predicates

In other words, concatenate all predicates with AND. I pass a list of predicates to this method in this way:

criteria.where(preds.toArray(new Predicate[0]));

The result query is something like this:

... where p1 and p2 and p3

however what I need is:

... where p1 and p2 or p3

I tried to use two list of preds, one for "ANDS" and another one for "ORS":

if(preds.isEmpty() && !orPreds.isEmpty()) {
    criteria.where(cb.or(orPreds.toArray(new Predicate[orPreds.size()])));
}
else if(!preds.isEmpty() && !orPreds.isEmpty()) {
    criteria.where(cb.and(preds.toArray(new Predicate[preds.size()])), 
    cb.or(orPreds.toArray(new Predicate[orPreds.size()])));
}
else {
    criteria.where(preds.toArray(new Predicate[0]));
}

But the result query is the same:

... where p1 and p2 and p3

Any idea?

Upvotes: 1

Views: 9160

Answers (2)

ieselisra
ieselisra

Reputation: 384

By default all predicates are understood as "AND".

You can "play" adding more complexity to the predicates using cb.and / cb.or (nested if needed)

You can create a list like this:

Root<BeanA> queryRoot = cq.from(BeanA.class);

List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(queryRoot.get("id"), valueA);
predicates.add(cb.or(
                cb.equal(queryRoot.get("id"), valueA,
                cb.equal(queryRoot.get("id"), valueB
        ));
predicates.add(cb.and(
       cb.equal(queryRoot.get("valueC"), valueC),
       cb.or(
             cb.equal(queryRoot.get("id"), valueA,
             cb.equal(queryRoot.get("id"), valueB
        ))
);

cq.where(predicates.toArray(Predicate[]::new));

If needed, you can use a multiselect too (adding here the result of subqueries if you want to handle them)

 List<Selection> selectList = new ArrayList<>();

 selectList.add(queryRoot.get("id"));
 selectList.add(queryRoot.get("title"));

 cq.multiselect(selectList.toArray(Selection[]::new));

After adding a multiselect you could need an orderBy. you can follow the same concept but using a Expression list

List<Expression> groupByList = new ArrayList<>();

groupByList.add(proyectoJoin.get("id"));
groupByList.add(proyectoJoin.get("title"));
 
cq.groupBy(groupByList.toArray(Expression[]::new));

Upvotes: 0

perissf
perissf

Reputation: 16273

Simply combine your arrays of predicates into simple predicates, using CriteriaBuilder.and(Predicate... restrictions) and CriteriaBuilder.or(Predicate... restrictions)

For getting where (p1 and p2) or p3, where p1, p2 and p3 are all arrays of predicates concatenated with and statements:

Predicate[] p1 = new Predicate[2];
Predicate[] p2 = new Predicate[2];
Predicate[] p3 = new Predicate[2];
// add your predicates to the arrays.     
Predicate p1all = cb.and(p1);    
Predicate p2all = cb.and(p2);
Predicate p3all = cb.and(p3);
Predicate pFinal = cb.or(cb.and(p1all, p2all), p3all);
criteria.where(pFinal);

For getting where p1 and (p2 or p3):

Predicate pFinal = cb.and(cb.or(p2all, p3all), p1all);
criteria.where(pFinal);

Finally, if you want to build a single predicate by concatenating an array of predicates with or statements, use this:

Predicate p1all = cb.or(p1); 

Upvotes: 2

Related Questions