JPA Criteria Query distinct - java

I am trying to write a distinct criteria query, using:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<RuleVar> query = builder.createQuery(RuleVar.class);
Root<RuleVar> ruleVariableRoot = query.from(RuleVar.class);
query.select(ruleVariableRoot.get("foo").<String>get("foo")).distinct(true);
Based on the example in the javadoc for CriteriaQuery.select()
CriteriaQuery<String> q = cb.createQuery(String.class);
Root<Order> order = q.from(Order.class);
q.select(order.get("shippingAddress").<String>get("state"));
However, this gives me an error:
The method select(Selection<? extends RuleVar>) in the type CriteriaQuery<RuleVar> is not applicable for the arguments (Path<String>)
Can someone please point out what I am doing wrong? Or how to get a Selection object from a Path?

I got it. The problem was my CriteraQuery needed to be of type String. This works:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<String> query = builder.createQuery(String.class);
Root<RuleVar> ruleVariableRoot = query.from(RuleVar.class);
query.select(ruleVariableRoot.get(RuleVar_.varType)).distinct(true);

Related

Not able to set from clause with subquery in Hibernate 6

I have the below piece of the code to get count query form the original query.
But this is the line causing the issue at compile time.
countQuery.from(sqmSubQuery);
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
SqmSubQuery sqmSubQuery = (SqmSubQuery<Tuple>) countQuery.subquery(Tuple.class);
SqmSelectStatement sqmOriginalQuery = (SqmSelectStatement) query;
SqmQuerySpec sqmOriginalQuerySpec = sqmOriginalQuery.getQuerySpec();
sqmSubQuery.setQueryPart(sqmOriginalQuerySpec.copy(SqmCopyContext.simpleContext()));
Root<T> subQuerySelectRoot = (Root<T>) sqmSubQuery.getRoots().iterator().next();
sqmSubQuery.multiselect(subQuerySelectRoot.get("id").alias("id"));
countQuery.select(builder.count(builder.literal(1)));
countQuery.from(sqmSubQuery);
Based on you comment you want to select the distinct count of all employee types. The query you provided should be equivalent to SELECT COUNT(DISTINCT employee_type) FROM Employee.
This can be written in JPA as shown below:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
Root<Employee> employeeRoot = countQuery.from(Employee.class);
countQuery.select(builder.countDistinct(employeeRoot.get("type")));
Long count = entityManager.createQuery(countQuery).getSingleResult();
where type is the name of the property that maps to the column employee_type
The type org.hibernate.query.criteria.JpaSelectCriteria declares this method:
<X> JpaDerivedRoot<X> from(jakarta.persistence.criteria.Subquery<X> subquery);
which is the one you need to call if you're trying to use a subquery in the from clause.
And SqmSelectStatement implements JpaSelectCriteria. (It is also the object which implements jakarta.persistence.criteria.CriteriaQuery.)
So you can cast any CriteriaQuery to JpaSelectCriteria and call from():
CriteriaQuery<Thing> query = ... ;
Subquery<OtherThing> subquery = ... ;
((JpaSelectCriteria<Thing>) query).from(subquery);
or whatever (I did not test this code).

JPA create count query from existing CriteriaQuery

I have a query with some predicates, I need to count total records for paging.
Currently, what I'm doing is declare 2 roots for the query to get result list (1) and the count query (2), then with each predicate, duplicate it with different root like this
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<A> root = cq.from(A.class);
CriteriaQuery<Long> cq = cb.createQuery(A.class);
Root<A> rootCount = countQuery.from(A.class);
List<Predicate> predicates = new ArrayList<>();
List<Predicate> predicatesCount = new ArrayList<>();
Predicate p = cb.equal(root.get(A.ID), 1);
predicates.add(p);
Predicate p1 = cb.equal(rootCount.get(A.ID), 1);
predicatesCount.add(p1);
...
// execute both query to get result
So the question is:
Is it possible to create count query from query (1)? Or something to reuse the predicates with count query?
Thanks for reading!
The below example showcases setting up a criteria builder/predicate restrictions, then reusing that to do a count query as well.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<EntityStub> criteriaQuery = builder.createQuery(EntityStub.class);
Root<EntityStub> entity_ = criteriaQuery.from(EntityStub.class);
entity_.alias("entitySub"); //assign alias to entity root
criteriaQuery.where(builder.equal(entity_.get("message"), "second"));
// Generic retrieve count
CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
Root<T> entity_ = countQuery.from(criteriaQuery.getResultType());
entity_.alias("entitySub"); //use the same alias in order to match the restrictions part and the selection part
countQuery.select(builder.count(entity_));
Predicate restriction = criteriaQuery.getRestriction();
if (restriction != null) {
countQuery.where(restriction); // Copy restrictions
}
Long count = entityManager.createQuery(countQuery).getSingleResult();
See if that helps you, take note of the root alias, and when doing a Count Query, make sure the Entity class type is Long.class
https://forum.hibernate.org/viewtopic.php?p=2471522#p2471522
You could use Blaze-Persistence to generate the count query for you as it's not that easy to implement such a count query efficiently.
Blaze-Persistence is a library that works on top of JPA/Hibernate and adds support for advanced SQL constructs, rich pagination support and much more. It also has a JPA Criteria implementation which you can use as a drop-in replacement. You can then convert this query to a Blaze-Persistence Core query builder which allows to generate a count query: https://github.com/Blazebit/blaze-persistence#jpa-criteria-api-quick-start
I think this guy answered your question with its utility class like so :
Long count = JpaUtils.count(entityManager, criteriaQuery);
https://stackoverflow.com/a/9246377/5611906

How can I use JPA do this SQL query?

Hello everyone I try do this query "SELECT SUM(cevecoin) FROM coin where clid="ABC" in JPA but it's no working. Can somebody help me?
public Integer SumCoin(String Clid) {
CriteriaBuilder builder =getSession().getCriteriaBuilder();
CriteriaQuery<CoinBean> criteria =builder.createQuery(CoinBean.class);
Root<CoinBean> root = criteria.from(CoinBean.class);
criteria.select(builder.sum(root.<Integer>get("clid")).as(CoinBean.class)).where(builder.equal(root.get("clid"),Clid));
return getSession().createQuery(criteria).getSingleResult().getCevecoin();
}
try this:
CriteriaBuilder builder =getSession().getCriteriaBuilder();
// the type of query criteria must correspond to the result we want to obtain
CriteriaQuery<BigDecimal> criteria = builder.createQuery(BigDecimal.class);
Root<CoinBean> root = criteria.from(CoinBean.class);
// use multiselect and sum the field cevecoin
criteria.multiselect(builder.sum(root.get("cevecoin")))
criteria.where(builder.equal(root.get("clid"),Clid));
return getSession().createQuery(criteria).getSingleResult();

how to write subquery using criteria

please help me out writing criteria builder for this query
SELECT *
FROM XYZ
WHERE date_v < "2020/01" AND
id NOT IN (SELECT id FROM XYZ WHERE date_v = '2020/01')
i have looked at using subqueries in jpa criteria api but i am unable to figure it
I have tried using subquery and joins but it throwing different error after all i get to know that i need to get more clarity about query criteria usages. any help much appreciated
You have to create XyzEntity with Long id and LocalDate date_v fields.
// query
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<XyzEntity> query = cb.createQuery(XyzEntity.class);
Root<XyzEntity> root = query.from(XyzEntity.class);
LocalDate date = LocalDate.of(2020, 1, 1);
// subquery
Subquery<Long> subQuery = query.subquery(Long.class);
Root<XyzEntity> subRoot = subQuery.from(XyzEntity.class);
Predicate idSubPredicate = cb.equal(root.get("id"), subRoot.get("id"));
Predicate dateSubPredicate = cb.equal(subRoot.get("date_v"), date);
subQuery.select(subRoot.get("id")).where(idSubPredicate, dateSubPredicate);
// query predicates
Predicate datePredicate = cb.greaterThan(root.get("date_v"), date);
Predicate notExistsPredicate = cb.exists(subQuery).not();
// query result
query.select(root).where(datePredicate, notExistsPredicate);
List<XyzEntity> result = entityManager.createQuery(query).getResultList();
I have mentioned the corrections in comments for the answer but I feel providing full solution seems good and helps others:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Entity> query = cb.createQuery(Entity.class);
Root<Entity> root = query.from(Entity.class);
// subquery
Subquery<Long> subQuery = query.subquery(Long.class);
Root<Entity> subRoot = subQuery.from(Entity.class);
Predicate subPredicate = cb.equal(subRoot.get("date_v"), dateValue);
subQuery.select(subRoot.get("id")).where(subPredicate);
// query predicates
Predicate datePredicate = cb.lessThan(root.get("date_v"), dateValue);
Predicate notExistsPredicate = root.get("id").in(subQuery).not();
// query result
query.select(root).where(datePredicate, notExistsPredicate);
Query d = entityManager.createQuery(query);
List<Entity> resultList = d.getResultList()

Delete multiple objects at once using CriteriaBuilder

I'm trying to delete a bunch of objects with one query using the CriteriaBuilder API. I'm looking for something like this select:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = criteriaBuilder.createQuery(entityClass);
Root<T> root = query.from(entityClass);
query.select(root).where(/*some condition*/);
return entityManager.createQuery(query).getResultList();
but then a delete instead of a select. As far as I can see, there's no remove or delete method on CriteriaQuery. Is it possible using this API?
I can of course execute the select, then call entityManager.remove(object) for each result, but that feels very inefficient.
Try this:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaDelete<SomeClass> query = criteriaBuilder.createCriteriaDelete(SomeClass.class);
Root<SomeClass> root = query.from(SomeClass.class);
query.where(root.get("id").in(listWithIds));
int result = entityManager.createQuery(query).executeUpdate();
The where clause can laso look like this:
query.where(criteriaBuilder.lessThanOrEqualTo(root.get("id"), someId));
in JPA 2.1, there are Criteria APIs exactly as what you want.it looks like this:
CriteriaBuilder cBuilder = em.getCriteriaBuilder();
CriteriaDelete<T> cq = cBuilder.createCriteriaDelete(entityClass);
Root<T> root = cq.from(entityClass);
cq.where(/*some codition*/);
int result = em.createQuery(cq).executeUpdate();
you can refert to JPA 2.1 SPEC and API here

Categories