How can I use MySQL assign operator(:=) in hibernate native query? - java

I'm using Hibernate. I wrote some native query because I need to use sub select statement.
Query looks like this:
SELECT sub.rownum FROM
(SELECT k.`news_master_id` AS id, #row := #row + 1 AS rownum
FROM keyword_news_list k
JOIN (SELECT #row := 0) r
WHERE k.`keyword_news_id` = :kid
ORDER BY k.`news_master_id` ASC) AS sub
WHERE sub.id = :nid
When I run this query like this:
sessionFactory.getCurrentSession()
.createSQLQuery(query)
.setParameter("kid", kid)
.setParameter("nid", nid)
.uniqueResult();
This exception comes out:
org.hibernate.QueryException: Space is not allowed after parameter prefix ':' ....
This might because of := operator. I found some Hibernate issue about this. This issue is still open. Isn't there any solution for this problem?

Note that HHH-2697 is now fixed for Hibernate 4.1.3 You can now escape with backslash:
SELECT k.`news_master_id` AS id, #row \:= #row + 1 AS rownum
FROM keyword_news_list k
JOIN (SELECT #row \:= 0) r
WHERE k.`keyword_news_id` = :kid
ORDER BY k.`news_master_id` ASC

Another solution for those of us who can't make the jump to Hibernate 4.1.3.
Simply use /*'*/:=/*'*/ inside the query. Hibernate code treats everything between ' as a string (ignores it). MySQL on the other hand will ignore everything inside a blockquote and will evaluate the whole expression to an assignement operator.
I know it's quick and dirty, but it get's the job done without stored procedures, interceptors etc.

you can implement this is a slightly different way.. you need to replace the : operator with something else (say '|' char ) and in your interceptor replace the '|' with the : .
this way hibernate will not try to think the : is a param but will ignore it
For the interceptor logic you can refer to the hibernate manual
This has worked for me using MySQL 5.
remember, this replacing of : must be only done to ':=' and other MySQL specific requirments.. don't try to replace the : for the param-placeholders. (hibernate will not be able to identify the params then)

I prefer to include Spring JDBC and execute the query rather than fight against Hibernate interceptors.

in Hibernate exception on encountering mysql := operator Stanislav gave another option other than interceptor to solve this issue

If you keep your SQL-files away from Java code - try this piece of code. Played a lot to get the right number of escaping slashes:
String sqlPattern = FileUtils.readFile(this.getClass(), /sql/my_query.sql");
sqlPattern = sqlPattern.replaceAll(":=", "\\\\:=");
Query query = entityManager.createNativeQuery(sqlPattern);

I guess there should not be a space after = , the operator should be written as =: (without any spaces)

Related

Error in Update JPA Query

I have a problem with this query when run is as follows
Query query = em.createQuery("UPDATE Equipo c JOIN c.histAsociados e SET e.horasTrabajadas = (CAST(c.horastd AS DECIMAL(18,2)) - (c.horastotales AS DECIMAL(18,2))) WHERE c.id=" + equipo.getId());
This is the exception that gets thrown when running
The SET identifier is missing from the UPDATE clause. [39, 39] The equal sign must be specified.[16, 38] The expression is invalid, which means it does not follow the JPQL grammar.[40, 43] The identification variable 'SET' cannot be a reserved word.[115, 115] The right parenthesis is missing from the sub-expression.[115, 115] The right parenthesis is missing from the sub-expression.[116, 126] The expression is invalid, which means it does not follow the JPQL grammar.[133, 151] The query contains a malformed ending.
Try something like this, putting it into a subquery, assuming that histAsociados is an entity inside Equipo entity:
Query query = em.createQuery("UPDATE Equipo c SET c.histAsociados WHERE c.histAsociados.id in(select Equipo.id from Equipo c1 LEFT JOIN c1.histAsociados e WHERE e.horasTrabajadas = (CAST(c1.horastd AS DECIMAL(18,2)) - (c1.horastotales AS DECIMAL(18,2))) AND c1.histAsociados.id = e.id) AND c.id=:id).setParameter("id", equipo.getId())
Also, could you please try putting some simple names to the attributes of entities. That increases readability.
According to the JPA specification, your syntax cannot be correct. The simplified form of the syntax for the UPDATEstatement can be stated as follows:
UPDATE <entity_name> <identification_variable> SET <identification_variable>.<state_field> = <value> WHERE <condition>
Applying this for your case it might look like:
"UPDATE HistAsociado?? e SET e.horasTrabajadas = (SELECT (CAST(c.horastd AS DECIMAL(18,2)) - CAST(c.horastotales AS DECIMAL(18,2))) AS DIFF FROM Equipo c WHERE c.id= " + equipo.getId())
In the above statement, I was guessing that HistAsociado could be an Entity name; otherwise you have to correct it!
The result of the subquery must also be a single value.
Warning: The modified statement would update all the records in the table as there is no WHERE condition is specified.
So, use this as a hint to solve the problem and don't use it before you have corrected the guessing and added the WHERE condition.
For more information and examples, you could read JPA update statement and this one too.

Hibernate exception on encountering mysql := operator

When I execute the following code the exception occurs:
Exception: org.springframework.orm.hibernate3.HibernateQueryException:
Not all named parameters have been set
Here is my code:
queryString = SET #quot=0,#latest=0,#comp='';
select B.* from (
select A.time,A.change,IF(#comp<>A.company,1,0) as LATEST,#comp:=A.company as company from (
select time,company,quote-#quot as `change`, #quot:=quote curr_quote
from stocks order by company,time) A
order by company,time desc) B where B.LATEST=1;
list = getHibernateTemplate().executeFind(new HibernateCallback(){
public Object doInHibernate(Session session)throws HibernateException,SQLException {
SQLQuery query = session.createSQLQuery(queryString);
query.setParameterList("list", custIds);
return query.list();
}
What is the reason for this behavior?
It's a little bit hard to understand, what is exactly the query you are executing, but if you need to use the colon character in native query, in your case as "assign a value" operator, you should escape all the colon occurances with \\ in your java String with the query, so it could be like:
select B.* from (
select A.time,A.change,IF(#comp<>A.company,1,0) as LATEST,#comp\\:=A.company as company from (
select time,company,quote-#quot as `change`, #quot\\:=quote curr_quote
from stocks order by company,time) A
order by company,time desc) B where B.LATEST=1;
Update: seems, it is not possible yet to escape the colons in Hibernate native queries, there is an open issue about it. That means, that you are not able to use a colons in Hibernate native queries not for the named parameters. You can try to create a function and call it instead of calling a query.
you can create a named query and then use it in spring jpa repository or hibernate. This link helped me from similar problem.

JPA CriterialBuilder.concat force to use concat function

I'm using CriteriaBuilder.concat to concatenate 2 Strings, using code below:
Expression<String> concat = criteriaBuilder.concat(expr1, expr2)
But the generated SQL is something like:
select distinct col_1 || col_2
which causes org.hibernate.hql.ast.QuerySyntaxException:
expecting CLOSE, found '||' near line 1, column 48 [
select count(distinct generatedAlias0.hostname || generatedAlias0.device) from ...
^(1,48)
I wonder how to force it to generate the following SQL which uses the concat() function, instead of the || operator?
select distinct concat(col_1, col_2)
Update:
From the error we can see that the problem is more on the Hibernate (v3.6.10.Final) side, which is why making MySQL to accept || for concatenation doesn't help, also updating to a newer version is not an option for me.
Thank you
I've actually found a workaround. by using #Formula (from Hibernate) instead of CriteriaBuilder for the same task, like this:
#Entity
public class MyEntity {
#Column(name="col_a")
private String colA;
#Column(name="col_b")
private String colB;
#Formula("concat(col_a, col_b)")
private String concated;
//...
}
This way I can use the concated field for CriteriaBuilder.countDistinct:
//...
Expression<?> exp = criteriaBuilder.countDistinct(entity.get("concated"));
criteriaQuery.select(exp);
TypedQuery<Long> query = entityManager.createQuery(criteriaQuery);
return query.getSingleResult();
I wish JPA would (or hopefully already) support countDistinct with multiple columns, then all these mess could have been avoided (see: How to countDistinct on multiple columns, the answer was NO).
I had a similar problem with the concat function.
I have used the concat function in a selectCase and this also returns the same QuerySyntaxException.
My workaround is to use the concat function via criteria builder function:
cb().selectCase().when(cb().equal(root.get(Person_.flag), cb().literal("1")),
cb().function("CONCAT", String.class, root.get(Person_.something), cb().literal(" bla bla bla")))
.otherwise(root.get(Person_.something)))
Hibernate Version 4.3.11.Final
I had this issue as well. The JPA/HQL generated sql query use pipes as concat (which is ||).
I am using Mariadb 10, Springboot-data-jpa2.0.6 (with Hibernate 5.2.17)
Issue example
Given HQL: select x from Xxx x where concat(x.field1, x.field2) = $1
Generated SQL: select ..... where (x.field1 || x.field2) = ?
Reason:
Use of || is deprecated, since mysql 8.0, unless the PIPES_AS_CONCAT SQL mode is enabled: https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html
For mariadb: https://mariadb.com/kb/en/or/
Work around:
(preferred) ranther then use concat function, there is a similar one concat_ws: https://mariadb.com/kb/en/concat_ws/
Use JPA native query #Query(nativeQuery = true, value ="select * from ....")
set global sql_mode=<list of modes which contains PIPES_AS_CONCAT>
(I didn't try this)

How to do a OR in HQL when there is no value with which we are comparing it gives this error java.lang.NullPointerException?

This is the sentence HQL
select r.response as response from Responsemix as r right join r.idOptQuestion
as opt where opt.idQuestion=5 and opt.content='Other' or opt.content='Others'
order by r.response asc
I don't think the problem with the or in this Hibernate query, apart from a possible logic issue with the order of precedence between OR and AND.
From your query text, we deduce that what you probably wanted is:
... where opt.idQuestion=5 and ( opt.content='Other' or opt.content='Others' )
I believe Hibernate will also allow you to write this as:
... where opt.idQuestion=5 and opt.content in ('Other','Others')
Are you saying that if you remove this portion: "or opt.content='Others'" from the query text, then the query works. But when you add that back in to the query, Hibernate is throwing a java.lang.NullPointerException ? Sweeeeet.

what is the equelent of sql query in hibernate

In SQL Server i am using this query
select *
from Unit c
ORDER BY CONVERT(INT, LEFT(name, PATINDEX('%[^0-9]%', name + 'z')-1)) desc;
I want this query to use in Hibernate. when I use this in Hibernate I got error
java.lang.IllegalArgumentException: org.hibernate.hql.ast.QuerySyntaxException:unexpected token: LEFT near line 1, column 122
[SELECT c FROM models.entities.UnitEntity c WHERE c.expSetId = :expSetId AND isWaitArea=:isWaitArea ORDER BY CONVERT(INT, LEFT(name, PATINDEX('%[^0-9]%', name + 'z')-1)) asc]
some of that is not in the hibernate dialect, you can change left with substring, convert with cast. and as for patindex i couldn't find the substitution. you either can add pathindex to the constructor of the dialect that you use
registerFunction( "patindex", new StandardSQLFunction("patindex") );
or create patindex() to a stored procedure.
then you can use something like this:
from Unit c order by cast(substring(name, 0, PATINDEX('%[^0-9]%', name + 'z')-1) as integer);
or you can use locate() instead of patindex(), but i think it doesn't support regular expression.
I am pretty sure that you can use SQL query with hibernate as well. When you create your hibernate session, you can use something like
session.createSQLQuery("Your Query Here")
Hope this helps.

Categories