JPA执行带JOIN FETCH的count查询报错,求原因与解决办法
问题背景
需要创建JPA查询统计符合特定条件的实体总数,但执行时出现错误。
原查询语句
select count(*) from Employee emplWithLicence JOIN FETCH emplWithLicence.firma fa JOIN FETCH emplWithLicence.user userWithLicence JOIN FETCH userWithLicence.contact ctc LEFT OUTER JOIN FETCH userWithLicence.licence lice where userWithLicence.active = true and emplWithLicence.firma.name like 'Be%'
错误信息
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=fa,role=at.home.digest.model.dave.Employee.firma,tableName=firma,tableAlias=firma1_,origin=empl employee0_,columns={employee0_.firma_id ,className=at.home.digest.model.dave.Firma}}]
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:217)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:1018)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:786)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:677)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:313)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:261)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:271)
at org.hibernate@5.3.10.Final//org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:191)
... 162 more
问题原因
JOIN FETCH的作用是一次性关联查询出关联实体的数据,用于加载完整的实体对象,但统计总数的查询目标是count(*),不需要加载关联实体的具体数据。- Hibernate规定,使用
JOIN FETCH时,查询的SELECT子句必须包含关联关系的拥有者(主实体),但当前查询只选择了count(*),违反该规则导致报错。
修复方法
去掉所有FETCH关键字,统计查询只需普通JOIN即可满足WHERE子句的条件筛选需求:
select count(*) from Employee emplWithLicence JOIN emplWithLicence.firma fa JOIN emplWithLicence.user userWithLicence JOIN userWithLicence.contact ctc LEFT OUTER JOIN userWithLicence.licence lice where userWithLicence.active = true and emplWithLicence.firma.name like 'Be%'
补充说明
未来WHERE子句添加更多条件时,只要条件基于关联实体属性,保留对应类型的JOIN(内连接/左外连接)即可,无需再使用FETCH——FETCH仅在需要加载实体关联数据时使用,统计类查询完全不需要。
内容的提问来源于stack exchange,提问作者Alex Mi

