not all named parameters have been set hibernate in createSQLQuery - java

I am getting the error of not all named parameters have been set. Below is my code.
my SqlQuery which is running fine at mysql prompt, You can refer schema in the question SQL Query
SELECT t.*
FROM (
SELECT #lim := 2,
#cg := ''
) vars,
(select * from Table1 order by product,amount, make) t
WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0
AND (#r := #r - 1) >= 0
AND (#cg := product) IS NOT NULL
ORDER BY
product,amount, make
my java code
try {
context.dbl.startTransaction();
Session session = context.dbl.getSession();
//String sqlQuery = "from com.infibeam.inventoryservice.dbObjects.PopularBrandDO";
String sqlQuery = "SELECT t.* ";
sqlQuery=sqlQuery + "FROM (";
sqlQuery=sqlQuery + "SELECT #lim := 2,";
sqlQuery=sqlQuery + "#cg := ''";
sqlQuery=sqlQuery + ") vars, ";
sqlQuery=sqlQuery + "(select * from Table1 order by product,amount, make) t";
sqlQuery=sqlQuery + " WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0";
sqlQuery=sqlQuery + " AND (#r := #r - 1) >= 0 ";
sqlQuery=sqlQuery + " AND (#cg := product) IS NOT NULL ";
sqlQuery=sqlQuery + " ORDER BY product,amount, make";
//Query query = session.createQuery(sqlQuery);
SQLQuery query = session.createSQLQuery(sqlQuery);
listItems = query.list();
}catch(RuntimeException e) {
e.printStackTrace();
}
Below is the exception i am getting
org.hibernate.QueryException: Not all named parameters have been set: [] [SELECT t.* FROM (SELECT #lim := 2,#cg := '') vars, (select * from Table1 order by product,amount, make) t WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0 AND (#r := #r - 1) >= 0 AND (#cg := product) IS NOT NULL ORDER BY product,amount, make]
at org.hibernate.impl.AbstractQueryImpl.verifyParameters(AbstractQueryImpl.java:291)
at org.hibernate.impl.SQLQueryImpl.verifyParameters(SQLQueryImpl.java:199)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:143)
at com.infibeam.weaverbird.helper.PopularBrandFacetHelper.bootstrap(PopularBrandFacetHelper.java:48)
Thanks in advance...

The problem is the assignments with :=, which are by the way no standard SQL.
In SQL after a : always a parameter is expected, like in where value = :param and :param has the be set as a parameter then. Now hibernate is scanning the select and find colons where no set parameters follow.
Solution: Redesign your selection using hibernate standards.
You can use two different HQL queries.
First: Select all product: select distinct product from Table1
Second: For each product you do from Table1 where product = :prod, :prod you set as a parameter with the actual product, and with setMaxResults(2) you can limit the number of rows as you need.
Now it is many selects and not a single one, but nevertheless they might be faster than the single query (the single query is complicated and risks an inefficient search strategy in the database). And a big advantage, now it is purely HQL and so your program is portable to different databases.

Related

Alternative for COALESCE when all its parameters is null

I am developing a web application (java-maven project) and there is a problem with the query I use. The UI of my web app has 2 search fields: PTT and ID.
The user needs to fill at least one of the fields to make a search. So both fields are nullable but not at the same time.
Before, I had only one field: PTR and it was showing a result array of size 52. (also getting the same number if
I execute select * from users where ptr='smthing' ). After that I added ID field and updated my query as below:
I execute this query in my webservice:
String query= "SELECT t.ptr, t.id ";
query+= "FROM users t ";
query+= "WHERE t.ptr = COALESCE(?, t.ptr) AND " ;
query+= "t.id = COALESCE(?, t.id) ";
and set the fields with the help of Prepared Statement.
Now if the ptr field is filled, but id field is left blank (this can be null or empty string) on the UI and user makes a search, result array size becomes 30. I compared with database
and it does not fetch the rows where ID is null. So coalesce is not what I need when both of its parameters (?, t.ptr) is null.
How can I fix this problem, any suggestions?
I think the logic you want is:
WHERE (t.ptr = ? OR ? IS NULL) AND
(t.id = ? OR ? IS NULL)
I would recommend using named parameters, so you don't have to pass them in twice.
Check this statement:
String query= "SELECT t.ptr, t.id ";
query+= "FROM users t ";
query+= "WHERE (t.ptr = ? OR 1 = ?)"
query+= " AND " ;
query+= "(t.id = ? OR 1 = ?)";
You see that for each of t.id and t.ptr there is a counterpart parameter. In total there will be 4 parameters.
You say that at least 1 of t.id or t.ptr has a valid value, so there are 2 cases:
[1] t.id and t.ptr both have valid values.
For both the counterpart parameters you pass 0 and the query becomes:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr OR 1 = 0) AND (t.id = valueid OR 1 = 0)"
In the WHERE part:
t.ptr = valueptr OR 1 = 0 is equivalent to t.ptr = valueptr, and
t.id = valueid OR 1 = 0 is equivalent to t.id = valueid,
and the query finally becomes:
"SELECT t.ptr, t.id FROM users t WHERE t.ptr = valueptr AND t.id = valueid"
[2] from t.id or t.ptr only one has a valid value, let's say this is t.ptr.
For the counterpart of t.ptr you pass 0, for t.id you pass -1 (or any other non existing value) and for the counterpart of t.id you pass 1 and the query becomes:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr OR 1 = 0) AND (t.id = -1 OR 1 = 1)"
In the WHERE part:
t.ptr = valueptr OR 1 = 0 is equivalent to t.ptr = valueptr, and
t.id = -1 OR 1 = 1 is equivalent to true because 1 = 1 is always true,
and the query finally becomes:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr OR 1 = 0)"
equivalent to:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr)"
(In the case where only t.id has a valid value then you pass an invalid value for t.ptr and 1 for its counterpart and for the counterpart if t.id you pass 0.)
Maybe it seems complicated but it's working and it can be extended for more than 2 columns.
Oracle has build function for this. But it's hard to understand how to use its.
lnnvl(a = b) = true if (a != b ) or ( a = null ) or (b = null)
in your case
WHERE lnnvl(t.ptr != ? ) AND lnnvl( t.id != ?)
LNNVL

How to group by Case statement using JPA Criteria API / Hibernate

I am trying to perform a query like the following, with selecting by a case statement and grouping by the same case statement..
Select USER,
(CASE
WHEN value between 0 AND 2 then '0-2'
WHEN value between 3 AND 4 then '3-4'
ELSE '5+'
END) as CASE_STATEMENT ,
SUM(value)
.....
Group by user, CASE_STATEMENT
using JPA 2.0 Criteria API, with Hibernate.
My test case looks like ...
CriteriaBuilder cb = em.getCriteriaBuilder()
CriteriaQuery cq = cb.createQuery(Tuple)
def root = cq.from(TestEntity)
def userGet = root.get('user')
def valueGet = root.get('value')
def caseExpr =
cb.selectCase()
.when(cb.between(valueGet, 0, 2), '0-2')
.when(cb.between(valueGet, 3, 4), '3-4')
.otherwise('5+')
def sumExpr = cb.sum(valueGet)
cq.multiselect([userGet, caseExpr, sumExpr])
cq.groupBy([userGet, caseExpr])
log(typedQuery.unwrap(Query).queryString)
List<Tuple> tuples = typedQuery.getResultList()
The log statement of the queryString reads
SELECT generatedAlias0.USER,
CASE
WHEN generatedAlias0.value BETWEEN 0 AND 2 THEN Cast(:param0 AS STRING)
WHEN generatedAlias0.value BETWEEN 3 AND 4 THEN Cast(:param1 AS STRING)
ELSE Cast(:param2 AS STRING)
END,
Sum(generatedAlias0.value)
FROM test AS generatedAlias0
GROUP BY generatedAlias0.USER,
CASE
WHEN generatedAlias0.value BETWEEN 0 AND 2 THEN Cast(
:param3 AS STRING)
WHEN generatedAlias0.value BETWEEN 3 AND 4 THEN Cast(
:param4 AS STRING)
ELSE Cast(:param5 AS STRING)
END
When calling the typedQuery.getResultList(), I get the following error statement
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not extract ResultSet
Caused by: org.h2.jdbc.JdbcSQLException: Column "TESTENTITY0_.VALUE" must be in the GROUP BY list; SQL statement:
select testentity0_.user as col_0_0_, case when testentity0_.value between 0 and 2 then cast(? as varchar(255)) when testentity0_.value between 3 and 4 then cast(? as varchar(255)) else cast(? as varchar(255)) end as col_1_0_, sum(testentity0_.value) as col_2_0_ from test testentity0_ group by testentity0_.user , case when testentity0_.value between 0 and 2 then cast(? as varchar(255)) when testentity0_.value between 3 and 4 then cast(? as varchar(255)) else cast(? as varchar(255)) end [90016-194]
Is there something wrong with the way I am trying to group by the Expression? I have also tried grouping by alias names, and by number literals (1, 2)
Is there another way I can go about structuring the SQL to get the same results?
Thanks.
As the exception message suggests, the problem is related to the Group By statement at DBMS level. See: https://www.percona.com/blog/2019/05/13/solve-query-failures-regarding-only_full_group_by-sql-mode/
To solve the error, you must either
Set the Group By Mode of the underlying DBMS to a less restrictive level (MySQL allows to disable only-full-group-by, but H2 does not (you may try setting MODE=MYSQL in jdbc connection string)
or (better)
Add all columns that are part of the select statement to the GROUP BY statement or to an aggregate function as described above.
You should be able to build a nested query which fulfills the GROUP BY RESTRICTIONS.
For the rescue, there are some (maybe DBMS specific) aggregate
functions (at least in MySQL). To trick JPA and Hibernate to understand these, there are
several ways to achieve this, as described at
https://vladmihalcea.com/hibernate-sql-function-jpql-criteria-api-query/
and
https://vladmihalcea.com/the-jpa-entitymanager-createnativequery-is-a-magic-wand/
Edit
In contrast and addition to the statement above, the findings after discussion below are:
The exception is raised by the h2 driver in the org.h2.expression.ExpressionColumn class, while it's verifying the query syntax
The solution requires setting and referencing an alias in the query (at the case statement or subquery), which is currently not possible in Criteria API (see column aliases usually can't be referenced in the query itself)
A workaround would be creating of a NativeQuery like this:
List<Tuple> tuples = em.createNativeQuery(
"SELECT generatedAlias0.USER, " +
" CASE " +
" WHEN generatedAlias0.value BETWEEN 0 AND 2 THEN Cast(:param0 AS VARCHAR) " +
" WHEN generatedAlias0.value BETWEEN 3 AND 4 THEN Cast(:param1 AS VARCHAR) " +
" ELSE Cast(:param2 AS VARCHAR) " +
" END c, " +
" Sum(generatedAlias0.value) as sumvalue " +
"FROM test AS generatedAlias0 " +
"GROUP BY generatedAlias0.USER, c "
)
.setParameter("param0", "0-2")
.setParameter("param1", "3-4")
.setParameter("param2", "5+")
.getResultList();

hibernate native query variable

I am trying to write native query:
#NamedNativeQuery(name = "getUncheckedTests",
query = "SELECT t.test_name AS test_name, q.question AS question, #counter \\:= COUNT(ua.is_checked=0) AS uncheckedAnswers FROM user_answer ua" +
" JOIN result r on r.id = ua.result_id" +
" JOIN test t on r.test_id = t.id" +
" JOIN question q on ua.question_id = q.id" +
" WHERE r.is_checked = false AND q.is_open = true AND ua.is_checked = false" +
" AND r.test_id IN (SELECT t.id FROM result r" +
" JOIN test t on t.id = r.test_id" +
" JOIN user u on u.id = r.user_id" +
" WHERE r.permission = 2 AND u.id = :userId) and r.permission = 1 and #counter > 0" +
" GROUP BY ua.question_id")
However, i am getting empty result as #counter > 0 does not work properly. I can execute this query right in Workbench, however no result if it's put in Hibernate as native query
You should probably check the actual SQL Hibernate executes by enabling logging of the statements or by using log4jdbc and make sure you execute the query with the proper parameters.

Can I add amounts from multiple tables in hql

I would like to perform a query in HQL similar to this in SQL:
SELECT (
SELECT COUNT(*) FROM EMPLOYER +
SELECT COUNT(*) FROM COMPANIES
)
FROM DUAL
When I add "FROM DUAL" to the query I get an error:
org.hibernate.hql.ast.QuerySyntaxException: DUAL is not mapped
And if I leave off the "FROM DUAL" I get:
org.hibernate.hql.ast.QuerySyntaxException: unexpected end of subtree
Any recommendations?
In SQL, subqueries need their own set of parentheses, so try this:
SELECT ((SELECT COUNT(*) FROM EMPLOYER) +
(SELECT COUNT(*) FROM COMPANIES)
)
If that doesn't work, resort to a from clause:
SELECT e.cnt + c.cnt
FROM (SELECT COUNT(*) as cnt FROM EMPLOYER
) e CROSS JOIN +
(SELECT COUNT(*) as cnt
FROM COMPANIES
) c
Since you didn't join the Company and Employee tables you'd better run two queries:
int companyCount = ((Number) getSession().createQuery("select count(*) from Company").uniqueResult()).intValue();
int employeeCount = ((Number) getSession().createQuery("select count(*) from Employee").uniqueResult()).intValue();
int totalCount = companyCount + employeeCount;
The (Number) is needed since PostgreSQL returns a BigInteger while HDSQLDB returns an Integer, but both are subclasses of Number.

sql "between statement" not working with spring

I have this sql which works fine(the result is given) when I execute the statement via the sql manager console.
SELECT * FROM (
SELECT s.*, #rank
:= #rank + 1 rank
FROM
quiz.score s, (SELECT #rank := 0) init
ORDER BY points DESC
) s
WHERE rank BETWEEN (select count(id) from score)-(10) AND (select count(id) from score)
ORDER BY rank;
If I try to execute the script via spring the result is always empty:
public List<Score> loadAllScoreLast(String pFrom, String pTo) {
return createJdbcTemplate().query(mLAUSFT, new Object[] { pFrom, pTo },
mScoreMapper);
}
pFrom is in this case (select count(id) from score)-(10) and pTo is (select count(id) from score)
I think I include pFrom and pTo the wrong way.
That is the original sql:
SELECT * FROM (
SELECT s.*, #rank
:= #rank + 1 rank
FROM
quiz.score s, (SELECT #rank := 0) init
ORDER BY points DESC
) s
WHERE rank BETWEEN ? AND ?
ORDER BY rank;
Could someone help me with that issue?
Thx in advance
Stefan
Why are pFrom and pTo strings? I guess it is some problem with the types and/or with the placeholders because the rest looks valid.
http://sqlfiddle.com/#!2/45e1b/1
You should check what plain SQL is generated.
I guess it might look like this (note the quotes).
SELECT * FROM (
SELECT s.*, #rank
:= #rank + 1 rank
FROM
quiz.score s, (SELECT #rank := 0) init
ORDER BY points DESC
) s
WHERE rank BETWEEN '(select count(id) from score)-(10)' AND '(select count(id) from score)'
ORDER BY rank;

Categories