首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring Data JPA動態查詢實戰:Specification與Criteria API深度解析

     編輯:Java 相關知識

Spring Data JPA 的 JpaSpecificationExecutor 配合 Specification 接口,提供了強大的類型安全動態查詢能力。本文深入剖析 Criteria API 核心組件,並通過兩個完整業務示例展示多條件組合、關聯子查詢、日期範圍等實戰技巧。

一、核心接口體系

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> spec);
    List<T> findAll(Specification<T> spec);
    Page<T> findAll(Specification<T> spec, Pageable pageable);
    List<T> findAll(Specification<T> spec, Sort sort);
    long count(Specification<T> spec);
}

public interface Specification<T> {
    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

二、Criteria API 關鍵角色

  • CriteriaBuilder:構建 Predicate、Expression、Order 等查詢元素的工廠
  • CriteriaQuery:代表完整查詢結構(select、from、where、group by、order by)
  • Root<T>:查詢根實體,對應 SQL FROM 子句,用於路徑導航 root.get("field")
  • Predicate:查詢條件謂詞,可組合 and/or/not

三、Repository 定義

public interface DevHREmpConstrastDao extends JpaRepository<DevHREmpConstrast, Long>,
        JpaSpecificationExecutor<DevHREmpConstrast> { }

四、動態查詢構建器示例

private Specification<DevHREmpConstrast> buildSpec(
        final String corg, final String name, final String type,
        final String date, final String checker) {

    return (root, query, cb) -> {
        List<Predicate> predicates = new ArrayList<>();

        // 1. 機構名稱模糊匹配 → 關聯子查詢獲取組織ID列表
        if (hasText(corg)) {
            List<String> orgIds = organizationDao.findByName("%" + corg + "%");
            if (validSize(orgIds))
                predicates.add(root.get("confirmOrgNo").in(orgIds));
        }

        // 2. 員工姓名模糊匹配 → 關聯子查詢獲取用戶ID列表
        if (hasText(name)) {
            List<String> userIds = userDao.findByName(name);
            if (validSize(userIds))
                predicates.add(root.get("hrUserName").in(userIds));
        }

        // 3. 變更類型精確匹配
        if (hasText(type))
            predicates.add(cb.equal(root.get("hrUpdateType"), type));

        // 4. 日期範圍查詢(當天 00:00:00 ~ 23:59:59)
        if (hasText(date)) {
            try {
                Date start = new SimpleDateFormat("yyyy-MM-dd").parse(date);
                Calendar cal = Calendar.getInstance();
                cal.setTime(start);
                cal.add(Calendar.DAY_OF_MONTH, 1);
                Date end = cal.getTime();
                predicates.add(cb.between(root.get("insDate"), start, end));
            } catch (ParseException e) {
                // 忽略格式錯誤
            }
        }

        // 5. 審核人模糊匹配
        if (hasText(checker)) {
            List<String> userIds = userDao.findByName(checker);
            if (validSize(userIds))
                predicates.add(root.get("confirmUserId").in(userIds));
        }

        return query.where(predicates.toArray(new Predicate[0])).getRestriction();
    };
}

// 輔助方法
private boolean hasText(String s) { return s != null && !s.isEmpty(); }
private boolean validSize(List<?> list) { return list != null && list.size() > 0 && list.size() < 1000; }

五、Service 層調用

@Autowired DevHREmpConstrastDao dao;

public Page<DevHREmpConstrast> search(String corg, String name, String type,
                                     String date, String checker, Pageable pageable) {
    return dao.findAll(buildSpec(corg, name, type, date, checker), pageable);
}

六、進階技巧總結

  1. 子查詢轉 IN 列表:將關聯表查詢結果收集為 ID 列表,再用 root.get("field").in(ids),避免複雜 Join
  2. 條件防禦:集合大小限制(<1000)防止 SQL 過長或性能劣化
  3. 日期邊界處理:用 Calendar 計算次日 0 點作為上界,確保包含全天
  4. 類型安全:root.<String>get("field") 編譯期檢查字段類型
  5. 複雜邏輯組合:cb.and()、cb.or()、cb.not() 任意嵌套

七、對比傳統 HQL 拼接優勢

維度HQL 字符串拼接Specification + Criteria
類型安全運行期報錯編譯期檢查
防注入需手動參數綁定自動參數化
重構友好字段重命名易出錯IDE 重構自動更新
可讀性碎片化字符串流式 API 結構清晰

掌握 Specification 模式,可構建出既靈活又健壯的動態查詢層,是 Spring Data JPA 進階開發的必備技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved