JPA Criteria builder for logical operator OR - jpa

I need to prepare a query some thing like below dynamically, I am getting group column in a different list and main column in a different list,
Can some one help me?
SELECT * FROM TABLE WHERE COLUMN_A =1 ? OR (COLUMN_B='A' AND COLUMN_B='Z')
Java code:
CriteriaBuilder builder;
List<Predicate> predicates = new ArrayList<Predicate>();
List<Predicate> innerPredicates = new ArrayList<Predicate>();
List<Predicate> outerPredicates = new ArrayList<Predicate>();
for(GroupClause gc : list.getGroupClause()){
List<Predicate> groupPredicates = new ArrayList<Predicate>();
groupPredicates.add(gc.getColumnB());
if(groupPredicates!=null && groupPredicates.size()>0){
innerPredicates.add(builder.and(groupPredicates.toArray(new Predicate[groupPredicates.size()])));
}
}
if(outerPredicates.size()>0){
predicates.add(builder.or(outerPredicates.toArray(new Predicate[outerPredicates.size()])));
predicates.add(builder.or(innerPredicates.toArray(new Predicate[innerPredicates.size()])));
}
The put for above code is
SELECT * FROM TABLE WHERE COLUMN_A =1 ? AND COLUMN_B='A' AND COLUMN_B='Z'

Use simple Criteria here is the example
public List<TABLE> getTABLE () {
Criteria tableCriteria= session.createCriteria(TABLE.class);
tableCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
tableCriteria.add(Restrictions.and(Restrictions.eq("COLUMN_A",1),Restrictions.or(Restrictions.eq("COLUMN_B","A"),
Restrictions.eq("COLUMN_B","Z")))
return tableCriteria.list();
}

Related

JPA Criteria API: Predicate for Exists in Another Table in Column

I need to run the following SQL in Criteria API where one additional constraint is that the main table's ned_orgunit_t.nihsac must also exist in a separate table, participating_orgs_t.nihsac.
select o.* from ned_orgunit_t o where
o.current_flag = 'Y' and
o.inactive_date is null
o.nihsac like 'HNM%' and
/* -- This is the problem -- */
exists (select nihsac from idp.participating_orgs_t where nihsac = o.nihsac);
Criteria API Java code:
// First the standard stuff, no issues: Core NED_ORGUNIT_T predicates & selection
CriteriaBuilder crbuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<NedOrgUnit> crquery = crbuilder.createQuery(NedOrgUnit.class);
Root<NedOrgUnit> root = crquery.from(NedOrgUnit.class);
crquery.select(root);
List<Predicate> predicates = new ArrayList<Predicate>();
Predicate currentFlag = crbuilder.equal(root.get("currentFlag"), "Y");
predicates.add(currentFlag);
Predicate inactiveDate = crbuilder.isNull(root.get("inactiveDate"));
predicates.add(inactiveDate);
Predicate nihsac = crbuilder.like(root.get("nihsac"), topNihsac + '%');
predicates.add(nihsac);
// Now I need to link an EXISTS match on a separate table, PARTICIPATING_ORGS_T
// ...???
// How to do it?

Creating Criteria By And or Or on Runtime by JPA Repository

I need to create a query by deciding on runtime. Basically I have few parameters and one of them will specify If I should use And or Or to combine criterias. By using Spring JPA Repository how can I do it? It is easy to do it for parameters as :
#Query(value = "SELECT u FROM User u WHERE u.name IN :names")
List<User> findUserByNameList(#Param("names") Collection<String> names);
But if I want to add one more criteria lets say size, and I need to decide which one of (AND,OR) to use to combine criteria, how can I do it?
Example:
SELECT u FROM User u WHERE u.name IN :names OR/AND size = 10;
public List<User> getUsers(List<String> names, Integer size, boolean useOrPredicate) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
Root<User> user = criteriaQuery.from(User.class);
Predicate predicate1 = user.get("name").in(names);
Predicate predicate2 = criteriaBuilder.equal(user.get("size"), size);
Predicate predicate;
if(useOrPredicate)
predicate = criteriaBuilder.or(predicate1, predicate2);
else
predicate = criteriaBuilder.and(predicate1, predicate2);
criteriaQuery.where(predicate);
return entityManager.createQuery(criteriaQuery).getResultList();
}

Dynamic JPA OR Expression

How can I construct Criteria Query to update status of multiple items?
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaUpdate<Item> query = builder.createCriteriaUpdate(Item.class);
Root<Item> root = query.from(Item.class);
List<Predicate> predicates = new ArrayList<Predicate>();
for(Long id : itemIds)
{
predicates.add(builder.or(builder.equal(root.get(Item_.id), id)));
}
query.set(root.get(Item_.status), status)
.where(predicates.toArray(new Predicate[predicates.size()]));
entityManager.createQuery(query).executeUpdate();
The above query only work if itemIds. size() == 1. If the itemIds.size() > 2 then the entities will not be updated. Can anyone help to construct correct predicate for OR expression please.
Thank in advance!

How to write subquery using criteria builder

Here I am using NativeQuery to perform selecting lookup type using subquery this is working right but I want to use Criteria Builder. How can I use it?
Query query = em.createNativeQuery(
"SELECT * FROM LOOKUPMASTER WHERE PARENTLOOKUPTYPEID = (SELECT LOOKUPID FROM LOOKUPMASTER WHERE LOOKUPTYPE =? ) ",
Lookupmaster.class
);
query.setParameter(1, lookUpType);
I tried to write the above query using criteria builder but I am getting different result here is my criteria query.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Lookupmaster.class);
Root<Lookupmaster> rt = cq.from(Lookupmaster.class);
Path<Object> path = rt.get("parentlookuptypeid");
cq.select(rt);
Subquery<Lookupmaster> subquery = cq.subquery(Lookupmaster.class);
Root rt1 = subquery.from(Lookupmaster.class);
subquery.select(rt1.get("lookupid"));
subquery.where(cb.equal(rt.get("lookuptype"),lookUpType));
cq.where(cb.in(path).value(subquery));
Query qry =em.createQuery(cq);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();
Root<EMPLOYEE> from = criteriaQuery.from(EMPLOYEE.class);
Path<Object> path = from.get("compare_field"); // field to map with sub-query
from.fetch("name");
from.fetch("id");
CriteriaQuery<Object> select = criteriaQuery.select(from);
Subquery<PROJECT> subquery = criteriaQuery.subquery(PROJECT.class);
Root fromProject = subquery.from(PROJECT.class);
subquery.select(fromProject.get("requiredColumnName")); // field to map with main-query
subquery.where(criteriaBuilder.equal("name",name_value));
subquery.where(criteriaBuilder.equal("id",id_value));
select.where(criteriaBuilder.in(path).value(subquery));
TypedQuery<Object> typedQuery = entityManager.createQuery(select);
List<Object> resultList = typedQuery.getResultList();
Here is a link
another article

How do I count the number of rows returned by subquery?

I want to do something like this:
select count(*) from (select ...)
(As it would be in SQL), but in JPA.
Any ideas on how I would do it?
I stumbled upon this issue as well. I would ultimately like to execute the following JPQL:
SELECT COUNT(u)
FROM (
SELECT DISTINCT u
FROM User u
JOIN u.roles r
WHERE r.id IN (1)
)
But this wasn't possible, also not with criteria API. Research taught that this was just a design limitation in JPA. The JPA spec states that subqueries are only supported in WHERE and HAVING clauses (and thus not in the FROM).
Rewriting the query in the following JPQL form:
SELECT COUNT(u)
FROM User u
WHERE u IN (
SELECT DISTINCT u
FROM User u
JOIN u.roles r
WHERE r.id IN (1)
)
using the JPA Criteria API like as follows:
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<User> u = query.from(User.class);
Subquery<User> subquery = query.subquery(User.class);
Root<User> u_ = subquery.from(User.class);
subquery.select(u_).distinct(true).where(u_.join("roles").get("id").in(Arrays.asList(1L)));
query.select(cb.count(u)).where(cb.in(u).value(subquery));
Long count = entityManager.createQuery(query).getSingleResult();
// ...
has solved the functional requirement for me. This should also give you sufficient insight into solving your particular functional requirement.
This should do the trick (If you want to use JPA criteria API):
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<Entity> root = query.from(Entity.class);
//Selecting the count
query.select(cb.count(root));
//Create your search criteria
Criteria criteria = ...
//Adding search criteria
query.where(criteria);
Long count = getEntityManager().createQuery(query).getSingleResult();
On the other hand, if you want to use JP-QL, the following code should do the trick:
//Add the where condition to the end of the query
Query query = getEntityManager().createQuery("select count(*) from Entity entity where...")
Long count = query.getSingleResult();
Use the following snippet to count rows for a given Criteria Query:
public static Query createNativeCountQuery(EntityManager em, CriteriaQuery<?> criteriaQuery) {
org.hibernate.query.Query<?> hibernateQuery = em.createQuery(criteriaQuery).unwrap(org.hibernate.query.Query.class);
String hqlQuery = hibernateQuery.getQueryString();
QueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator(
hqlQuery,
hqlQuery,
Collections.emptyMap(),
em.getEntityManagerFactory().unwrap(SessionFactoryImplementor.class),
null
);
queryTranslator.compile(Collections.emptyMap(), false);
String sqlCountQueryTemplate = "select count(*) from (%s)";
String sqlCountQuery = String.format(sqlCountQueryTemplate, queryTranslator.getSQLString());
Query nativeCountQuery = em.createNativeQuery(sqlCountQuery);
Map<Integer, Object> positionalParamBindings = getPositionalParamBindingsFromNamedParams(hibernateQuery);
positionalParamBindings.forEach(nativeCountQuery::setParameter);
return nativeCountQuery;
}
private static Map<Integer, Object> getPositionalParamBindingsFromNamedParams(org.hibernate.query.Query<?> hibernateQuery) {
Map<Integer, Object> bindings = new HashMap<>();
for (var namedParam : hibernateQuery.getParameterMetadata().getNamedParameters()) {
for (int location : namedParam.getSourceLocations()) {
bindings.put(location + 1, hibernateQuery.getParameterValue(namedParam.getName()));
}
}
return bindings;
}