EclipseLink Criteria API count in results after joining table - java

After ours of searching I was still unable to find a way to write this SQL equivalent in EclipseLink criteria query:
SELECT preke.*, count(nuotrauka.prid) AS cnt FROM preke LEFT JOIN nuotrauka ON nuotrauka.prid=preke.prid WHERE trash = 1 GROUP BY preke.prid ORDER BY cnt DESC
I tried joins, multiselects and etc. I need getResultList() to return me List within List like list[0] - (Preke)preke1, list[1] - (Integer)count1; list[0] - (Preke)preke2, list[1] - (Integer)count2 ... .
EDIT 1:
CriteriaBuilder cb = EntityManager.getInstance().getCriteriaBuilder();
CriteriaQuery criteriaQuery = cb.createQuery(Tuple.class);
Root<Preke> from = criteriaQuery.from(Preke.class);
Expression<Long> count = cb.count(from.get("images"));
criteriaQuery.where(cb.equal(from.get("trash"), true));
criteriaQuery.multiselect(from.alias("preke"), count.alias("count"));
criteriaQuery.groupBy(from.get("prid"));
TypedQuery<Tuple> typedQuery = EntityManager.getInstance().createQuery(criteriaQuery);
typedQuery.setFirstResult(PAGE * ITEMS_PER_PAGE);
typedQuery.setMaxResults(ITEMS_PER_PAGE);
prekes = typedQuery.getResultList();
...
for(Tuple t : prekes) {
Preke p = (Preke)t.get("preke");
long count = (long)t.get("count");
...
}
It give me following JPQL statement:
SELECT t0.prid AS a1,
...,
COUNT(t1.id)
FROM preke t0,
nuotrauka t1
WHERE
((t0.trash = ?) AND (t1.prid = t0.prid))
GROUP BY t0.prid LIMIT ?, ?
This is almost fine, but it doesn't include results where count is 0.
As above JPQL statement says - t1.prid = t0.prid should be the bad part, how to replace it? I think what I need here is a LEFT JOIN. But how to do it?

Instead of using
Expression<Long> count = cb.count(from.get("images"));
try using
Join<Preke, Nuotrauka> images = from.join("images", JoinType.LEFT);
Expression<Long> count = cb.count(images);

Related

Is it possible to include COUNT into multiselect using TypedQuery in java 7?

SQL (that works in database):
SELECT ffm.REPORTING_PERIOD, count(DISTINCT ffm.FI_MESSAGE_ID), count(ffa.FI_ACCOUNT_ID) FROM RAZV.FINTAEOI2_FI_MESSAGE ffm
JOIN razv.FINTAEOI2_FI_MESSAGE_FI ffmf ON ffm.FI_MESSAGE_ID = ffmf.FI_MESSAGE_ID
JOIN razv.FINTAEOI2_FI_ACCOUNT ffa ON ffmf.FI_MESSAGE_FI_ID = ffa.FI_MESSAGE_FI_ID
WHERE ffm.REPORTING_PERIOD = '2020-12-31'
GROUP BY ffm.REPORTING_PERIOD
There will be 6 possible WHERE statements so I decided to use TypedQuery to simplify conditions appending.
Problem is; How can I include count into TypedQuery select and is it even possible? If not, what would be the best way except making multiple queries? If only way is multiple queries, how do I fill the dto?
This is my query where I should include count.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ReportListDto> cq = cb.createQuery(ReportListDto.class);
Root<FiMessage> fromFIMessage = cq.from(FiMessage.class);
Join<FiMessage, FiMessageFi> fiMessageFi = fromFIMessage.join("fintaeoi2FiMessageFis", JoinType.INNER);
Join<FiMessageFi, FiAccount> fiAccount = fiMessageFi.join("fintaeoi2FiAccounts", JoinType.INNER);
List<Predicate> conditions = new ArrayList<Predicate>();
`**TypedQuery<ReportListDto> query = entityManager.createQuery(
cq.multiselect(
fromFIMessage.get("reportingPeriod"),
fromFIMessage.get("fiMessageId"),
fiAccount.get("fiAccountId"))
.where(conditions.toArray(new Predicate[] {})));**
List<ReportListDto> result = query.getResultList();
`
result of above is
[results without count included](https://i.stack.imgur.com/MO7Zk.png)
but i need
[result ash it should be with applying count in typedquery](https://i.stack.imgur.com/gqnC9.png)
So reporting period is fine and I need distinct count for messageId and count for accountId.
Thank you for your answers.
The answer is
TypedQuery<ReportListDto> query = entityManager.createQuery(cq.multiselect(fromFIMessage.get("reportingPeriod"), cb.countDistinct(fromFIMessage.get("fiMessageId")), cb.count(fiAccount.get("fiAccountId")))
.where(conditions.toArray(new Predicate[] {})));

QueryDSL - order by count as alias

I'm using queryDSL to get users with some additional data from base:
public List<Tuple> getUsersWithData (final SomeParam someParam) {
QUser user = QUser.user;
QRecord record = QRecord.record;
JPQLQuery = query = new JPAQuery(getEntityManager());
NumberPath<Long> cAlias = Expressions.numberPath(Long.class, "cAlias");
return query.from(user)
.leftJoin(record).on(record.someParam.eq(someParam))
.where(user.active.eq(true))
.groupBy(user)
.orderBy(cAlias.asc())
.list(user, record.countDistinct().as(cAlias));
}
Despite it's working as desired, it generates two COUNT() in SQL:
SELECT
t0.ID
t0.NAME
to.ACTIVE
COUNT(DISTINCT (t1.ID))
FROM USERS t0 LEFT OUTER JOIN t1 ON (t1.SOME_PARAM_ID = ?)
WHERE t0.ACTIVE = true
GROUP BY t0.ID, to.NAME, t0.ACTIVE
ORDER BY COUNT(DISTINCT (t1.ID))
I want to know if it's possible to get something like this:
SELECT
t0.ID
t0.NAME
to.ACTIVE
COUNT(DISTINCT (t1.ID)) as cAlias
FROM USERS t0 LEFT OUTER JOIN t1 ON (t1.SOME_PARAM_ID = ?)
WHERE t0.ACTIVE = true
GROUP BY t0.ID, to.NAME, t0.ACTIVE
ORDER BY cAlias
I failed to understand this from documentation, please, give me some directions if it's possible.
QVehicle qVehicle = QVehicle.vehicle;
NumberPath<Long> aliasQuantity = Expressions.numberPath(Long.class, "quantity");
final List<QuantityByTypeVO> quantityByTypeVO = new JPAQueryFactory(getEntityManager())
.select(Projections.constructor(QuantityByTypeVO.class, qVehicle.tipo, qVehicle.count().as(aliasQuantity)))
.from(qVehicle)
.groupBy(qVehicle.type)
.orderBy(aliasQuantity.desc())
.fetch();
select
vehicleges0_.type as col_0_0_, count(vehicleges0_.pk) as col_1_0_
from vehicle vehicleges0_
group by vehicleges0_.type
order by col_1_0_ desc;
I did something like that, but I did count first before ordering. Look the query and the select generated.
That's a restriction imposed by SQL rather than by queryDSL.
You may try to run your suggested query in a DB console - I think it won't execute, at least not on every DB.
But I don't think this duplicated COUNT() really creates any performance overhead.

How do I create my query with Hibernate Criteria

I have to create the following query with Criteria:
SELECT r.*,(
SELECT COUNT(*) FROM APP.P_FORM_QUESTION_ANSWER qa
WHERE qa.J_STATUS = 'CORRECT'
AND qa.J_FORM_ID = r.J_FORM_ID AND qa.J_AUTHOR_ID = r.J_AUTHOR_ID
AND qa.J_FORM_RESULT_ID = r.J_ROW_ID
) as correctCount
FROM APP.P_FORM_RESULT r
WHERE r.J_FORM_ID = '123456'
ORDER BY correctCount DESC;
I try to use DetachedCriteria for extra column, but I do not see how to represent :
"qa.J_FORM_ID = r.J_FORM_ID AND qa.J_AUTHOR_ID = r.J_AUTHOR_ID
AND qa.J_FORM_RESULT_ID = r.J_ROW_ID"
in the DetachedCriteria, and add my DetachedCriteria as a new column
Criteria criteria =
HibernateUtil.getSession().createCriteria(FormResult.class, "formResult");
criteria.add(Restrictions.eq("formResult.formId", formId));
DetachedCriteria correctCount =
DetachedCriteria.forClass(QuestionAnswer.class, "questionAnswer");
correctCount.add(
Restrictions.eq("questionAnswer.status",QuestionAnswerStatus.CORRECT));
// How to retrieve 'formResult' ?
correctCount.add(Restrictions.eq("questionAnswer.formId", "formResult.formId"));
// How to retrieve 'formResult' ?
correctCount.add(Restrictions.eq("questionAnswer.authorId", "formResult.authorId"));
// How to retrieve 'formResult' ?
correctCount.add(
Restrictions.eq("questionAnswer.formResult.rowId", "formResult.rowId"));
correctCount.setProjection(Projections.rowCount());
// How to add my DetachedCriteria as a new column
criteria.setProjection(Projections.alias(correctCount, "correctCount"));
The Lines with comments are the lines for which I cannot find the solution.
Finally, I created a different SQL query that gives me the same result:
SELECT r.J_ROW_ID, COUNT(DISTINCT qa.J_ROW_ID) as correctCount
FROM APP.P_FORM_RESULT r left join APP.P_FORM_QUESTION_ANSWER qa on qa.J_FORM_ID = r.J_FORM_ID AND qa.J_AUTHOR_ID = r.J_AUTHOR_ID AND qa.J_FORM_RESULT_ID = r.J_ROW_ID AND qa.J_STATUS = 'CORRECT'
WHERE r.J_FORM_ID = '123456'
GROUP BY r.J_ROW_ID
ORDER BY correctCount DESC;
And I used HQL because I cannot find how to do it in Criteria:
// HQL Query to retrieve the list of FormResult IDs
StringBuilder hql = new StringBuilder();
hql.append("SELECT r.id");
hql.append(" FROM QuestionAnswer qa RIGHT JOIN qa.formResult as r");
hql.append(" WHERE r.formId = :formId AND (qa.status = :status OR qa.status IS NULL)");
hql.append(" GROUP BY r.id");
hql.append(" ORDER BY COUNT(DISTINCT qa.id) DESC"));
Query query = HibernateUtil.getSession().createQuery(hql.toString());
query.setParameter("formId", formId);
query.setParameter("status", QuestionAnswerStatus.CORRECT);
// Retrieve the list of FormResult objects from the list of IDs previously retrieved
List<Long> result = query.list();
if (result != null && !result.isEmpty()) {
for (Long resultId : result) {
formResults.add(getData(FormResult.class, resultId));
}
}
The downside is that I have to retrieve each FormResult one by one from my list of IDs retrieved by my HQL query

How to formulate a JOIN in a typed query?

I want to create a typed query.
TypedQuery<PubThread> query = em.createQuery(queryString, PubThread.class);
query.setParameter("threadId", threadId);
List<PubThread> otherPubThreads = query.getResultList();
In the queryString is the following SQL (currently without param and static selection values)
SELECT pt2 FROM pubthread pt2
JOIN pub_pubthread ppt2 ON pt2.id = ppt2.pubThreads_id
JOIN pub p2 ON ppt2.pups_id = p2.id
JOIN pubcategory pc2 ON p2.pubCategoryId = pc2.id
WHERE pt2.id != 1 and EXISTS (
SELECT DISTINCT(pt.id)
FROM pubthread pt
JOIN pub_pubthread ppt ON pt.id = ppt.pubThreads_id
JOIN pub p ON ppt.pups_id = p.id
JOIN pubcategory pc ON p.pubCategoryId = pc.id
WHERE pc2.id = pc.id and pt.id = 1
)
It does work, if I limit the String to a simple select: SELECT Distinct(pt2.id), pt2.name FROM pubthread pt2. As soon I add a JOIN line to it, it will complain. How do you properly query with JOINS in JPA? The error is:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ON near line 1, column 81 [SELECT pt2 FROM com.brayan.webapp.model.PubThread pt2 JOIN pub_pubthread ppt2 ON pt2.id = ppt2.pubThreads_id ]
Doubtless, a criteria query would be nicer. I accept that as part of the solution space.
When you call createQuery you have to write HQL but not SQL (your queryString is not HQL).
In HQL you have to join objects according to your mapping entities.
If you sill need SQL query use createNativeQuery method.
See documentation about how to create HQL query.
Got it. See below a fully example of joins. It consists of:
multiple joins (join-chaining)
a subquery
a predicate correlation/equation over join tables, other than the root table.
I also commented the obsolete code lines for other to see what a wrong approach.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery mainQuery = criteriaBuilder
.createQuery(PubThread.class);
// 1) MainQuery
// Create the FROM
Root<PubThread> rootPubThread = mainQuery.from(PubThread.class);
// Create the JOIN from the first select: join-chaining. You only need the return for ordering. e.g. cq.orderBy(cb.asc(categoryJoin.get(Pub_.title)));
Join<Pub, PubCategory> categoryJoin = rootPubThread.join(PubThread_.pups).join(Pub_.pubCategory);
// Create the WHERE
mainQuery.where(criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)));
// Create the SELECT, at last
mainQuery.select(rootPubThread).distinct(true);
// 2) Subquery
Subquery<PubThread> subquery = mainQuery.subquery(PubThread.class);
Root<PubThread> rootPubThreadSub = subquery.from(PubThread.class);
//subquery.where(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId));
Join<Pub, PubCategory> categoryJoinSub = rootPubThreadSub.join(PubThread_.pups).join(Pub_.pubCategory);
subquery.select(rootPubThreadSub);
//Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Predicate correlatePredicate = criteriaBuilder.and(
//criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread),
criteriaBuilder.equal(categoryJoinSub.get(PubCategory_.id), categoryJoin.get(PubCategory_.id)),
criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), threadId)
);
subquery.where(correlatePredicate);
//Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Predicate mainPredicate = criteriaBuilder.and(
criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)),
criteriaBuilder.exists(subquery)
);
//cq.where(criteriaBuilder.exists(subquery));
mainQuery.where(mainPredicate);

How do I rewrite this particular JPQL query with SELECT MAX() to CriteriaQuery?

I am quite new to JPA and I have encountered a problem with understanding one particular query. I have rewritten in to the CriteriaQuery but the result and the query translated to SQL is not correct.
Background situation: I have a table of store transaction (moves) and the current amount in the store is defined as a sum of all changes. Now I want to select and display the last moves as they also contain an information about the resulting amount on store.
So, this is the query in JPQL:
SELECT m FROM move m WHERE m.id = (
SELECT MAX(o.id) FROM move o WHERE (o.item = m.item AND m.cell.store = :s)
I tried to rewrite it to the following CriteriaQuery:
CriteriaBuilder builder = model.getCriteriaBuilder();
CriteriaQuery<Move> query = builder.createQuery(Move.class);
Root<Move> root = query.from(Move.class);
Subquery<Long> subquery = query.subquery(Long.class);
Root<Move> subroot = subquery.from(Move.class);
subquery.select(builder.max(subroot.get("id").as(Long.class)));
subquery.where(builder.and(
builder.equal(
subroot.get("item").get("id"),
root.get("item").get("id")),
builder.equal(
subroot.get("cell").get("store").as(Store.class),
store)));
Expression<Boolean> where = builder.equal(
root.get("id"),
subquery);
query.where(where);
return model.getList(query);
The incorrect SQL query produced is following:
SELECT t0.id, (...) FROM move t0 WHERE (t0.id = (
SELECT MAX(t1.id) FROM item t3, item t2, move t1
WHERE (((t2.id = t3.id) AND
(t1.cell_store = ?)) AND
((t2.id = t1.item_ref) AND
(t3.id = t0.item_ref))))
)
I do not understand why there is a double cross join in the subquery. Thank you for helping!

Categories