写在前面
spring-data-jpa是spring对hibernate框架的封装,如果你一开始熟悉的是mybatis,那么转到jpa可能会让你有些不适应。jpa的简单查询确实非常非常方便,但是对于复杂查询来说,jpa确实有点“复杂”。当然,你可以写原生sql,但是那样就违背了jpa操作对象、避免操作sql的出发点了。好,废话不多说,进入正题吧。
遇到的问题
原先是针对单表的列表查询操作,非常简单,所以就用了Specification来做:
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = Lists.newArrayList();
//一些查询条件,数量比较多
if (deviceStatus != null && deviceStatus > 0) {
//主要是这段,之前是单表的状态查询
if (DeviceFilterStatus.STOP_USE.getCode().equals(deviceStatus)) {
predicates.add(criteriaBuilder.equal(root.get("isUsing"), deviceStatus));
} else {
predicates.add(criteriaBuilder.equal(root.get("online"), deviceStatus));
}
}
Predicate[] predicateArr = new Predicate[predicates.size()];
return criteriaBuilder.and(predicates.toArray(predicateArr));
};
后来改了需求,查询逻辑有所改变,涉及到第二张表,而我又不想推翻重新写原生sql,那样效率比较低(懒),去百度上疯狂找,奈何没答案,也问了很多人,纷纷表示没有用过这样的写法。后来我去了spring官网看文档,发现:
官网只有一些简单例子,并没有复杂查询的介绍(可能得去看hibernate官网,不过我没去看)。
so.....只能自己摸索
结果
我找了一下CriteriaBuilder的方法,发现存在exists的方法,那么,接下来的问题就在于如何构建他的参数Subquery。
Subquery看字面意思是子查询,显然sql里面exists后面的查询得放在这里面做
事情有了转机,我发现第二个参数CriteriaQuery有构建子查询的方法
然后我通过这个构建出子查询
Subquery<DeviceAlarmEntity> subQuery = query.subquery(DeviceAlarmEntity.class);
Root<DeviceAlarmEntity> subRoot = subQuery.from(XXX.class);
//这里是子查询的条件,前者是自身的条件,后者是主表的关联条件,当然where方法的参
//数是个可变参数,可以根据自己需要加条件
subQuery.where(criteriaBuilder.equal(subRoot.get("valid"), true), criteriaBuilder.equal(root.get("deviceId"), subRoot.get("deviceId")));
//这句话不加会报错,因为他不知道你子查询要查出什么字段
subQuery.select(subRoot.get("id"));
最后,把上面的子查询放进exists方法的参数就可以了,
如果是not exists,前面再嵌套not方法,下面是示例:
//exists
criteriaBuilder.exists(subQuery)
//not exists
criteriaBuilder.not(criteriaBuilder.exists(subQuery)
本文没什么深度,纯粹经验之谈,希望能帮助到有同样需求的你。