i need to translate this SQL expressioninto Hibernate using Criteria
select id,field1,field2,count(*) from my_table
group by field1,field2 having count(*)>1;
here is my code so far.
final Session session = ......
ProjectionList projectionList = (ProjectionList)emptyProjection();
projectionList.add(Projections.property("id"),"id");
projectionList.add(Projections.groupProperty("codeI"));
projectionList.add(Projections.groupProperty("codeII"));
projectionList.add(Projections.sqlGroupProjection("count(*)","having count(*)>1",new String[]{},new Type[]{StandardBasicTypes.INTEGER}));
final Criteria criteria = session.createCriteria(MyClass.class).setProjection(projectionList).setResultTransformer(transformer(MyClass.class));
return new ArrayList<MyClass>(criteria.list());
my problem is
the criteria is generating this SQL.
group by
this_.FIELD1,
this_.FIELD2,
having
count(*)>1
as you can see the second group by is followed by a COMMA this_.FIELD2, and mySQL is throwing SyntaxError
UPDATE
here is my hibernate.cfg
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
and we are using mysql 5.5.29 and HIBERNATE 4.2.3
i hope somebody give a tip.
best regards..
Hibernate does not support a having clause in the Criteria API. Your code is tantalizing close to the SQL you want, but what you've actually specified is just another group by condition, hence the comma. Here is a feature request from forever ago: https://hibernate.atlassian.net/browse/HHH-1043.
If you must use a criteria, then you can use a subquery which will end up being slow. HQL does support the clause so you could do it that way.
More discussion and examples: Hibernate Criteria API - HAVING clause work arounds.
Related
To combine multiple columns as one,
I found one answer
SELECT id,CONCAT_WS(',', field_1, field_2, field_3, field_4) list
FROM `table`;
This query working fine in SQL but it gives me error in HQL:
Error is .
(java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode )
please help me to find out what wrong i did, help me to know how to use CONCAT_WS() IN HQL
below how i written my HQL query
SELECT C1._URI,C1.HEALTH_FACILITY,C1.DISTRICT,CONCAT_WS(',', C1.BLOCKS_OF_BHUBRI, C1.BLOCKS_OF_GOLAGHAT, C1.BLOCKS_OF_HAILAKANDI) as Block_name
FROM GapAnalysisWashInHealthFacilitiesCore C1
any help will appreciate
CONCAT_WS is a function specific to mySql. HQL is a generic language and not aware of native SQL functions and syntax. If you really need the function, then you should use Hibernate's API for native SQL.
Session session = ...;
Query query = session.createSQLQuery("
SELECT id,CONCAT_WS(',', field_1, field_2, field_3, field_4) Block_name FROM `table`");
List result = query.list();
Then you may like to have a look at Result Transformers to get result as list of GapAnalysisWashInHealthFacilitiesCore objects.
I wanted to know how can we convert a HQL query into a sql query .I know if we make the showsql = true parameter on we can get the sql query but the parameter value would not be appended with its values .I need to find ways to print the SQL query generated in logs and use for my performance optimization .
Thanks in advance
You can check my answer here: https://stackoverflow.com/a/37749916/1350643
In short you can use following code to convert hql to sql:
QueryTranslatorFactory translatorFactory = new ASTQueryTranslatorFactory();
SessionFactoryImplementor factory = (SessionFactoryImplementor) getSessionFactory();
QueryTranslator translator = translatorFactory.
createQueryTranslator(hqlQueryText, hqlQueryText, Collections.EMPTY_MAP, factory);
translator.compile(Collections.EMPTY_MAP, false);
translator.getSQLString();
Source: http://narcanti.keyboardsamurais.de/hibernate-hql-to-sql-translation.html
Hibernate uses dialects for specific optimizations. Maybe you could extend one of the existing Oracle dialects or supply your own.You can create your custom dialect by subclassing the Oracle dialect.
I got very typical issue. My dynamically generated query like this...
UPDATE Templates t SET t.TEMPLATE_DATA = replace(t.TEMPLATE_DATA, 'Test\'s Test', 'Kent"s Test'), t.TEMPLATE_DATA = replace(t.TEMPLATE_DATA, 'Test"s Test', 'Kent"s Test'), UPDATE_DATE = NOW() where PRACTICE_ID = 1 AND CATEGORY_ID IN (1)
This works perfect when I explictily fire this query in db. but by using hibernate's session.createQuery(-- my query --) if thwows an error QueryTranslatorException.
Database : Mysql 5.3
Have any one faced this issue?
Thanks in advance.
Try to run this in Hibernate as native SQL query:
session.createSQLQuery(-- query text --);
Because if you use
session.createQuery(-- query text --);
Hibernate will try to execute it as HQL query which differs from usual SQL query.
HQL is object oriented query language. It operates in terms of objects rather then in terms of tables. Here posted a brief description of difference between SQL and HQL. But if you have time better to read appropriate sections of hibernate's documentation about HQL and Native SQL usage.
If you want to execute SQL Query in hibernate, Use : session.createSQLQuery(String query);
I'm using eclipse indigo and am having "JPA Validation Problems".
My named query is:
from Person p where p.name = :name
and there is this error:
The query does not start with a valid identifier, has to be either SELECT, UPDATE or DELETE FROM.
But it's a valid JPQL query. Somebody know how I can remove this error?
If I change my query to
select p from Person p where p.name = :name
there is no more error, but I do not want to change all my queries.
thanks
mp5
If you are not concerned with portability, you can turn off the JPQL validation that was added to Dali in the the Indigo release. If you have a JPA project with the Hibernate platform selected you will still get whatever validtion Hibernate Tools has for JPQL/HQL.
Go to workspace preferences 'Java Persistence'->JPA->Errors/Warnings' under 'Queries and generators' and change 'Invalid or incomplete JPQL queries' to 'Ignore'. You can enter a bug against the Hibernate tools if you would like them to extend the Dali JPQL validation for the Hibernate platform or just to turn it off by default.
It looks to me like any queries that are of the form:
from Person p where p.name = :name
Are not in fact valid JPQL. According to the language reference at:
http://docs.oracle.com/javaee/5/tutorial/doc/bnbuf.html
each statement needs to have either a SELECT, UPDATE or DELETE statement preceding the FROM portion.
Here are more examples:
http://en.wikipedia.org/wiki/Java_Persistence_Query_Language
Unfortunately, it looks like you need to update all the queries to make them fit this format.
It's not a valid JPQL query. It's a valid HQL query, but HQL ain't JPQL. A JPQL query must have a select clause.
Here's the BNF syntax of a JPQL clause, from the specifications:
select_statement :: = select_clause from_clause [where_clause] [groupby_clause [having_clause] [orderby_clause]
And indeed that is not a valid JPQL query. JPQL starts with "SELECT", "UPDATE" or "DELETE".
Obviously it may work in Hibernate (i.e HQL) but that query is not standard and not portable. So if you don't want to change your queries then you aren't using JPA, and your app is non-portable.
The JPA spec would confirm this.
In this query:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createTupleQuery();
// FROM GamePlayedEvent gpe
Root<GamePlayedEvent> gpe = q.from(GamePlayedEvent.class);
// SELECT gameId, COUNT(*) AS count, AVG(duration)
// AS avDur, AVG(rewardCurrency) AS avCur, AVG(rewardXP) avXp
q.select(cb.tuple(
gpe.<String>get("gameId"),
cb.count(gpe).alias("count"),
cb.avg(gpe.<Double>get("duration")).alias("avDur"),
cb.avg(gpe.<Integer>get("rewardCurrency")).alias("avCur"),
cb.avg(gpe.<Integer>get("rewardXp")).alias("avXp")
));
// WHERE loginTime BETWEEN ...
q.where(cb.between(gpe.<Date>get("time"), fromTime, toTime));
// GROUP BY gameId
q.groupBy(gpe.<String>get("gameId"));
// ORDER BY count DESC
q.orderBy(cb.desc(???));
How can I add the ORDER BY count DESC, referring to the "count" defined in the SELECT clause?
What if you just captured the count expression, and used it directly?
Expression event_count = cb.count(gpe);
q.select(cb.tuple(
gpe.<String>get("gameId"),
event_count,
...
));
q.orderBy(cb.desc(event_count));
I came across the same problem today but none of the suggested solutions worked for me because I needed to reuse the expression not only in the order by clause but also in the group by clause.
One obvious solution would be to create a view on the database level but this is a bit clumsy, creates an unnecessary subquery and even not possible if the db user isn't granted enough privileges. A better option which I ended up implementing is to write something like this
q.select(cb.tuple(
gpe.<String>get("gameId"),
cb.count(gpe),
...
)).groupBy(cb.literal(2)).orderBy(cb.literal(2));
The first downside of this approach is that the code is errorprone. The other drawback is that the generated sql query contains ordinal position notation, which works on some databases (like postgresql or mariadb) but doesn't work on others (like sql server). In my case, however, I found this to be the best option.
Tested on jpa 2.1 with hibernate 5.2.3 as a provider and postgresql 9.6.
Even though the Pro JPA 2 book describes that the alias method can be used to generate a sql query alias (on page 251) I have had no success with making it work with neither EclipseLink or Hibernate. For your question I would say that your orderBy line should read:
q.orderBy(cb.desc(cb.count(gpe));
if it was supported by the different vendors.
As far as my research goes it seams that the alias method is only used for naming elements in the tuble used in the select (so only for projection).
I have one question though. Why would you want to use the JPA Criteria API for this query. It (the query) seams to be static in nature so why not use JPQL where you can define your query aliases directly.
Have you tried setting up a projection with an alias?
criteria.setProjection(Projections.projectionList()
.add(Projections.count("item.id"), "countItems"));
criteria.addOrder(Order.desc("countItems"));
For a sum aggregation field I have the following code which worked for me:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(entity);
Root<T> root = cq.from(entity);
cq.orderBy(cb.desc(cb.sum(root.get(orderByString))));
// orderByString is string entity field that is being aggregated and which we want to put in orderby clause as well.