Spring Boot使用EntityManager创建动态查询时count(*)报错如何解决
现有代码问题排查
- 依赖注入逻辑错误:你写的
entityManager = entityManager属于自我赋值,实际没有把注入的em赋值给全局变量,会导致后续调用entityManager!!直接抛出空指针异常,正确的赋值应该是entityManager = em - 查询仅创建未执行:
createQuery方法仅构造了查询对象,没有调用getResultList()执行查询获取结果,自然拿不到统计数据 - 变量命名笔误:你定义的
teamDate从业务逻辑来看应该是teamData,虽然不影响运行但容易造成后续维护混淆
该查询方式的合理性判断
固定统计逻辑的场景下,直接通过EntityManager.createQuery写JPQL做count统计是合理的,性能和可读性都符合要求。如果你的统计逻辑需要动态拼接筛选条件、动态切换分组字段、动态调整统计项,硬编码JPQL的方式就不合理,容易出现SQL注入风险,且维护成本极高,这种场景推荐使用JPA Criteria API构造动态查询。
正确实现示例
1. 固定统计逻辑的修复版代码
// 先修正依赖注入赋值 @Autowired fun JobCompletionNotificationListener(em: EntityManager) { entityManager = em } override fun afterJob(jobExecution: JobExecution) { if (jobExecution.status === BatchStatus.COMPLETED){ log.info("!!! JOB FINISHED! Time to verify the results") val teamData : MutableMap<String, Long> = HashMap() // 执行查询并遍历结果 val resultList = entityManager!!.createQuery( "SELECT m.team1 , COUNT(*) FROM Match m GROUP BY m.team1", Array<Any>::class.java ).resultList resultList.forEach { row -> val teamName = row[0] as String val count = row[1] as Long teamData[teamName] = count } // 后续可自行处理teamData的业务逻辑 } }
注意COUNT(*)返回的是Long类型,不要强行转为Int避免数值溢出。
2. 动态count查询(Criteria API实现)
如果需要动态加筛选条件,比如可选按比赛日期过滤再统计,示例代码如下:
override fun afterJob(jobExecution: JobExecution) { if (jobExecution.status === BatchStatus.COMPLETED){ val cb = entityManager!!.criteriaBuilder val query = cb.createQuery(Array<Any>::class.java) val root = query.from(Match::class.java) // 动态构造查询条件示例:如果有传入开始日期就加过滤 val predicates = mutableListOf<Predicate>() if (startDate != null) { predicates.add(cb.greaterThanOrEqualTo(root.get("matchDate"), startDate)) } query.where(*predicates.toTypedArray()) // 构造统计+分组逻辑 query.multiselect( root.get<String>("team1"), cb.count(root) // 等价于COUNT(*) ) query.groupBy(root.get<String>("team1")) // 执行查询 val resultList = entityManager!!.createQuery(query).resultList // 后续处理结果逻辑和上面一致 } }
这种方式完全避免了字符串拼接的风险,类型安全,适合复杂动态统计场景使用。
内容的提问来源于stack exchange,提问作者Mama
相关产品推荐
相关产品推荐

