We have an existing predicate to fetch data from a table, we need to add this condition as well to that query
AND (SERVICE_ID IN (1,2,34,5) or (SERVICE_ID IN (83,24) and DEST_WALLET_TYPE=4 and DEST_ENTITY_TYPE = 6))
We tried different ways but all we can achieve is this
and (SERVICE_ID in (1,2,34,5)
or DEST_WALLET_TYPE_ID=4 and DEST_ENTITY_TYPE=6 and (SERVICE_ID in (24 , 83)))
A simple bracket should start after the or and should end after the final condition.
We tried these
Step-1
Predicate p1 = builder.and(root.get("serviceId").in(params.getFinancialServiceList()));
Predicate p22 = builder.equal(root.get("destWalletType"), (params.getMainWallet()));
Predicate p23 = builder.equal(root.get("destEntity"), params.getBillerEntity());
Predicate p2 = builder.and(p22, p23, builder.and(root.get("serviceId").in(Services.TOPUP, Services.DATA_RECHARGE)));
builder.or(p1, p2);
Step-2
Predicate p1 = builder.and(root.get("serviceId").in(params.getFinancialServiceList()));
builder.or(p1,
builder.and(builder.equal(root.get("destWalletType"), (params.getMainWallet()))),
builder.and(builder.equal(root.get("destEntity"), params.getBillerEntity())),
builder.and(builder.and(root.get("serviceId").in(Services.TOPUP, Services.DATA_RECHARGE))));
Can we get some help?
Use this:
Predicate p1 = root.get("serviceId").in(params.getFinancialServiceList());
Predicate p21 = root.get("serviceId").in(Services.TOPUP, Services.DATA_RECHARGE);
Predicate p22 = builder.equal(root.get("destWalletType"), (params.getMainWallet()));
Predicate p23 = builder.equal(root.get("destEntity"), params.getBillerEntity());
Predicate p2 = builder.and(p21, p22, p23);
builder.or(p1, p2);
I tried a similar query in my project.
final Predicate p1 = root.get("id").in(1, 2, 3);
final Predicate p21 = root.get("regulatorId").in(1, 2);
final Predicate p22 = criteriaBuilder.equal(root.get("name"), ("app"));
final Predicate p23 = criteriaBuilder.equal(root.get("type"), AppType.DEVELOPER);
final Predicate p2 = criteriaBuilder.and(p21, p22, p23);
criteriaBuilder.or(p1, p2);
Generated query:
select generatedAlias0 from App as generatedAlias0 where
( generatedAlias0.id in (1, 2, 3) ) or
( ( generatedAlias0.regulatorId in (1, 2) ) and ( generatedAlias0.name=:param0 ) and ( generatedAlias0.type=:param1 ) )
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 months ago.
Improve this question
In my Spring Boot app, I created a custom filtering using JPA Specification as mentioned on Searching And Filtering Using JPA Specification - Spring Boot. However, I need to join multiple tables and build a WHERE clause for my specific search via #Query.
I checked https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications page, but could not build a proper structure.
So, how can create a dynamic WHERE clause for my query?
You can use Specification. To create:
Specification<Entity> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
// Add conditions to the predicates list
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
The predicates list is used to hold conditions for the WHERE. You can add conditions to this list using the cb (CriteriaBuilder) object and the root and query parameters. These parameters provide access to the entity and the query being constructed.
You can then use the Specification object in a #Query annotation on your repository method to apply the dynamic WHERE clause to the query.
Repository example:
#Repository
public interface EntityRepository extends JpaRepository<Entity, Long>, JpaSpecificationExecutor<Entity> {
// Other repository methods
List<Entity> findAll(Specification<Entity> spec, Pageable pageable);
}
The above repository extends the JpaSpecificationExecutor to allow working with the JPA criteria API. The findByFields method also takes a Specification object as an argument. This Specification dynamically constructs the WHERE clause for the query.
So running the query:
List<Entity> entities = entityRepository.finAll(spec, pageable);
It's something like this:
Specification<BugData> bugDataSpecification = new Specification<BugData>() {
#Override
public Predicate toPredicate(Root<BugData> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> bugName = root.get("bugName");
Path<Object> bugType = root.get("bugType");
Path<Object> bugLevel = root.get("bugLevel");
List<Predicate> predicateListAnd = new ArrayList<>();
List<Predicate> predicateListOr = new ArrayList<>();
if (!StringUtils.isNullOrEmpty(bugRequestParam.getBugLevel())) {
Predicate pLevel = cb.equal(bugLevel, bugRequestParam.getBugLevel()); // ==
predicateListAnd.add(pLevel);
}
for (int i = 0; i < bugRequestParam.getBugTypeList().size(); i++) {
Predicate p1 = cb.equal(bugType, bugRequestParam.getBugTypeList().get(i));
predicateListOr.add(p1);
}
if (!StringUtils.isNullOrEmpty(bugRequestParam.getBugName())) {
Expression<Integer> findStr = cb.locate(bugName.as(String.class), bugRequestParam.getBugName()); //LOCATE
Predicate pName = cb.greaterThan(findStr, 0); // >
predicateListAnd.add(pName);
}
Predicate resultAnd[] = predicateListAnd.toArray(new Predicate[predicateListAnd.size()]);
Predicate resultOr[] = predicateListOr.toArray(new Predicate[predicateListOr.size()]);
Predicate end = cb.and(cb.and(resultAnd), cb.or(resultOr));
return end;
}
};
The whole part of this code:
#GetMapping(value = "specification")
public List<BugData> whereTiaojian() {
BugRequestParam bugRequestParam = new BugRequestParam();
bugRequestParam.setBugLevel("mid");
bugRequestParam.setBugName("CVE-2019-8331");
bugRequestParam.setLimit(100);
bugRequestParam.setPage(0);
List<String> bugTypeList = new ArrayList<>(4);
bugTypeList.add("CWE-79");
bugTypeList.add("CWE-502");
bugTypeList.add("CWE-284");
bugRequestParam.setBugTypeList(bugTypeList);
Pageable pageable = PageRequest.of(bugRequestParam.getPage(), bugRequestParam.getLimit());
Specification<BugData> bugDataSpecification = new Specification<BugData>() {
#Override
public Predicate toPredicate(Root<BugData> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> bugName = root.get("bugName");
Path<Object> bugType = root.get("bugType");
Path<Object> bugLevel = root.get("bugLevel");
List<Predicate> predicateListAnd = new ArrayList<>();
List<Predicate> predicateListOr = new ArrayList<>();
if (!StringUtils.isNullOrEmpty(bugRequestParam.getBugLevel())) {
Predicate pLevel = cb.equal(bugLevel, bugRequestParam.getBugLevel());
predicateListAnd.add(pLevel);
}
for (int i = 0; i < bugRequestParam.getBugTypeList().size(); i++) {
Predicate p1 = cb.equal(bugType, bugRequestParam.getBugTypeList().get(i));
predicateListOr.add(p1);
}
if (!StringUtils.isNullOrEmpty(bugRequestParam.getBugName())) {
Expression<Integer> findStr = cb.locate(bugName.as(String.class), bugRequestParam.getBugName());
Predicate pName = cb.greaterThan(findStr, 0);
predicateListAnd.add(pName);
}
Predicate resultAnd[] = predicateListAnd.toArray(new Predicate[predicateListAnd.size()]);
Predicate resultOr[] = predicateListOr.toArray(new Predicate[predicateListOr.size()]);
Predicate end = cb.and(cb.and(resultAnd), cb.or(resultOr));
return end;
}
};
Page<BugData> bugDataPage = bugDataVersionFiveDao.findAll(bugDataSpecification, pageable);
// This findAll method is the most important part of this all;
return bugDataPage.getContent();
}
I've been searching for a while and I don't find what i want...
Here is my code :
public List<MasseSalariale> findMasseSalarialeByCriteria(List<Section> sections,
Integer exercice, List<BpCaEtats> etats) {
List<String> etatString = new ArrayList<String>();
for (BpCaEtats e : etats) {
etatString.add(BpCaEtats.bpCaEtatToString(e));
}
CriteriaBuilder builder = this.getCriteriaBuilder();
CriteriaQuery<MasseSalariale> query = builder.createQuery(MasseSalariale.class);
Root<MasseSalariale> masseSalariale = query.from(MasseSalariale.class);
Join<MasseSalariale, Etablissement> etablissement =
masseSalariale.join(MasseSalariale_.etablissement);
Predicate p1 = builder.equal(masseSalariale.get(MasseSalariale_.annee), exercice);
Expression<List<Section>> exp2 = etablissement.get(Etablissement_.sections);
Predicate p2 = exp2.in(sections);
Expression<String> exp3 = masseSalariale.get(MasseSalariale_.etat);
Predicate p3 = exp3.in(etatString);
query.select(masseSalariale).where(builder.and(p1, p2, p3));
return this.find(query);
}
Basically, I need to know if one (at least) value in the section list from etablissement is contained in the section list in parameter. But the predicate p2 is wrong I think...
Using ListJoin, it worked well :
ListJoin<Etablissement, Section> sectionsEtab = etablissement.join(Etablissement_.sections);
Predicate p2 = sectionsEtab.in(sections);
Thanks
I have a CriteriaBuilder where I am trying to get characters starting from 0 to 10. However I am not able to get the desired output.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Emp> cq = cb.createQuery(Emp.class);
Root<Emp> c = cq.from(Emp.class);
cb.substring(c.<String>get("projDesc"), 0, 10);
cq.orderBy(cb.desc(c.get("salary")));
Query query = em.createQuery(cq);
.....
What could be the reason for this?
From the javadoc
Create an expression for substring extraction. Extracts a substring of
given length starting at the specified position. First position is 1.
Try doing cb.substring(c.<String>get("projDesc"), 1, 10);
I think you're forgetting to select the Expression<E>
Try cq.select(cb.substring(c.<String>get("projDesc"), 1, 10))
It will return List<String> if you need to Return the Emp you can use the
cb.construct(Emp.class, e.get("prop1"), e.get("prop2"), cb.substring(c.<String>get("projDesc"), 1, 10)));
I also, faced same problem in which I have to substring first three char and fetch all accounts starting from 107 which is number. for that I have used CriteriaBuilder and substring method like below.
Predicate accountNumber = criteriaBuilder.equal(criteriaBuilder.substring(from.get("accountNumber").as(String.class), 0, 3), cwipAcc);
but unfortunately it is not working for CriteriaBuilder and substring. so I have used like query to resolve this issue by given code below.
Predicate accountNumber = criteriaBuilder.like(from.get("accountNumber").as(String.class), String.valueOf(cwipAcc) + "%");
here, I have just fetched all the records which is starting from 107 and so on.
Example:
public List<GLCharts> findAccountForCWIP(Long cwipAcc, Long glInfo) {
Map<String, Object> data = new HashMap();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<GLCharts> criteriaQuery = criteriaBuilder.createQuery(GLCharts.class);
Root<GLCharts> from = criteriaQuery.from(GLCharts.class);
Predicate accountNumber = criteriaBuilder.like(from.get("accountNumber").as(String.class), String.valueOf(cwipAcc) + "%");
Predicate glCompanyInfo = criteriaBuilder.equal(from.join("gLCompanyInfo").get("id"), glInfo);
Predicate finalPredicate = criteriaBuilder.and(accountNumber, glCompanyInfo);
criteriaQuery.select(from).where(finalPredicate).orderBy(Stream.of(criteriaBuilder.asc(from.get("accountNumber"))).collect(Collectors.toList()));
List<GLCharts> glChartsList = entityManager.createQuery(criteriaQuery).getResultList();
return glChartsList;
}
I faced the problem because my requirement was to use a number into substr. Following is the sample code.
#Override
public List<SampleProfile> findNonSampleProfileBySequence(Long SampleNo) {
List<SampleProfile> profiles = new ArrayList<>();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<SampleProfile> criteriaQuery = criteriaBuilder.createQuery(SampleProfile.class);
Root<SampleProfile> SampleProfileRoot = criteriaQuery.from(SampleProfile.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (SampleUtil.isValidLong(SampleNo)) {
String SampleStr = Long.toString(SampleNo);
if (StringUtils.isNotBlank(SampleStr) && SampleStr.length() > 5) {
String SampleSequence = SampleStr.substring(5);
predicates.add(criteriaBuilder.equal(criteriaBuilder.substring(SampleProfileRoot.get(SampleProfile_.id).as(String.class), 6), SampleSequence));
predicates.add(criteriaBuilder.equal(SampleProfileRoot.get(SampleProfile_.address).get(Address_.department), SampleStr.substring(0,3)));
}
}
if (!CollectionUtils.isEmpty(predicates)) {
criteriaQuery.where(criteriaBuilder.and(Iterables.toArray(predicates, Predicate.class)));
profiles = entityManager.createQuery(criteriaQuery).setMaxResults(AbstractJpaDAO.MAX_ROW_LIMIT).getResultList();
}
return profiles;
}
Also note that for performance benefits you have to create an index on the same.
The Cast Keyword is important as Hibernate Dialect will create query like this, thus, it has to match with your index.
CREATE INDEX MY_SCHEMA_OWNER.IDX_SUBSTR_SMP_CODE ON MY_SCHEMA_OWNER.SMP_PROFILE (SUBSTR(**CAST**(SMP_CODE AS VARCHAR2(255 CHAR)),6));
JPA Criteria builder How to group logical operator AND
Current
select * from Offers marketingo0_ where (upper(marketingo0_.SOURCE_KEY_UID)=? OR marketingo0_.STATUS=? and marketingo0_.STATUS=? )
Expected
select * from Offers marketingo0_ where upper(marketingo0_.SOURCE_KEY_UID)=? OR (marketingo0_.STATUS=? and marketingo0_.STATUS=? )
List<Predicate> innerPredicates = new ArrayList<Predicate>();
List<Predicate> predicates = new ArrayList<Predicate>();
List<Predicate> outerPredicates = new ArrayList<Predicate>();
//Create all outer predicates
createPredicates(filter.getRootBooleanClause(),builder, marketingOffer, outerPredicates);
//Create all sub clauses predicates
for (BooleanClause subClause : filter.getRootBooleanClause().getSubClauses()) {
List<Predicate> groupPredicates = new ArrayList<Predicate>();
createPredicates(subClause,builder, marketingOffer, groupPredicates);
if(groupPredicates!=null && groupPredicates.size()>0 && filter.getOperator().equals(LogicOperator.OR)){
innerPredicates.add(builder.and(groupPredicates.toArray(new Predicate[groupPredicates.size()])));
}else if(groupPredicates!=null && groupPredicates.size()>0 && filter.getRootBooleanClause().getOperator().equals(LogicOperator.AND)){
innerPredicates.add(builder.or(groupPredicates.toArray(new Predicate[groupPredicates.size()])));
}
}
if(innerPredicates.size()>0){
outerPredicates.addAll(innerPredicates);
}
if(outerPredicates.size()>0 && filter.getRootBooleanClause().getOperator().equals(LogicOperator.OR)){
predicates.add(builder.or(outerPredicates.toArray(new Predicate[outerPredicates.size()])));
}else if(outerPredicates.size()>0 && filter.getRootBooleanClause().getOperator().equals(LogicOperator.AND)){
predicates.add(builder.and(outerPredicates.toArray(new Predicate[outerPredicates.size()])));
}
Try it like this:
builder.or(
builder.and(
<Column1 predicate>,
<Column2 predicate>
),
<ColumnC predicate>
);
The CriteriaBuilder will take care of correctly nesting the sub-expressions.
To achieve
upper(marketingo0_.SOURCE_KEY_UID)=?
OR
(marketingo0_.STATUS=? and marketingo0_.STATUS=? )
I think this should work
criteriaBuilder.or(
criteriaBuilder.equal(criteriaBuilder.upper(root.<String>get("SOURCE_KEY_UID")), key),
criteriaBuilder.and(
criteriaBuilder.equal(root.get("STATUS"), status),
criteriaBuilder.equal(root.get("STATUS"), status) // This doesnot make sense but sticking to OP
)
)
I'm a bit confused while creating a criteriaQuery with JPA 2.0.
Prerequisites:
I have a Gui, where the user can mark some checkboxes of (let us say) wheatherstations with some options like temperature/wind/timeperiod/etc...
Now I want to set up a criteriaQuery to pick just the selected items from a sql database and return it as an object/Map/List for building some DataModels (this will be used for generating a few primefaces charts).
What i have so far:
// for presentation purposes just this mockup-data
Calendar start = new GregorianCalendar(2011, Calendar.APRIL, 1);
Calendar end = new GregorianCalendar(2011, Calendar.MAY, 1);
List<String> selectedStations = new LinkedList<String>() {{
add("PS1");
add("PS2");
add("PS3");
}};
Map<String, Object selectedOptions = new LinkedHashMap<String, Object>() {{
put("opt1","val1");
put("opt2","val2");
put("opt3","val3");
}};
List<String> sel = new LinkedList<String>() {{
add("selOpt1");
add("selOpt2");
add("selOpt3");
}};
criteriaBuilder, criteriaQuery and the mapping class:
// go for the criteriaBuilder
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<StationItem> r = cq.from(StationItem.class);
Setting up the predicates:
// ... where (name="PS1" or name="PS2" or name="PS3") ...
Predicate p1 = cb.disjunction();
for (String s : selectedStations) {
p1 = cb.or(p1, cb.equal(r.get("name").as(String.class), s));
}
Predicate p2 = cb.between(r.get("fetchDate").as(Date.class),
start.getTime(), end.getTime());
Predicate p3 = cb.conjunction();
for (Map.Entry<String, Object> param : selectedOptions.entrySet())
p3 = cb.and(p3, cb.equal(r.get(param.getKey()), param.getValue()));
And the final step to run the query and fetching the results:
At this point I do not know what is the best approach to fill the multiselect criteria with my selections. I would like to insert all items/selections from the List sel to cq.multiselect() with some kind of a loop in a dynamic way...Any idea is welcome!
// This is working but static :(
cq.multiselect(r.get(sel.get(0)), r.get(sel.get(1)), r.get(sel.get(2)));
// i would prefer to have something like
for (int i=0;i<sel.size();i++) {
cq.multiselect().add(r.get(sel.get(i)));
}
Concatenating my WHERE-clause and executing the query:
cq.where(cb.and(p1,p2,p3));
List<Tuple> res = em.createQuery(cq).getResultList();
for (Tuple t : res) {
// do something ...
};
return <something useful>
Following a pseudo SQL query to sum up what I want to achieve:
SELECT {items from List<String> sel}
FROM MyStationDatabase
WHERE (name = selectedStation.get(0) OR ... OR name = selectedStation.get(last))
AND {items from Map<String,Object> selectedOptions}
Well, sometimes it's too trivial to be true -.-
One way to fill the cq.multiselect() with my dynamic list is to just create a list of selections and pass this over to my multiselect-query.
List<Selection<?>> s = new LinkedList<Selection<?>>();
for (String item : sel) {
s.add(r.get(item));
}
cq.multiselect(s);
easy, but maybe someone has the same struggles with this :)
and even if not, see it as an example for a criteriaQuery ;)