实现一个通用分页查询接口

我们使用的持久层框架是:jpa + hibernate 经常使用到里面的动态查询。

最开始使用的时候觉得:窝草!还能这样玩。

写了一年多代码后.....

尼玛,不想写了。

于是乎,我最近想写个通用型的分页查询接口,只需要传递一个实体类就执行查询了,

再也不需要为了做一个查询还写repository,serivec,serviceImpl。想想都开心!

于是乎我决定先做一个demo版:

由于对jpa、hibernate源码,反射使用的都还不熟.....导致了一些问题,我这里也统统记录下来;

思路以及版本演进:
0.1:在原有方法上进行改进,将原来的从实体类上获取字段以及属性值改为通过反射获取;

问题:需要创建相关实体类的repository接口!还需要依赖接口调用,而通过反射调用方法的时候需要对象实例化,接口不能被实例化;
改进:尝试通过spring注入JpaSpecificationExecutor接口,没有成功!
0.2:修改思路,通过实例化JpaSpecificationExecutor 的实现类 SimpleJpaRepository ,直接调用findAll;
问题:SimpleJpaRepository 的构造函数中需要 EntityManager,尝试多个方法后使用了注解 @PersistenceContext 获取到了实例;
代码(只对String类型的属性做模糊查询):
这不是一个工具类,而是一个service实现类。因为注入功能需要加载到Spring容器中。
反射获取不到父类中的属性,介于我们先在项目中的实体id是继承而来,下面的代码做了特殊处理;
@PersistenceContext
private EntityManager entityManager;

/**
 * 通过反射匹配对象中的属性分页查询数据库
 *
 * @param entity   实体类
 * @param pageable 分页对象
 * @return
 */
@Override
public Object findByPage(final Object entity, Pageable pageable) {
    final Class<?> entityClass = entity.getClass();
    SimpleJpaRepository simpleJpaRepository = new SimpleJpaRepository(entityClass, entityManager);
    return simpleJpaRepository.findAll(new Specification() {
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
            List<Predicate> predicateList = new ArrayList<>();
            Field[] fields = entityClass.getDeclaredFields();
            //属性名
            String fieldName;
            //方法名
            String getMethodName;
            //方法
            Method method;
            //属性值
            Object value;
            //属性类型
            String type;
            try {
                //获取父类以及继承属性
                Class<?> supper = entityClass.getSuperclass();
                //获取继承的id属性
                Field id = supper.getDeclaredField("id");
                type = id.getGenericType().toString();
                Method sm = supper.getDeclaredMethod("getId");
                value = sm.invoke(entity);
                if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
                    Path p = root.get("id");
                    predicateList.add(cb.equal(p, value.toString()));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            for (Field field : fields) {
                fieldName = field.getName();
                type = field.getGenericType().toString();
                getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                try {
                    method = entityClass.getDeclaredMethod(getMethodName);
                    value = method.invoke(entity);
                    if (value == null) {
                        continue;
                    }
                    if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
                        Path p = root.get(fieldName);
                        //对id做eq对比 非id的做like对比
                        predicateList.add(cb.like(p, "%" + value.toString() + "%"));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            query.where(predicateList.toArray(new Predicate[predicateList.size()]));
            return query.getRestriction();

        }
    }, pageable);
}

0.3:实体类是由 EntityManager 来管理的,既然已经获取到了EntityManager那我们就直接拼JPQL查询吧,没必要再封装一次了!

代码如下:
/**
 * 根据对象属性的值通过entityManager查询
 *
 * @param entity
 * @param searchParam
 * @return
 */
@Override
public Object findByPageEntity(Object entity, SearchParam searchParam) {
    Class<?> entityClass = entity.getClass();
    Field[] fields = entityClass.getDeclaredFields();
    //属性名
    String fieldName;
    //方法名
    String getMethodName;
    //方法
    Method method;
    //属性值
    Object value;
    //属性类型
    String type;
    //标记是否有where条件
    boolean w = true;
    //构建查询JPQL语句
    StringBuffer jpql = new StringBuffer("SELECT c FROM " + entityClass.getName() + " c ");
    StringBuffer where = new StringBuffer();
    Map<String, Object> map = new HashedMap();
    try {
        //获取父类以及继承属性
        Class<?> supper = entityClass.getSuperclass();
        //获取继承的id属性
        Field id = supper.getDeclaredField("id");
        type = id.getGenericType().toString();
        Method sm = supper.getDeclaredMethod("getId");
        value = sm.invoke(entity);
        if (value != null && type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
            w = false;
            where.append(" c.id").append(" = :id AND");
            map.put("id", value);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    for (Field field : fields) {
        fieldName = field.getName();
        type = field.getGenericType().toString();
        getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        try {
            method = entityClass.getDeclaredMethod(getMethodName);
            value = method.invoke(entity);
            if (value == null) {
                continue;
            }
            //字符串类型的参数才允许模糊查询
            if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
                map.put(fieldName, "%" + value.toString() + "%");
                where.append(" c.").append(fieldName).append(" LIKE :").append(fieldName).append(" AND");
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //去掉多余的and
    if (!w) {
        jpql.append("WHERE");
        where.delete(where.length() - 4, where.length());
        jpql.append(where);
    }
    List<OrderFiled> orderFiles = searchParam.getOrderFiled();
    if (orderFiles.size() > 0) {
        jpql.append(" ORDER BY");
        //拼接排序语句 key为排序字段 value为排序类型
        for (OrderFiled orderFiled : orderFiles) {
            jpql.append(" c.").append(orderFiled.getOrderFiled()).append(" ").append(orderFiled.getOrderType()).append(" ,");
        }
        jpql.deleteCharAt(jpql.length() - 1);
    }
    Query query = entityManager.createQuery(jpql.toString());
    //循环map为查询语句条件赋值
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        query.setParameter(entry.getKey(), entry.getValue());
    }
    Integer number = searchParam.getPageNumber() - 1;
    Integer size = searchParam.getPageSize();
    query.setFirstResult(number * size);
    query.setMaxResults(size);
    return query.getResultList();
}
0.4:自定义字段匹配条件,通过字段注解来实现
自定义注解类
/**
 * 用于实体类属性查询时的判断条件
 * Created by nankeyimeng on 6/28/2017.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SearchLimit {
    /**
     * 条件枚举
     * 分别对应模糊查询,等于,不等于,大于,大于等于,小于,小于等于
     *
     * @author peida
     */
    enum Where {
        like, eq, ne, gt, ge, lt, le
    }

    Where searchLimit() default Where.eq;
}

demo出炉,这里的排序和分页是自己封装的对象

/**
 * 根据对象属性的值通过entityManager查询
 *
 * @param entity
 * @param searchParam
 * @return
 */
@Override
public Object findByPageEntity(Object entity, SearchParam searchParam) {
    Class<?> entityClass = entity.getClass();
    Field[] fields = entityClass.getDeclaredFields();
    //属性名
    String fieldName;
    //方法名
    String getMethodName;
    //方法
    Method method;
    //属性值
    Object value;
    //属性类型
    String type;
    //标记是否有where条件
    boolean w = true;
    //构建查询JPQL语句
    StringBuffer jpql = new StringBuffer("SELECT c FROM " + entityClass.getName() + " c ");
    StringBuffer where = new StringBuffer();
    Map<String, Object> map = new HashedMap();
    try {
        //获取父类以及继承属性
        Class<?> supper = entityClass.getSuperclass();
        //获取继承的id属性
        Field id = supper.getDeclaredField("id");
        type = id.getGenericType().toString();
        Method sm = supper.getDeclaredMethod("getId");
        value = sm.invoke(entity);
        if (value != null && type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
            w = false;
            where.append(" c.id").append(" = :id AND");
            map.put("id", value);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    for (Field field : fields) {
        fieldName = field.getName();
        if (field.isAnnotationPresent(SearchLimit.class)) {
            type = field.getGenericType().toString();
            getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            //获取自定义注解
            SearchLimit searchLimit = field.getAnnotation(SearchLimit.class);
            //获取注解值
            String limitAnnotation = searchLimit.searchLimit().toString();
            try {
                method = entityClass.getDeclaredMethod(getMethodName);
                value = method.invoke(entity);
                if (value == null) {
                    continue;
                }
                switch (limitAnnotation) {
                    case "like":
                        if (w) {
                            w = false;
                        }
                        //字符串类型的参数才允许模糊查询
                        if (type.equals("class java.lang.String") && StringUtils.isNotBlank(value.toString())) {
                            map.put(fieldName, "%" + value.toString() + "%");
                            where.append(" c.").append(fieldName).append(" LIKE :").append(fieldName).append(" AND");
                        }
                        break;
                    case "eq":
                        if (w) {
                            w = false;
                        }
                        map.put(fieldName, value);
                        where.append(" c.").append(fieldName).append(" = :").append(fieldName).append(" AND");
                        break;
                    case "ne":
                        if (w) {
                            w = false;
                        }
                        map.put(fieldName, value);
                        where.append(" c.").append(fieldName).append(" != :").append(fieldName).append(" AND");
                        break;
                    case "gt":
                        if (w) {
                            w = false;
                        }
                        map.put(fieldName, value);
                        where.append(" c.").append(fieldName).append(" > :").append(fieldName).append(" AND");
                        break;
                    case "ge":
                        if (w) {
                            w = false;
                        }
                        map.put(fieldName, value);
                        where.append(" c.").append(fieldName).append(" >= :").append(fieldName).append(" AND");
                        break;
                    case "lt":
                        if (w) {
                            w = false;
                        }
                        map.put(fieldName, value);
                        where.append(" c.").append(fieldName).append(" < :").append(fieldName).append(" AND");
                        break;
                    case "le":
                        if (w) {
                            w = false;
                        }
                        map.put(fieldName, value);
                        where.append(" c.").append(fieldName).append(" <= :").append(fieldName).append(" AND");
                        break;
                    default:
                        //没有加注解的不加入查询条件
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //去掉多余的and
    if (!w) {
        jpql.append("WHERE");
        where.delete(where.length() - 4, where.length());
        jpql.append(where);
    }
    List<OrderFiled> orderFiles = searchParam.getOrderFiled();
    if (orderFiles.size() > 0) {
        jpql.append(" ORDER BY");
        //拼接排序语句 key为排序字段 value为排序类型
        for (OrderFiled orderFiled : orderFiles) {
            jpql.append(" c.").append(orderFiled.getOrderFiled()).append(" ").append(orderFiled.getOrderType()).append(" ,");
        }
        jpql.deleteCharAt(jpql.length() - 1);
    }
    Query query = entityManager.createQuery(jpql.toString());
    //循环map为查询语句条件赋值
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        query.setParameter(entry.getKey(), entry.getValue());
    }
    Integer number = searchParam.getPageNumber() - 1;
    Integer size = searchParam.getPageSize();
    query.setFirstResult(number * size);
    query.setMaxResults(size);
    return query.getResultList();
}
测试代码:
实体类:
@Entity
@DynamicInsert(true)
@DynamicUpdate(true)
@Table(name = "UM_SYS_USER_MEMBER")
public class SysUserMember extends IdEntity implements java.io.Serializable {

    private static final long serialVersionUID = 1531492635701976701L;
    private String userName;//用户名(昵称)
    private String userPassword;
    private String userSalt;
    private List<SysRole> sysRoles = new ArrayList<SysRole>();
    private List<SysResource> sysResources = new ArrayList<SysResource>();
    private List<SysOrganization> sysOrganizations = new ArrayList<SysOrganization>();
    private String available;
    private String alias;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+08:00")
    @SearchLimit(searchLimit = SearchLimit.Where.lt)
    private Date createTime;
    @SearchLimit(searchLimit = SearchLimit.Where.eq)
    private Integer isAdmin;
  ........

测试方法

@Test
public void findByPageEntity() throws Exception {
    SysUserMember sysUserMember = new SysUserMember();
    sysUserMember.setIsAdmin(1);
    sysUserMember.setCreateTime(new Date());
    List<OrderFiled> order = new ArrayList<>();
    order.add(new OrderFiled("createTime", "asc"));
    order.add(new OrderFiled("isAdmin", "desc"));
    SearchParam searchParam = new SearchParam(1, 10, order);
    Object object = searchService.findByPageEntity(sysUserMember, searchParam);
    System.out.print(object.toString());
}

测试结果符合预期,就先酱紫了。。。。已知的缺陷还是蛮多了,比如范围之类的怎么处理等等......等我有时间了再改吧。欢迎大家提意见啊.....提想法

 

posted @ 2017-07-27 21:55  Java界的小菜鸟  阅读(10215)  评论(0编辑  收藏  举报