here is my code:
query.groupBy(_entity.field1,_entity.field2).fetchCount()
then the exception:
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found ',' near line 1, column 56 [select count(distinct entity.field1, entity.field2)
from com.demo.core.domain.model.entity entity
where entity.field3 = ?1
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:133)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:670)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305)
at com.sun.proxy.$Proxy119.createQuery(Unknown Source)
at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:101)
at com.querydsl.jpa.impl.AbstractJPAQuery.fetchCount(AbstractJPAQuery.java:81)
at com.demo.core.infrastructure.repository.impl.queryRepositoryImpl.query(queryRepositoryImpl.java:91)
at com.demo.core.infrastructure.repository.impl.queryRepositoryImplTest.query(queryRepositoryImplTest.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found ',' near line 1, column 56 [select count(distinct entity.field1, entity.field2)
from com.demo.core.domain.model.rebate.entity entity
where entity.field3 = ?1
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:91)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:291)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:186)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662)
... 41 more
and when I remove one field in the groupBy, the same code will success
so what's the problem?
I have same problem. Please help!
fetchCount()
uses a COUNT
function, which is an aggregate function. Your query already has aggregate functions. You cant aggregate aggregate functions, unless a subquery is used (which is not available in JPA). Therefore this use case cannot be supported.
Duplicate of #2053
My temporary solution
@SneakyThrows
protected <E> Page<E> pageDTOs(JPAQuery<E> jpaQuery, Long pageNumber, Integer pageSize) {
if (jpaQuery.getMetadata().getGroupBy().size() > 1) {
// Fixed https://github.com/querydsl/querydsl/pull/2605/files
// Get JPQLSerializer
var serializer = (JPQLSerializer) $.bean.invoke(jpaQuery,
JPAQueryBase.class.getDeclaredMethod("serialize", boolean.class), false);
String hql = serializer.toString();
// HQL to Native SQL
var translatorFactory = new ASTQueryTranslatorFactory();
var hibernateSession = entityManager.getEntityManagerFactory().unwrap(SessionFactory.class);
var translator = translatorFactory.
createQueryTranslator("", hql, Collections.EMPTY_MAP, hibernateSession.getSessionFactory(), null);
translator.compile(Collections.EMPTY_MAP, false);
var sql = translator.getSQLString();
// Package COUNT
var countSql = "select count(1) from (" + sql + ") _tmp_" + System.currentTimeMillis();
var nativeQuery = entityManager.createNativeQuery(countSql);
// Add sql parameters
JPAUtil.setConstants(nativeQuery, serializer.getConstantToAllLabels(), jpaQuery.getMetadata().getParams());
// Fetch total number
var totalNumber = ((BigInteger) (nativeQuery.getSingleResult())).longValue();
// Fetch paginated records
var objs = jpaQuery
.limit(pageSize)
.offset(pageNumber == 1 ? 0 : pageNumber * pageSize)
.fetch();
return Page.build(pageNumber, pageSize, totalNumber, objs);
} else {
var objs = jpaQuery
.limit(pageSize)
.offset(pageNumber == 1 ? 0 : pageNumber * pageSize)
.fetchResults();
return Page.build(pageNumber, pageSize, objs.getTotal(), objs.getResults());
}
}
That approach works only with Hibernate - not plain JPA, and only if there is a one-to-one mapping between your HQL parameters and SQL parameters, which isn't always the case (for example when a named parameter is reused in the query).
If you need reliable count queries I encourage you to use Blaze-Persistence, which has an awesome mechanism of converting any query to its count query. It also integrates with Querydsl (the blaze-persistence-querydsl
module): https://persistence.blazebit.com/documentation/1.5/core/manual/en_US/index.html#querydsl-integration .
Using a BlazeJPAQuery instead of a JPAQuery both fetchResults
and fetchCount
work reliably again.
When should we expect a fix for 2504
Not, its going to be removed as it cannot be implemented.
Why this is not mentioned in the documentation? How are people supposed to use it?
Because this method was originally intended for just querydsl-sql
and was ported over to JPA very poorly. It inherently can't be supported in JPA, and therefore we will log warnings in the future.
I guess the answer is: don't rely on querydsl-jpa
to make your count queries for you, use blaze-persistence-querydsl
instead.
I've updated the javadoc with the rationale and alternatives: https://github.com/querydsl/querydsl/pull/2605/files#diff-63e9a52b83b73925263ae9ef7ad9372d4e76e8eceb9b2233139325bee46d8605R81-R102