使用Jpa封装复杂查询

1 首先封装查询对象,前台传来的对象的数据结构为

能实现以下搜索功能

对应后端的类结构为

2 实现的功能:

查询,排序,分页,配置查询后需要显示的字段,可以fetch当前类中fetchType=lazy的字段;

import cn.topcheer.ivms.pojo.search.JpaClassify;
import cn.topcheer.ivms.pojo.search.JpaConditionType;
import cn.topcheer.ivms.pojo.search.JpaPageRequest;
import cn.topcheer.ivms.pojo.search.JpaSort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

import javax.persistence.*;

import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.util.*;

/**
 * jpa搜索工具类
 * Created by wmh on 2018/11/12.
 */
public class JpaSearchHelper<T> {
    private static final Logger logger = LoggerFactory.getLogger(JpaSearchHelper.class);
    /**
     * 搜索
     * @param jpaPageRequest 搜索的相关条件
     * @param clazz 当前查询的类
     * @param em EntityManager
     * @param fetchFields fetch当前类中fetch=lazy的属性
     * @param selectFields 查询后需要显示的字段
     * @param <T>
     * @return
     */
    public static<T> Page search(JpaPageRequest jpaPageRequest,Class<T> clazz,EntityManager em,String fetchFields,String selectFields){
        try {
            List<Tuple> list = build(em,clazz,jpaPageRequest,fetchFields,selectFields);
            return toPage(selectFields,list,jpaPageRequest);
        }catch (Exception e){
            logger.error("search error:"+e);
            return null;
        }
    }

    /**
     * 使用entityManager构建查询
     * @param em
     * @param clazz
     * @param jpaPageRequest
     * @param fetchFields
     * @param selectFields
     * @param <T>
     * @return
     */
    private static <T> List build(EntityManager em, Class clazz, JpaPageRequest jpaPageRequest, String fetchFields, String selectFields) throws Exception {
        try {
            if (jpaPageRequest != null) {
                CriteriaBuilder cb = em.getCriteriaBuilder();
                //列表查询
                CriteriaQuery criteriaQuery;
                if (selectFields != null && !selectFields.isEmpty()) {
                    criteriaQuery = cb.createTupleQuery();
                } else {
                    criteriaQuery = cb.createQuery(clazz);
                }

                //from
                Root<T> root = criteriaQuery.from(clazz);

                //fetch
                addFetchFields(root, fetchFields);

                //select
                List<Selection<?>> selections = addSelectFields(root, selectFields);
                if (selections != null && selections.size() > 0) {
                    criteriaQuery.multiselect(selections);
                } else {
                    criteriaQuery.select(root);
                }

                //where
                Predicate predicate = toPredicate(root, cb, clazz, jpaPageRequest);
                if (predicate != null) {
                    criteriaQuery.where(predicate);
                }

                //order by
                List<Order> orders = addOrders(root, jpaPageRequest.getSorts(), cb);
                if (orders != null && orders.size() > 0) {
                    criteriaQuery.orderBy(orders);
                }
                criteriaQuery.distinct(true);

                TypedQuery query = em.createQuery(criteriaQuery);
                query.setFirstResult(jpaPageRequest.getPage() * jpaPageRequest.getSize());
                query.setMaxResults(jpaPageRequest.getSize());
                return query.getResultList();
            }
            return null;
        }catch (Exception e){
            throw new Exception("build search error"+e);
        }
    }

    /**
     * @param root
     * @param fetchFields
     * @param <T>
     */
    private static <T> void addFetchFields(Root root,String fetchFields){
        if(fetchFields!=null && !fetchFields.isEmpty()) {
            String[] splitFetchFields = fetchFields.split(",");
            //此处若用root.fetch,分页会出现问题
            Join join = root.join(splitFetchFields[0],JoinType.LEFT);
            for(int i=1;i<splitFetchFields.length;i++) {
                join = join.join(splitFetchFields[i], JoinType.LEFT);
            }
        }
    }

    /**
     * 添加排序
     * @param root
     * @param jpaSorts
     * @param criteriaBuilder
     * @param <T>
     * @return
     */
    private static <T> List addOrders(Root root, List<JpaSort> jpaSorts, CriteriaBuilder criteriaBuilder){
        if(jpaSorts!=null && jpaSorts.size()>0) {
            List result = new ArrayList();
            for (JpaSort jpaSort : jpaSorts) {
                String field = jpaSort.getField();
                Path path = getPath(field, root);

                if (path != null && jpaSort.getDirection()!=null) {
                    if (jpaSort.getDirection().equals("ASC")) {
                        result.add(criteriaBuilder.asc(path));
                    } else if (jpaSort.getDirection().equals("DESC")) {
                        result.add(criteriaBuilder.desc(path));
                    }

                }
            }
            return result;
        }
        return null;
    }

    /**
     * 添加select所需字段
     * 可以在filterFields中添加别名,例如name as 名称...暂未实现
     * @param root
     * @param selectFields
     * @param <T>
     */
    private static <T> List addSelectFields(Root root, String selectFields){
        List result = new ArrayList();
        if(selectFields!=null && !selectFields.isEmpty()) {
            String[] splitSelectFields = selectFields.split(",");
            for (String field : splitSelectFields) {
                Selection selection = getPath(field, root).alias(field);
                if (selection != null) {
                    result.add(selection);
                }
            }
        }
        return result;
    }

    private static<T> Predicate toPredicate(Root root, CriteriaBuilder criteriaBuilder, Class clazz, JpaPageRequest jpaPageRequest){
        if(jpaPageRequest!=null) {
            Predicate orPredicate = null;
            Predicate andPredicate = null;
            List<Predicate> predicateOrList = new ArrayList<>();
            List<Predicate> predicateAndList = new ArrayList<>();

            String filter = jpaPageRequest.getFilter();
            List<JpaClassify> jpaClassifyList = jpaPageRequest.getClassifyList();

            if (jpaPageRequest.getFilter() != null && !"".equals(filter)) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.getType().equals(String.class) && !field.getName().equals("id") && !field.getName().equals("serialVersionUID")) {
                        Predicate predicate = addPredicate(root, criteriaBuilder, field.getName(), field.getType(), jpaPageRequest.getFilter(), JpaConditionType.LIKE);
                        if (predicate != null) {
                            predicateOrList.add(predicate);
                        }
                    }
                }

                //将所有filter条件用 or 联合起来
                if (predicateOrList.size() > 0) {
                    orPredicate = criteriaBuilder.or(predicateOrList.toArray(new Predicate[predicateOrList.size()]));
                }
            }
            if (jpaClassifyList != null && jpaClassifyList.size() > 0) {
                for (JpaClassify jpaClassify : jpaClassifyList) {
                    //暂未用到,预留
                    Class<?> c = null;
//                            try {
//                                c = clazz.getDeclaredField(jpaClassify.getField()).getType();
//                            } catch (NoSuchFieldException e) {
//                                e.printStackTrace();
//                            }
                    Predicate predicate = addPredicate(root, criteriaBuilder, jpaClassify.getField(), c, jpaClassify.getValue(), jpaClassify.getCondition());
                    if (predicate != null) {
                        predicateAndList.add(predicate);
                    }
                }
                //将classifies条件用 and 联合起来
                if (predicateAndList.size() > 0) {
                    andPredicate = criteriaBuilder.and(predicateAndList.toArray(new Predicate[predicateAndList.size()]));
                }
            }

            if(andPredicate!=null && orPredicate!=null){
                return criteriaBuilder.and(andPredicate,orPredicate);
            }else {
                if(andPredicate==null && orPredicate!=null){
                    return orPredicate;
                }else if(orPredicate==null && andPredicate!=null){
                    return andPredicate;
                }
            }
            return null;
        }
        return null;
    }

    private static<T> Predicate addPredicate(Root<T> root, CriteriaBuilder criteriaBuilder, String field, Class<?> tClass, Object value, JpaConditionType condition){
        Path path = getPath(field,root);

        if(path!=null && value!=null) {
            switch (condition) {
                case LIKE:
                    return criteriaBuilder.like(path, "%" + value + "%");
                case EQUAL:
                    return criteriaBuilder.equal(path, value);
                case NOT_EQUATE:
                    return criteriaBuilder.notEqual(path, value);
                case GREATER_THAN:
                    //日期类型未测试
                    return criteriaBuilder.greaterThan(path, (Comparable) value);
                case GREATER_THAN_OR_EQUAL_TO:
                    return criteriaBuilder.greaterThanOrEqualTo(path, (Comparable) value);
                case LESS_THAN:
                    return criteriaBuilder.lessThan(path, (Comparable) value);
                case LESS_THAN_OR_EQUAL_TO:
                    return criteriaBuilder.lessThanOrEqualTo(path, (Comparable) value);
//               if(tClass.equals(Date.class)){
//                   Date date = null;
//                   SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//                   try {
//                       date = sdf.parse(value);
//                   } catch (ParseException e) {
//                       e.printStackTrace();
//                   }
//                   return criteriaBuilder.lessThanOrEqualTo(root.get(field).as(Date.class),date);
//               }else {
//                   return criteriaBuilder.lessThanOrEqualTo(root.get(field).as(Double.class),Double.parseDouble(value));
//               }
            }
        }
       return null;
    }

    private static <T> Path getPath(String field,Root root){
        if(field!=null) {
            String[] fields = field.split("\\.");
            Path path = null;
            if (fields.length > 1) {
                Join join = null;
                Set<Join<T, ?>> joinSet = root.getJoins();
                //重要:检查join是否已经存在,存在的话就不需要join
                String joinName = fields[fields.length - 2];
                boolean joinFlag = false;
                Iterator iterator = joinSet.iterator();
                while (iterator.hasNext()) {
                    join = (Join) iterator.next();
                    if (joinName.equals(join.getAttribute().getName())) {
                        joinFlag = true;
                        break;
                    }
                }

                if (!joinFlag) {
                    join = root.join(fields[0], JoinType.LEFT);
                    for (int i = 1; i < fields.length - 1; i++) {
                        join = join.join(fields[i], JoinType.LEFT);
                    }

                }
                path = join.get(fields[fields.length - 1]);

            } else {
                path = root.get(fields[0]);
            }
            return path;
        }
        return null;

    }

    private static <T> Page toPage(String selectFields,List<Tuple> list,JpaPageRequest jpaPageRequest){
        Sort sort = jpaPageRequest.generateSort();
        PageRequest pageRequest = new PageRequest(jpaPageRequest.getPage(), jpaPageRequest.getSize(), sort);
        Page results = null;
        if(selectFields!=null && !selectFields.isEmpty()) {
            //将查询结果转成List<map>
            List<Map<String, Object>> dataList = new ArrayList<>();
            if (list != null && !list.isEmpty()) {
                for (Tuple tu : list) {
                    Map<String, Object> itemmap = new HashMap<>();
                    for (TupleElement element : tu.getElements()) {
                        try {
                            itemmap.put(element.getAlias(), tu.get(element.getAlias()));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    dataList.add(itemmap);
                }
            }

            results = new PageImpl<>(dataList,pageRequest,jpaPageRequest.getSize());
        } else {
            results = new PageImpl<>(list,pageRequest,jpaPageRequest.getSize());
        }
        return results;
    }
}

3 难点

(1)生成sql时,语句中会有多余的letf join代码,在查看Specification的实现时,发现可以对join进行检查,当join已经存在时,使用已有的join不要添加新的join,具体代码在getPath方法中

(2)使用fetch时,分页出现错误,root.fetch的实现使用join实现,尝试使用root.join实现fetch的功能,发现可以解决分页问题,注意要去除重复项criteriaQuery.distinct(true);

猜你喜欢

转载自blog.csdn.net/u013984781/article/details/84651709