美文网首页
利用java反射动态拼接JPA查询条件

利用java反射动态拼接JPA查询条件

作者: ivms8200 | 来源:发表于2020-05-09 18:44 被阅读0次

    起因

    假设有个需求是需要按照条件查询数据库的数据。一般来说我们可以根据指定的参数去创建JPA查询条件。就像这样:

    if (StringUtils.isNotBlank(pageCgRecordInfoValidator.getCgmc())) {
      list.add(cb.like(root.get("cgmc"), "%" + pageCgRecordInfoValidator.getCgmc() + "%"));
    }
    

    如果有多个参数,那么就要写很多行这样的代码。这样实在是太麻烦了。能不能写一个方法,让他去动态拼接查询参数?这时候就可以用到反射了。

    实现

    首先我们约定,把参数放到一个对象里面,这个对象可以是DTO BO DO等。然后我们通过反射去解析这个对象,如果该对象的某个属性有值,我们就在List<Predicate> list中添加一条查询条件。
    代码如下

    package com.*****.utils;
    import org.apache.commons.lang3.StringUtils;
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JpaUtils {
    
        /**
         * 获取List<Predicate>,暂时只支持String,Integer,Long,Double,Short,Date
         * 只适用于全包装类属性的对象
         * 该方法暂时不适用于数字或者日期之间查询,如需查询要单独将条件加入predicateList
         *
         * @param o
         * @param root
         * @param cb
         * @return
         */
        public static List<Predicate> getPredicateList(Object o, Root root, CriteriaBuilder cb) {
            //集合 用于封装查询条件
            List<Predicate> list = new ArrayList<>();
            // 得到类对象
            Class objClass = (Class) o.getClass();
            /* 得到类中的所有属性集合 */
            Field[] fs = objClass.getDeclaredFields();
            for (int i = 0; i < fs.length; i++) {
                Field f = fs[i];
                f.setAccessible(true); // 设置些属性是可以访问的
                Object value;
                try {
                    value = f.get(o);
                    // 得到此属性的键
                    String key = f.getName();
                    //得到此属性的类型
                    String type = f.getType().toString();
                    String substring = type.substring(type.lastIndexOf(".") + 1);
                    switch (substring) {
                        case "String":
                            String trValue = (String) value;
                            if (StringUtils.isNotBlank(trValue)) {
                                list.add(cb.like(root.get(key), "%" + trValue + "%"));
                            }
                            break;
                        case "Date":
                        case "Integer":
                        case "Long":
                        case "Double":
                        case "Short":
                            if (value != null) {
                                list.add(cb.equal(root.get(key), value));
                            }
                            break;
                        default:
                            System.out.println("不适用");
                    }
    
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return list;
        }
    }
    
    

    因为项目中还没遇到其他的类型,所以暂时只写了对以上几种类型的处理。
    使用方法如下,是不是方便很多了?

        /**
         * 根据参数查询一条数据
         */
        @Override
        public List<CgRecordInfo> findByParam(CgRecordInfoDTO cgRecordInfoDTO) {
            //构造自定义查询条件
            // Root 用于获取属性字段,CriteriaQuery可以用于简单条件查询,CriteriaBuilder 用于构造复杂条件查询
            Specification<CgRecordInfo> specification = (root, query, cb) -> {
                List<Predicate> predicateList = JpaUtils.getPredicateList(cgRecordInfoDTO, root, cb);
                return cb.and(predicateList.toArray(new Predicate[0]));
            };
            return cgRecordInfoRepository.findAll(specification);
        }
    

    测试

    经测试,该方式几乎不会有性能损失。

    注意

    实体最好使用包装类,这样便于判断是否有传入值作为查询条件。
    该方法暂时不适用于数字或者日期之间查询。
    如果有特殊需求,可以在获取List<Predicate> predicateList后手动添加查询条件。比如我要查询某个时间范围内的数据

    if(pageCgRecordInfoValidator.getUpdateTimeStart() != null && pageCgRecordInfoValidator.getUpdateTimeEnd() != null){
      predicateList.add(cb.between(root.get("updateTime"), pageCgRecordInfoValidator.getUpdateTimeStart(), pageCgRecordInfoValidator.getUpdateTimeEnd()));
    }
    

    相关文章

      网友评论

          本文标题:利用java反射动态拼接JPA查询条件

          本文链接:https://www.haomeiwen.com/subject/caklnhtx.html