背景:前面有写过一篇使用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(); }