QueryDSL如何在FROM子句中使用子查询,解决JPAQuery转EntityPath报错问题
错误原因
报错com.querydsl.jpa.impl.JPAQuery cannot be cast to com.querydsl.core.types.EntityPath的核心原因是:JPA 规范本身不支持在FROM子句后直接使用子查询作为数据源,QueryDSL的标准JPAQuery的from()方法只接收实体映射对象EntityPath,你直接传入JPAExpressions构造的子查询对象就会触发类型转换异常。
另外你现有代码还有逻辑错误:内层查询中写了qKpiRuleBoard.objectPhysicalName.countDistinct().count(),多拼接了一次.count(),和你原始SQL的逻辑不符。
最优解决方案(逻辑简化)
你提供的原始SQL存在逻辑冗余,可以直接简化为单层级查询,完全不需要子查询,同时可以避免上述报错,查询性能也更好:
你的原始SQL等价于:
select gf_storage_zone_type, count(distinct gf_object_physical_name) numObjects from t_kgov_kpi_streaming_object group by gf_storage_zone_type
对应的QueryDSL代码如下:
QKpiRuleBoard qKpiRuleBoard = QKpiRuleBoard.kpiRuleBoard; List<ObjectsByStorageZoneProjection> objectsByStorageZone = jpaQueryFactory .select(Projections.constructor(ObjectsByStorageZoneProjection.class, qKpiRuleBoard.storageZoneType, qKpiRuleBoard.objectPhysicalName.countDistinct())) .from(qKpiRuleBoard) .where( qKpiRuleBoard.cutoffDate.eq(cutoffDate) .and(qKpiRuleBoard.countryId.eq(countryId)) .and(qKpiRuleBoard.executionFrequencyType.eq(executionFrequencyType))) .groupBy(qKpiRuleBoard.storageZoneType) .fetch();
必须使用子查询的场景解决方案
如果后续有更复杂的查询逻辑确实需要在FROM后接子查询,可以使用JPASQLQuery走原生SQL查询的方式实现,示例代码如下:
// 定义子查询返回字段的路径映射 NumberPath<Long> numObjectPhysicalName = Expressions.numberPath(Long.class, "numObjectPhysicalName"); StringPath storageZoneType = Expressions.stringPath("gf_storage_zone_type"); QKpiRuleBoard qKpiRuleBoard = QKpiRuleBoard.kpiRuleBoard; // 构造内层子查询 com.querydsl.core.Query<?> subQuery = JPAExpressions .select( qKpiRuleBoard.storageZoneType.as("gf_storage_zone_type"), qKpiRuleBoard.objectPhysicalName.countDistinct().as("numObjectPhysicalName") ) .from(qKpiRuleBoard) .where( qKpiRuleBoard.cutoffDate.eq(cutoffDate) .and(qKpiRuleBoard.countryId.eq(countryId)) .and(qKpiRuleBoard.executionFrequencyType.eq(executionFrequencyType))) .groupBy(qKpiRuleBoard.storageZoneType, qKpiRuleBoard.objectPhysicalName); // 使用JPASQLQuery处理子查询作为数据源的场景,方言根据你的实际数据库调整 List<ObjectsByStorageZoneProjection> objectsByStorageZone = new JPASQLQuery<>(entityManager, MySQLTemplates.builder().build()) .select(Projections.constructor(ObjectsByStorageZoneProjection.class, storageZoneType, numObjectPhysicalName.sum().as("numObjects") )) .from(subQuery, "v") // 给子查询指定别名 .groupBy(storageZoneType) .fetch();
内容的提问来源于stack exchange,提问作者exoding
相关产品推荐
相关产品推荐

