How do I create my query with Hibernate Criteria - java

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

Related

Is there any spring jpa equivalent to following query

Query :
#Query("Select p.name,t.points from Player p,Tournament t where t.id=?1 And p.id=t.player_id")
I have my player and tournament entity and their corresponding JPA repositories. But the problem is we can get only entities from our query, but i want to do above query, please help me with this i am new to it.
this is my sql query i want to add but where to add i am not getting:
Select p.name, t.points_rewarded from player p, participant t where t.tournament_id="1" and t.player_id=p.id;
This is how you can do it with JPQL for JPA:
String queryString = "select p.name, t.points from Tournament t," +
" Player p where t.player_id=p.id " +
"and t.id= :id_tournament";
Query query = this.entityManager.createQuery(queryString);
query.setParameter("id_tournament", 1);
List results = query.getResultList();
You can take a look at this JPA Query Structure (JPQL / Criteria) for further information about JPQL queries.
And this is ho you can do it using HQL for Hibernate, these are two ways of doing it:
String hql = "SELECT p.name, t.points from Player p,Tournament t WHERE t.id= '1' And p.id=t.player_id";
Query query = session.createQuery(hql);
List results = query.list();
Or using query.setParameter() method like this:
String hql = "SELECT p.name, t.points from Player p,Tournament t WHERE t.id= :tournament_id And p.id=t.player_id";
Query query = session.createQuery(hql);
query.setParameter("tournament_id",1);
List results = query.list();
You can take a look at this HQL Tutorial for further information about HQL queries.
Note:
In both cases you will get a list of Object's array List<Object[]> where element one array[0] is the p.name and the second one is t.points.
TypedQuery instead of normal Query in JPA
this is what i was looking for, thanks chsdk for help, i have to create pojos class, and in above link answer is working fine foe me,
Here is my code sample
String querystring = "SELECT new example.restDTO.ResultDTO(p.name,t.pointsRewarded) FROM Player p, Participant t where t.tournamentId=?1 AND t.playerId = p.id ORDER by t.pointsRewarded DESC";
EntityManager em = this.emf.createEntityManager();
try {
Query queryresults = em.createQuery(querystring).setParameter(1, tournamentId);
List<ResultDTO> result =queryresults.getResultList();
return new ResponseEntity<>(result, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} finally {
if (em != null) {
em.close();
}}

How to join 2 tables based on passing a parameter for search operation?

I have 2 entities Addemp and Job.I want to join these 2 tables based on empid .empid is foreign key in job table and primary key in addemp table
here i am doing a search operation based on employee id .i am using criteria builder for search operation
the relationship here is manytoone
public List<Object[]> findEmployeeList(Integer id)
{
EntityManager em=null;
try
{
em=getEntityManager();
CriteriaBuilder cb=em.getCriteriaBuilder();
CriteriaQuery cq=cb.createQuery(Addemp.class);
Root<Addemp>rt= cq.from(Addemp.class);
Join<Addemp,Job> job=rt.join(Addemp_.jobCollection);
Predicate predicate=cb.equal(rt.get("empId"),id);
cq.where(predicate);
/* cq.select(rt.get("firstName"));
cq.where (cb.equal(rt.<String>get("empId"),id));*/
Query qry= em.createQuery(cq);
return qry.getResultList();
Based on the question and JPQL query from the above comments here is the criteria query proposal:
public List<Tuple> findEmployeeList(Integer id)
{
em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Addemp> empRoot = cq.from(Addemp.class);
Root<Job> jobRoot = cq.from(Job.class);
cq.multiselect(empRoot.get("empId").alias("id"),
empRoot.get("firstName").alias("first"),
empRoot.get("lastName").alias("last"),
jobRoot.get("jobTitle").alias("title"),
jobRoot.get("empStatus").alias("status"),
jobRoot.get("subUnit").alias("subunit"));
cq.where(cb.equal(empRoot.get("empId"), id));
TypedQuery<Tuple> q = em.createQuery(cq);
return q.getResultList();
}
Next you may want to extract a tuple result:
List<Tuple> tuples = service.findEmployeeList(2);
for (Tuple t : tuples) {
StringBuilder builder = new StringBuilder(64)
.append(t.get("id")).append(", ")
.append(t.get("first")).append(", ")
.append(t.get("last")).append(", ")
.append(t.get("title")).append(", ")
.append(t.get("status")).append(", ")
.append(t.get("subunit"));
System.out.println(builder.toString());
}
I hope it helps.

Why does JPA Criteria Query not allow to correlate a subquery?

I need to match the main query with a subquery with the ID of category. JPA Criteria Query does not allow me to set the Predicate to the Category because the query is returning types PubThread.
Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Below the entire criteria query.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<PubThread> cq = criteriaBuilder.createQuery(PubThread.class);
// 1) MainQuery
// Create the FROM
Root<PubThread> rootPubThread = cq.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
cq.where(criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)));
// Create the SELECT, at last
cq.select(rootPubThread).distinct(true);
// 2) Subquery
Subquery<PubThread> subquery = cq.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);
subquery.where(correlatePredicate);
cq.where(criteriaBuilder.exists(subquery));
How do you formulate this Criteria Query to match this SQL code? I think I'm pretty close.
SELECT Distinct(pt2.id), pt2.name
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
)
I assigned the wrong object to the predicate. If you want to match a subquery's id to the parent id, you must assign the Join, not the Root, if predicates do not belong to the root. Well, how obvious is that. :) However, glad to share. Below is the new query. There is an additional Predicate at the very bottom, which solves the issue. Also in conjunction with another boolean statement.
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);
TypedQuery<PubThread> typedQuery = em.createQuery(mainQuery);
List<PubThread> otherPubThreads = typedQuery.getResultList();

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

EclipseLink Criteria API count in results after joining table

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

Categories