目前的spring data jpa已經幫我們幹了CRUD的大部分活了,但如果有些活它幹不了(CrudRepository接口中沒定義),那麼只能由我們自己幹了。這裏要說的就是在它的框架裏,如何實現自己定製的多條件查詢。下面以我的例子說明一下:業務場景是我現在有張訂單表,我想要支持根據訂單狀態、訂單當前處理人和訂單日期的起始和結束時間這幾個條件一起查詢。
先看分頁的,目前spring data jpa給我們做分頁的Repository是PagingAndSortingRepository,但它滿足不了自定義查詢條件,只能另選JpaRepository。那麼不分頁的Repository呢?其實還是它。接下來看怎麼實現:
Repository:
import com.crocodile.springboot.model.Flow;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface FlowRepository extends JpaRepository<Flow, Long> {
Long count(Specification<Flow> specification);
Page<Flow> findAll(Specification<Flow> specification, Pageable pageable);
List<Flow> findAll(Specification<Flow> specification);
}
Service:
/**
* 獲取結果集
*
* @param status
* @param pageNo
* @param pageSize
* @param userName
* @param createTimeStart
* @param createTimeEnd
* @return
*/
public List<Flow> queryFlows(int pageNo, int pageSize, String status, String userName, Date createTimeStart, Date createTimeEnd) {
List<Flow> result = null;
// 構造自定義查詢條件
Specification<Flow> queryCondition = new Specification<Flow>() {
@Override
public Predicate toPredicate(Root<Flow> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicateList = new ArrayList<>();
if (userName != null) {
predicateList.add(criteriaBuilder.equal(root.get("currentOperator"), userName));
}
if (status != null) {
predicateList.add(criteriaBuilder.equal(root.get("status"), status));
}
if (createTimeStart != null && createTimeEnd != null) {
predicateList.add(criteriaBuilder.between(root.get("createTime"), createTimeStart, createTimeEnd));
}
return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
}
};
// 分頁和不分頁,這裏按起始頁和每頁展示條數爲0時默認爲不分頁,分頁的話按創建時間降序
try {
if (pageNo == 0 && pageSize == 0) {
result = flowRepository.findAll(queryCondition);
} else {
result = flowRepository.findAll(queryCondition, PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "createTime"))).getContent();
}
} catch (Exception e) {
LOGGER.error("--queryFlowByCondition-- error : ", e);
}
return result;
}
上面我們可以看到,套路很簡單,就是兩板斧:先通過Specification對象定義好自定義的多查詢條件,我這裏的條件是當傳了當前用戶時,那麼將它加入到查詢條件中,不傳該參數自然就不加,同理,傳了訂單狀態的話那是通過相等來判斷,最後,如果傳了起始和結束時間,通過between來查在起始和結束之間的數據;第二板斧調用我們在Repository中定義好的findAll方法,如果分頁就用帶Pageable分頁對象參數的方法,不分頁不帶該參數即可。
如果你的自定義查詢條件裏需要模糊查詢,比如我有個訂單ID要支持模糊查詢,也很簡單:
if (orderId!= null) {
predicateList.add(criteriaBuilder.like(root.get("orderId"), "%" + orderId+ "%"));}
最後我們看回到FlowRepository的第一個方法count,它是返回不分頁的多查詢的總記錄數的,套路也是一樣的:
/**
* 查記錄數
*
* @param status
* @param userName
* @param createTimeStart
* @param createTimeEnd
* @return
*/
public Long getCounts(String status, String userName, Date createTimeStart, Date createTimeEnd) {
Long total = 0L;
Specification<Flow> countCondition = new Specification<Flow>() {
@Override
public Predicate toPredicate(Root<Flow> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicateList = new ArrayList<>();
if (userName != null) {
predicateList.add(criteriaBuilder.equal(root.get("currentOperator"), userName));
}
if (status != null) {
predicateList.add(criteriaBuilder.equal(root.get("status"), status));
}
if (createTimeStart != null && createTimeEnd != null) {
predicateList.add(criteriaBuilder.between(root.get("createTime"), createTimeStart, createTimeEnd));
}
return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
}
};
try {
total = flowRepository.count(countCondition);
} catch (Exception e) {
LOGGER.error("--getCountsByCondition-- error: ", e);
}
return total;
}