背景:前面有寫過一篇使用springboot Jpa通過引入 JpaSpecificationExecutor 來實現動態生成查詢條件的方式,主要代碼如下。
1 private Page<DrugApplyEntity> getDataList(DrugApplyInfoQuery query, Pageable pageable) { 2 Specification<DrugApplyEntity> querySpecifi = new Specification<DrugApplyEntity>() { 3 public Predicate toPredicate(Root<DrugApplyEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { 4 5 List<Predicate> predicates = new ArrayList<>(); 6 if (!StringUtils.isEmpty(query.getBeginDate())) { 7 //大於或等於傳入時間 8 predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("applyTime").as(Date.class), query.getBeginDate())); 9 } 10 if (!StringUtils.isEmpty(query.getEndDate())) { 11 //小於或等於傳入時間 12 predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("applyTime").as(Date.class), query.getEndDate())); 13 } 14 if (query != null) { 15 Class<? extends DrugApplyInfoQuery> clazz = query.getClass(); 16 Field[] fields = clazz.getDeclaredFields(); 17 for (Field tmpField : fields) { 18 tmpField.setAccessible(true); 19 try { 20 NotCondition annotation = (NotCondition) tmpField.getDeclaredAnnotation(NotCondition.class); 21 if (tmpField.get(query) != null && annotation == null) { 22 String name = tmpField.getName(); 23 if(query.getIsFuzzySearch()){ 24 // 模糊匹配 25 predicates.add(criteriaBuilder.like(root.get(name).as(String.class), "%"+ tmpField.get(query) + "%")); 26 }else{ //精準查找 27 predicates.add(criteriaBuilder.equal(root.get(name), tmpField.get(query))); 28 } 29 } 30 } catch (Exception e) { 31 log.error("構建查詢對象失敗",e); 32 } 33 } 34 } 35 // and到一起的話所有條件就是且關係,or就是或關係 36 return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])); 37 } 38 }; 39 return drugApplyRepository.findAll(querySpecifi, pageable); 40 }
問題:現在有一個新的需要實現CURD的對象使用的是聯合主鍵的方式 ,再直接使用之前的條件創建方法root.get(name) 就會報 org.springframework.data.mapping.PropertyReferenceException: No property name found for type 的錯誤 。但我仍然想繼續通過一個查詢實體類,根據實體類中的字段動態生成查詢條件。查詢瞭解到對於這種聯合主鍵其中某一個字段作爲查詢條件的創建格式是如下方式
root.get(<attribute embedded>).get(<subkey>)
因此,將上面的getDataList方法修改一下,引入對於EmbedCondition自定義註解的判斷,進而正確創建查詢條件,主要代碼如下:
private Page<DeepSkuInfo> getDataList(DeepSkuInfoQuery query, Pageable pageable) { Specification<DeepSkuInfo> querySpecifi = new Specification<DeepSkuInfo>() { public Predicate toPredicate(Root<DeepSkuInfo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> predicates = new ArrayList<>(); if (!StringUtils.isEmpty(query.getBeginDate())) { //大於或等於傳入時間 predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("lastModifyTime").as(Date.class), query.getBeginDate())); } if (!StringUtils.isEmpty(query.getEndDate())) { //小於或等於傳入時間 predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("lastModifyTime").as(Date.class), query.getEndDate())); } if (query != null) { Class<? extends DeepSkuInfoQuery> clazz = query.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field tmpField : fields) { tmpField.setAccessible(true); try { NotCondition annotation = (NotCondition) tmpField.getDeclaredAnnotation(NotCondition.class); if (tmpField.get(query) != null && annotation == null) { String name = tmpField.getName(); EmbedCondition embedCondition = (EmbedCondition) tmpField.getDeclaredAnnotation(EmbedCondition.class); if(query.getIsFuzzySearch()){ // 模糊匹配 if(embedCondition!=null){ //表示該查詢字段是聯合主鍵中的字段 predicates.add(criteriaBuilder.like(root.get(embedCondition.superClassName()).get(name).as(String.class), "%"+ tmpField.get(query) + "%")); }else{ predicates.add(criteriaBuilder.like(root.get(name).as(String.class), "%"+ tmpField.get(query) + "%")); } }else{ //精準查找 if(embedCondition!=null) { //表示該查詢字段是聯合主鍵中的字段 predicates.add(criteriaBuilder.equal(root.get(embedCondition.superClassName()).get(name), tmpField.get(query))); }else{ predicates.add(criteriaBuilder.equal(root.get(name), tmpField.get(query))); } } } } catch (Exception e) { log.error("構建查詢對象失敗",e); } } } // and到一起的話所有條件就是且關係,or就是或關係 return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])); } }; return deepSkuInfoRepository.findAll(querySpecifi,pageable); }
自定義註解代碼如下:
/** * 在jpa 條件查詢反射時,針對使用聯合主鍵的字段,由於需要使用 root.get(<attribute embedded>).get(<subkey>) 的形式,因此增加此註解,方便拼接查詢條件使用 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface EmbedCondition { String superClassName(); }