Hibernate Criteria with condition on count - java

I need the equivalent of
SELECT m.id, count(i.id)
FROM master m LEFT JOIN item i on m.id = i.master_id
GROUP BY m.id, m.size
HAVING m.size <> count(i.id);
in Hibernate Criteria. Thanks to this question, I know how to get the grouped result as a list of Object[]:
ProjectionList projList = Projections.projectionList();
projList.add(Projections.groupProperty("master"));
projList.add(Projections.count("id"));
session
.createCriteria(Item.class)
.join("master")
.setProjection(projList)
.addRestriction(???) // <- my HAVING clause
.list();
I have no clue how to add the HAVING clause. I guess, it's something like Restrictions.eqProperty, but how can I refer to the count?
Is there a way how to refer to the resulting tuple elements in the query?

Hibernate Criteria API does not support HAVING clauses. Since it is deprecated anyway in newer Hibernate versions, I suggest you move to JPA Criteria API, or use HQL/JPQL or more advanced wrappers like Querydsl JPA.

You can use a sqlRestriction how workaround, something like:
Restrictions.sqlRestriction("1=1 group by this_.id, this_.size HAVING this_.size <> count(i_.id));
Here is a example:
ct.setProjection(Projections.sqlProjection(
"cobr_.client_id as clientID"
, new String[] {"clientID" }
, new Type[] { new LongType()}));
ct.add(Restrictions.sqlRestriction("1=1 group by cobr_.vlr_total,clientID having (sum(this_.vlr_net)-cobr_.vlr_total) < -20"));

Related

Create same join multiple times hibernate criteria

I am trying to create a query that uses the same join multiple times. An example would look as follows:
select * from a
join b b1 on b1.id=a.id
join c c1 on c1.id=b1.id
join b b2 on b2.id=a.id
join c c2 on c2.id=b2.id
where b1.attribute = "a" and c1.attribute = "b"
where b2.attribute = "c" and c2.attribute = "d"
I need to do this utilizing Hibernate Criteria. I was thinking that something like this should work:
Criteria criteria = createCriteria(a.class, "a"); //a would be the class representing table "a"
Criteria criteria1 = criteria.createAlias(b.class, "b1").createAlias(c.class, "c1");
Criteria criteria2 = criteria.createAlias(b.class, "b2").createAlias(c.class, "c2");
However, I am getting a QueryException: duplicate assocation path exception. I have seen this so: Hibernate Create Criteria to join the same table twice - tried 2 approach with 2 difference error, but this solution doesn't work for me I think, and it also implies it is not possible. Has anyone else had better luck? Thanks!
I think that is not possible with the legacy Hibernate Criteria API. You should rather use the JPA Criteria API, especially because the legacy Hibernate Criteria API was deprecated in Hibernate 5 and removed in Hibernate 6.

Is this query possible using Criteria or DetachedCriteria Hibernate

The question is simple can this query be done in Hibernate using Criteria or DetachedCriteria? i guess not but i wanted to do this question maybe exist a workaround.
SELECT
COLUMNS
FROM table
WHERE id not in (
SELECT * FROM (
SELECT id
FROM table
WHERE
SOMECONDITIONS
ORDER BY timestamp desc limit 0, 15
)
as t);
I will mark the answer by #Dean Clark as correct but another question arises is the following
where can i find the findByCriteria from SessionFactory we are not using Spring
To exactly match you query, you really need to do it with 2 steps, but I'd avoid this if possible:
final Criteria innerCriteria = getSession().createCriteria(YourEntity.class);
// SOME CONDITIONS
innerCriteria.add(Restrictions.eq("someColumn", "someValue"));
innerCriteria.addOrder(Order.desc("timestamp"));
innerCriteria.setMaxResults(15);
innerCriteria.setProjection(Projections.id());
List<YourIdClass> ids = innerCriteria.list();
final Criteria criteria = getSession().createCriteria(YourEntity.class);
criteria.add(Restrictions.not(Restrictions.in("id", ids)));
List<YourEntity> results = criteria.list();
Would the objects you're trying to identify have the same "SOMECONDITIONS"? If so, this would functionally accomplish what you're looking for:
final DetachedCriteria criteria = DetachedCriteria.forClass(YourEntity.class);
// SOME CONDITIONS
criteria.add(Restrictions.eq("someColumn", "someValue"));
criteria.addOrder(Order.desc("timestamp"));
getHibernateTemplate().findByCriteria(criteria, 16, 9999999);

Hibernate 2 with MSSQL for ORDER BY

I have been working with Oracle and Postgre and recently switched to MS SQL 2012.
I use hibernate in my application and wherever I have used the Order by Criteria:
(criteria.addOrder(Order.asc("applicationId")));
It causes an error saying:
aggregate functions dont work.
Once I comment that line out my program works and data can be retrieved.
I'm using Hibernate 3.
Is there any way to order it through hibernate without this error?
edit..
This is one error I get,
Column "SKY.tcrent.RENTNO" is invalid in the ORDER BY clause because
it is not contained in either an aggregate function or the GROUP BY
clause.
Edit 2..
MY query
Query tcSchaduleQ = getSession().createQuery("SELECT SUM(tcs.dueAmount) FROM TrialCalculationSchedule tcs WHERE tcs.facilityId=:facilityId AND tcs.rentalNumber>:rentalNumber AND tcs.dueDate>:dueDate AND dueTypeId IN(:dueTypeId) ORDER BY tcs.rentalNumber ").setInteger("rentalNumber", facility.getPeriod() - noOfprePayments).setInteger("facilityId",facility.getFacilityId()).setDate("dueDate", date).setParameterList("dueTypeId", plist);
Number tcsAmt = (Number) tcSchaduleQ.uniqueResult();
and this is what hibernate generates in HQL
SELECT
SUM(tcs.dueAmount)
FROM
TrialCalculationSchedule tcs
WHERE
tcs.facilityId=:facilityId
AND tcs.rentalNumber>:rentalNumber
AND tcs.dueDate>:dueDate
AND dueTypeId IN(
:dueTypeId
)
ORDER BY
tcs.rentalNumber
and this is the SQL
select
SUM(trialcalcu0_.DUEAMT) as col_0_0_
from
SKYBANKSLFHP.tcrent trialcalcu0_
where
trialcalcu0_.FACID=?
and trialcalcu0_.RENTNO>?
and trialcalcu0_.DUEDATE>?
and (
trialcalcu0_.DUETYPEID in (
? , ?
)
)
order by
trialcalcu0_.RENTNO
Look Like you mix aggregate and non-aggregate expressions .If you are using any aggregate function like AVG() in Select query with some other non-aggregate then you must use Group By ..
Try something like this
createQuery("SELECT SUM(tcs.dueAmount) As DueAmount ...
If you are using Criteria then it should be like this
Criteria crit = sess.createCriteria(Insurance.class);
ProjectionList proList = Projections.projectionList();
proList.add(Projections.sum("investementAmount"));
crit.setProjection(proList);
List sumResult = crit.list();

UNION to JPA Query

Is it possible to query "UNION" in JPA and even "Criteria Builder"?
I'm looking for examples, but so far i got no result.
Does anyone have any examples of how to use it?
Or would that be with native sql?
SQL supports UNION, but JPA 2.0 JPQL does not. Most unions can be done in terms of joins, but some can not, and some are more difficult to express using joins.
EclipseLink supports UNION.
Depending on the case, one could use sub queries, something like:
select e
from Entity e
where e.id in
(
select e.id
from Entity2 e2
join e2.entity e
where e2.someProperty = 'value'
)
or e.id in
(
select e.id
from Entity3 e3
join e3.entity e
where e3.someProperty = 'value2'
)
One thing just comes to my mind (searching for the exact same problem):
Perform two different JPA queries on the same entity mapping and simply add the objects of the second result to the list (or set to avoid duplicates) of the first result.
That way you get the same effect as with a UNION, the difference being that you use two SQL statements instead of one. But actually, I would expect that to perform just as good as issueing one UNION statement.
write native query (set it true , default its false) - ex.
String findQuery = "select xyz from abc union select abc from def"
#Query(value = findQuery, nativeQuery = true)
//method
using EntityManager.createNativeQuery(...);
It's allow you use UNION
There is no direct union for JPA, what I did was to build two specifications.
Specification<Task> specification = Specification.where(null);
Specification<Task> specification2 = Specification.where(null;
They belong to a single table but return different values
specification = specification.and((root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(criteriaBuilder.function(MONTH, Integer.class, root.get("deliveryExpirationDate")), month));
specification2 = specification2.and((root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.lessThan(criteriaBuilder.function(MONTH, Integer.class, root.get("deliveryExpirationDate")), month))
.and((root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("enable"), true));
for this example it is a table of tasks that in the first specification I need the tasks of the current month enabled and disabled, and in the second specification I only need the tasks enabled of the previous months.
Specification<Task> specificationFullJoin = Specification.where(specification).or(specification2);
Esto es muy útil para que la lista de tareas devueltas tenga paginación.
taskRepository.findAll(specificationFullJoin, pageable).map(TaskResponse::new); //Here you can continue adding filters, sort or search.
It helps me a lot, I hope it is what they are looking for or that it serves them something.
I have solved this in my project.
Union/Union All will work if you change it to native query and use like below
//In Your Entity class
#NamedNativeQuery(name="EntityClassName.functionName",
query="your_native_query")
//In your Repository class
#Query(native=true)
List<Result> functionName();
Below method of defining Native query in JPA repository will not solve this problem
#Query(value="your_native_query", native=true)
will not
You can directly use UNION in your query.
The following query returns id of friends from FRIENDSHIP table.
Eg:
TypedQuery<String> queryFriend = em.createQuery("SELECT f.Id FROM Friendship f WHERE f.frStuId = :stuId UNION "
+ "SELECT f.frStuId FROM Friendship f WHERE f.FriendId = :stuId", String.class);
queryFriend.setParameter("stuId", stuId);
List<String> futureFriends = queryFriend.getResultList();

Need help creating JPA criteria query

I'm building my first Java EE web application using Glassfish and JSF. I'm fairly new to the criteria query and I have a query I need to perform but the javaee6 tutorial seems a little thin on examples. Anyway, I'm having a hard time creating the query.
Goal: I want to pull the company with the most documents stored.
Companies have a OneToMany relationship with Documents.
Documents has a ManyToOne relationship with several tables, the "usertype" column distinguishes them.
MySQL query:
SELECT USERID, COUNT(USERID) AS CNT
FROM DOCUMENTS
WHERE USERTYPE="COMPANY"
GROUP BY USERID
ORDER BY CNT DESC
Thanks
--update--
Based on user feedback, here is what I have so far:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Documents> cqry = cb.createQuery(Documents.class);
//Intersting Stuff
Root<Documents> root = cqry.from(Documents.class);
Expression userid = root.get("userID");
Expression usertype = root.get("userType");
Expression count = cb.count(userid);
cqry.multiselect(userid, count);
Predicate userType = cb.equal(usertype, "COMPANY");
cqry.where(userType);
cqry.groupBy(userid);
cqry.orderBy(cb.desc(count));
//more boilerplate
Query qry = em.createQuery(cqry);
List<Documents> results = qry.getResultList();
The error I get is:
Exception Description: Partial object queries are not allowed to maintain the cache or be edited. You must use dontMaintainCache().
Typical error, means nothing to me!
Your query doesn't return a complete entity object as you're only selecting two fields of the given table (this is why you're getting an error that says yadayadapartialyadayada).
Your solution is almost right, here's what you need to change to make it work—making it partial.
Instead of a plain CriteriaQuery<...> you have to create a tuple CriteriaQuery<..> by calling CriteriaBuilder.createTupleQuery(). (Basically, you can call CriteriaBuilder.createQuery(...) and pass Tuple.class to it as an argument. Tuple is a sort of wildcard entity class.)
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq= cb.createTupleQuery();
Root<Documents> root = cq.from(Documents.class);
Expression<Integer> userId = root.get("USERID");
Expression<String> userType = root.get("USERTYPE");
Expression<Long> count = cb.count(userId);
cq.multiselect(userId.alias("USERID"), count.alias("CNT"));
cq.where(cb.equal(userType, "COMPANY");
cq.groupBy(userId);
cq.orderBy(cb.desc(count));
TypedQuery<Tuple> tq = em.createQuery(cq);
for (Tuple t : tq.getResultsList()) {
System.out.println(t.get("USERID"));
System.out.println(t.get("CNT"));
}
(Accessing fields of a Tuple gave me an error if I didn't use aliases for them (in multiselect(...)). This is why I've used aliases, but this can be tackled more cleanly by using JPA 2's Metamodel API, which is described in the specification quite thoroughly. )
The documentation for CriteriaQuery.multiselect(...) describes the behaviour of queries using Tuple objects more deeply.
If you are using Hibernate, this should work:
ProjectionList pl = Projections.projectionList()
.add(Projections.groupProperty("userid"))
.add(Projections.property("userid"))
.add(Projections.count("userid"));
Criteria criteria = session.createCriteria(Document.class)
.add(Restrictions.eq("usertype",usertype))
.setProjection(pl)
.addOrder(Order.desc("cnt"));
Hope it helps!
Take a look into this easy tutorial. It uses JPA2 and Criteria
http://www.jumpingbean.co.za/blogs/jpa2-criteria-api
Regards!
You need to add a constructor to Documents with only userid and count because you will need it on:
cqry.multiselect(userid, count);

Categories