Projections.countDistinct with Hibernate produces unexpected result - java

I have the following code
Criteria criteria = this.getCriteriaForClass(DeviceListItem.class);
Projection rowCountProjection = Projections.countDistinct("color");
criteria.setProjection(rowCountProjection);
int rowCount = ((Long) criteria.uniqueResult()).intValue();
return rowCount;
, whose purpose is to find out the number of rows with different values for the field named "color". The problem is that
Projections.countDistinct("color");
returns the same number of results as
Projections.count("color");
even though there are multiple rows with same color in the database view. When converting the Criteria object to SQL, I see that the SQL produced by Hibernate is
select count(this_.COLOR) as y0_ from DEVICESLIST_VIEW this_ where 1=1
when I would expect it to be
select count(distinct this_.COLOR) as y0_ from DEVICESLIST_VIEW this_ where 1=1
Why doesn't it work like expected and is there some remedy? Unfortunately I have no option to use HQL in this case.

It's a bug, fixed in 3.5.2: HHH-4957.

Related

Java Spring JPA: How to find the sum of only non null values in a column?

I currently have a query like this:
SELECT DISTINCT t.column1, SUM(t2.column2 IS NOT NULL)
FROM table t
LEFT OUTER JOIN table t2 on table.id = t2.id
GROUP BY column1, column2;
I am trying to implement the query using Spring JPA CriteriaBuilder. I see the CriteriaBuilder.sum() method, but I don't see a way to apply the IS NOT NULL part to the selection. Column2's data type is string.
Sample of my code
criteriaBuilder.multiselect(root.get("column1"), cb.sum(root.get("column2")));
Only in MySQL would such a query run, due MySQL’s relaxed syntax rules.
In particular, in mysql sum(column2 is not null) is a count, not a sum. The expression column2 is not null is boolean and in mysql false is 0 and true is 1, so summing this expression is a mysql hack to count the number of times column2 is not null.
To convert it to standard sql:
select
t.column1,
count(t2.column2)
from table t
left join table t2 on t.id = t2.id
group by t.column1
This works because count() (and all aggregate functions) ignore nulls.
This version also corrects the errant column in the group by clause - in any other database, your query would have produced a “grouping by aggregate expression” error.
This query will produce the same result in MySQL as your current query.
I was able to find a solution to my problem. Thanks to #bohemian for helping me write a correct sum expression.
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Model1> cq = cb.createQuery(Model1.class);
final Root<Model1> root = cq.from(Model1.class);
final Join<Model1, Model1> selfJoin =
root.join("tableJoinColumn", JoinType.LEFT);
selfJoin.on(...);
cq.multiselect(root.get("column1"), cb.sum(cb.selectCase()
.when(cb.isNull(selfJoin.get("column2")), 0).otherwise(1).as(Long.class)));
...
The self join required me to create an additional property on my model.
Model1.java
/**
* Property for LEFT INNER JOIN.
*/
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="id")
private Model1 tableJoinColumn;
How to use JPA CriteriaBuilder selectCase() so that it can have Predicate as result?
Self join in criteria query

hibernate criteria group Query is wrong

I am trying to generate the following query using Criteria:
select order_number, order_date, sum(order_amount)
from st_pur_orders
group by ORDER_NUMBER,ORDER_DATE;
Here is my Criteria:
Criteria cr = session.createCriteria(StPurOrders.class);
cr.setProjection(Projections.projectionList()
.add(Projections.property("orderNumber"), "orderDate")
.add(Projections.sum("orderAmount"))
.add(Projections.groupProperty("orderNumber"))
.add(Projections.groupProperty("orderDate")));
cr.setResultTransformer(Transformers.aliasToBean(PurOrderColl.class));
list = cr.list();
But the query getting generate in the background is this:
select this_.ORDER_NUMBER as y0_, sum(this_.ORDER_AMOUNT) as y1_,
this_.ORDER_NUMBER as y2_, this_.ORDER_DATE as y3_
from STOCK.ST_PUR_ORDERS this_
group by this_.ORDER_NUMBER, this_.ORDER_DATE;
My Question is why is the ORDER_NUMBER field being listed twice?
My Question is why is the ORDER_NUMBER field being listed twice?
Your Criteria already has Projections.groupProperty("orderNumber"). So the generated query will include orderNumber in its select clause.
I don't see a need for specifying Projections.property("orderNumber") explicitly. Can you please remove that and try.
Same applies for any other field that is specified using groupProperty(..).

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

ResultSetMetaData.getColumnName returns different values for UNION and non-UNION queries

Using Java JDBC, I want to collect information returned from SQL Select query.
If I fire below SQL query:
SELECT col1 AS 'Field1', col2 AS 'Field2' FROM Table;
Then, using resultSetMetaData.getColumnName(1), I get 'col1' as result, which is the expected result.
Now, the problem is, when I join 2 SQL tables (Since, MySQL does not provide Full Outer Join, hence, I fired the following query)
SELECT Table1.Col1 AS 'Field1', Table1.Col3 AS 'Field2',
Table2.Col5 AS 'Field3',Table2.Col4 AS 'Field4' FROM Table1
LEFT JOIN Table2 ON Table1.id = Table2.id
UNION
SELECT Table1.Col1 AS 'Field1', Table1.Col3 AS 'Field2',
Table2.Col5 AS 'Field3',Table2.Col4 AS 'Field4' FROM Table1
RIGHT JOIN Table2 ON Table1.id = Table2.id;
Now, using resultSetMetaData.getColumnName(1), I get 'Field1' as result, where as, I expected 'col1'.
I tried resultSetMetaData.getColumnLabel(1) also, but it still returned 'Field1'.
I want 'col1' as the result, which I could not get by any of the methods of resultSetMetaData.
Any help on this will be appreciable.
You are seeing those results because it is a UNION query. It is entirely possible that such a query could do something like
SELECT Col1 AS Field1 FROM Table1
UNION
SELECT Col2 AS Field1 FROM Table2
In that case there is no single "correct" answer if getColumnName was to try and return the name of the underlying column in the result: Should it return 'Col1' or 'Col2'?
Since any column in the result set of a UNION query can be derived from more than one underlying column, getColumnName can only return the effective name of that column, which is Field1 in the example above.

SQL subquery in Hibernate Query Language

I am new to HQL and I am working on subqueries.
I have the following SQL subquery:
select * from (
select * from table order by columnname
) as subquery
where columnvalue = 'somevalue';
I want to fire the query in HQL. I wrote the below code :
Result = session.createQuery("from (from table order by columnname) as subquery where columnvalue = :somevalue")
.setParameter(/*setting all parameters*/)
.list();
I am getting this exception:
QuerySyntaxException : unexpedted token :( line 1, column 10 [from (from ...)]
My SQL query is giving me correct results. How do I write it in HQL ?
I did not think HQL could do subqueries in the from clause
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-subqueries
note the sentence:
Note that HQL subqueries can occur only in the select or where clauses.
It will be better if you excute native SQL.

Categories