寫在前面
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)
本文沒什麼深度,純粹經驗之談,希望能幫助到有同樣需求的你。