简体   繁体   中英

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?

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); 

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));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM