目前的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; }
