秋天的北京一如既往的没太阳,分不清天上是云是雾或者又是什么奇奇怪怪的东西,建筑物不分黑白的证明着自己的轮廓。雾蒙蒙的天一眼望不到边,就像我需要用到 Spring Data Jpa 实现一个特别小的 SQL,怎么写都看不到我想要的效果一样。
想实现的 SQL 十分简单,就是一个简单的统计:
SELECT
activity_code,
activity_name,
sum(contact_user_nums),
conversion_step_code,
conversion_step
FROM
epm_estimate_main
WHERE
activity_code ='1'
GROUP BY
conversion_step_code
ORDER BY
contact_user_nums DESC;
由于一些条条框框的原因,最终选择用 jpa 的这种实现方式,想到这个玩意五分钟写完就能回家玩游戏,于是上来就写:
但是查询出来的结果却不是很好看:第一,我没有拿到想要的求和;第二,查询出来的结果集包括所有字段;
好在分组和排序都按照预期的想法实现了。
首先去 B 某搜索引擎上找,找来找去,大家的方法无非四种:
第一种:去 XXX 的 JPA, MyBatis 真香;
第二种:实在不行用 JPQL;
第三种:自己创建 CriteriaBuilder,CriteriaQuery 及 Root;
第四种:不知道怎么写,等大腿。
国内搜完,于是又去 G 某引擎上翻翻,老外似乎问的比较具体,但都没找到我想要的答案(散装的四级还是吃力);
翻看了一些网上的问题与解答,貌似出现了一个规律,用 findAll 方法实现 Specification 接口的文章似乎都有这个问题,而使用 EntityManager 自己来实现的小伙伴都不关注怎么。
赤裸裸的歧视,下定决心就要用这个 findAll 实现一把。
窗外的天色越来越暗,但是我觉得这么简单的一个查询居然浪费了我开黑的时间,不由得开始怀疑自己。不管我怎么对 CriteriaQuery.multiselect 操作,查询出来得结果集都是包含所有字段,不由得开始怀疑 multiselect 这个方法为什么没有生效?
JpaSpecificationExecutor 这个接口到底什么情况?
找到这个接口的 API:有两个实现类(其中 QuerydslJpaRepository 已显示过期)
剩下的这个实现类 SimpleJpaRepository 中找到对应的 findAll 方法,它调用了本类中的其他方法,一路向下找,最终在方法调用的末端,找到了其中的猫腻:
我最终所有定义的字段都被 query.select(root)
覆盖掉了!
天色已晚,华灯初上,最后本来想重写一下这个方法,但撇了一眼楼下的公交站,广告牌的背景似乎有点过年的味道。
那段代码最终变成这样(Spring Boot 自动注册了 EntityManager):
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<EstimateDO> query = builder.createQuery(EstimateDO.class);
Root<EstimateDO> root = query.from(EstimateDO.class);
query
.multiselect(root.get("activityCode").as(String.class).alias("activityCode"),
root.get("activityName").as(String.class).alias("activityName"),
builder.sum(root.get("contactUserNums").as(Long.class)).alias("contactUserNums"),
root.get("conversionStepCode").as(String.class).alias("conversionStepCode"),
root.get("conversionStep").as(String.class).alias("conversionStep")
);
List<Predicate> predicates = new ArrayList();
predicates.add(builder.equal(root.get("activityCode").as(String.class), estimateDO.getActivityCode())
);
if (!StringUtils.isEmpty(estimateDO.getContactCode())) {
predicates.add(builder.equal(root.get("contactCode").as(String.class), estimateDO.getContactCode())
);
}
query
.where(predicates.toArray(new Predicate[predicates.size()]))
.groupBy(root.get("conversionStepCode"))
.orderBy(builder.desc(root.get("contactUserNums")));
List<EstimateDO> list = entityManager.createQuery(query).getResultList();
我的决心一文不值。