JPA/Hibernate环境的复杂查询

我们在SpringData/JPA/Hibernate环境中,对于一些简单的增删改查可以使用Repository搞定,更复杂的查询可以使用JpaSpecificationExecutor等查询工具搞定,但是更复杂的,比如多表关联查询就有点儿力不从心了。今日主角闪亮登场。

package com.palmte.tcm.utils;

import com.palmte.tcm.pojo.Page;
import org.hibernate.Session;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author xiongshiyan at 2018/5/9
 * @see QueryHelper
 * @see Record
 * SQL语句用{@see QueryHelper}封装。结果集用JavaBean或者{@see Record}来封装。JavaBean需要保证别名就是属性。
 * 可以使用{@see QueryHelper}完全处理参数,也可以不处理,支持?和:的方式
 */
@Component
public class Pagination {
    @Autowired
    @PersistenceContext
    private EntityManager entityManager;

    public Pagination(EntityManager entityManager){
        this.entityManager = entityManager;
    }
    public Pagination(){}

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }


    /**
     * 返回查询的一个Record,没有则为null
     */
    public Record findFirst(String sql , Object... params){
        return findFirst(sql , Record.class , params);
    }
    public Record findFirst(String sql , Map<String , Object> searchMap){
        return findFirst(sql , Record.class , searchMap);
    }

    /**
     * 返回查询的一个实体,没有则为null
     */
    public <T> T findFirst(String sql , Class<T> clazz , Object... params){
        List<T> ts = find(sql, clazz, params);
        return (ts == null || ts.size() == 0) ? null : ts.get(0);
    }
    public <T> T findFirst(String sql , Class<T> clazz ,Map<String , Object> searchMap){
        List<T> ts = find(sql, clazz, searchMap);
        return (ts == null || ts.size() == 0) ? null : ts.get(0);
    }


    public List<Record> find(String sql , Object... params){
        return find(sql, Record.class , params);
    }
    public List<Record> find(String sql , Map<String , Object> searchMap){
        return find(sql, Record.class , searchMap);
    }
    public List<Record> find(String sql){
        return find(sql, Record.class , (Map<String , Object>)null);
    }

    /**
     * 查询列表
     * @param sql native sql语句,可以包含?
     * @param clazz 返回的类型,可以是JavaBean,可以是Record
     * @param params 参数列表
     * @param <T> 泛型
     * @return 查询列表结果
     */
    public <T> List<T> find(String sql , Class<T> clazz , Object... params){
        Session session = entityManager.unwrap(Session.class);
        org.hibernate.Query query = session.createSQLQuery(sql);

        //0-Based
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i , params[i]);
        }

        List list = getList(query, clazz);

        return list;
    }
    /**
     * 查询列表
     * @param sql native sql语句,可以包含 :具名参数
     * @param clazz 返回的类型,可以是JavaBean,可以是Record
     * @param searchMap 具名参数列表
     * @param <T> 泛型
     * @return 查询列表结果
     */
    public <T> List<T> find(String sql , Class<T> clazz , Map<String , Object> searchMap){
        Session session = entityManager.unwrap(Session.class);
        org.hibernate.Query query = session.createSQLQuery(sql);

        if(null != searchMap) {
            searchMap.forEach(query::setParameter);
        }

        List list = getList(query, clazz);

        return list;
    }



    /**----------------------------------------------record-positioned-parameter---------------------------------------------------*/
    public Page<Record> paginate( String nativeSQL,int pageNumber, int pageSize, Object... params){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( null, nativeSQL, nativeCountSQL, Record.class,pageNumber, pageSize, params);
    }

    public Page<Record> paginate( String nativeSQL, Boolean isGroupBySql, int pageNumber, int pageSize, Object... params){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( isGroupBySql, nativeSQL, nativeCountSQL, Record.class, pageNumber, pageSize,params);
    }
    public Page<Record> paginate( String nativeSQL, String nativeCountSQL, int pageNumber, int pageSize, Object... params){
        return paginate( null, nativeSQL, nativeCountSQL, Record.class, pageNumber, pageSize,params);
    }

    public Page<Record> paginate( Boolean isGroupBySql, String nativeSQL ,String nativeCountSQL ,int pageNumber, int pageSize, Object... params){
        return paginate( isGroupBySql, nativeSQL, nativeCountSQL, Record.class,pageNumber, pageSize, params);
    }

    /**----------------------------------------------record-maped-parameter---------------------------------------------------*/
    public Page<Record> paginate( String nativeSQL,int pageNumber, int pageSize, Map<String , Object> searchMap){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( null, nativeSQL, nativeCountSQL, Record.class,pageNumber, pageSize, searchMap);
    }

    public Page<Record> paginate( String nativeSQL, Boolean isGroupBySql, int pageNumber, int pageSize, Map<String , Object> searchMap){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( isGroupBySql, nativeSQL, nativeCountSQL, Record.class, pageNumber, pageSize,searchMap);
    }
    public Page<Record> paginate( String nativeSQL, String nativeCountSQL, int pageNumber, int pageSize, Map<String , Object> searchMap){
        return paginate( null, nativeSQL, nativeCountSQL, Record.class, pageNumber, pageSize,searchMap);
    }

    public Page<Record> paginate( Boolean isGroupBySql, String nativeSQL ,String nativeCountSQL ,int pageNumber, int pageSize, Map<String , Object> searchMap){
        return paginate( isGroupBySql, nativeSQL, nativeCountSQL, Record.class,pageNumber, pageSize, searchMap);
    }


    /**----------------------------------------------JavaBean-positioned-parameter---------------------------------------------------*/
    public <T> Page<T> paginate( Boolean isGroupBySql, String nativeSQL , Class<T> clazz, int pageNumber, int pageSize,Object... params){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( isGroupBySql, nativeSQL, nativeCountSQL, clazz, pageNumber, pageSize,params);
    }
    public <T> Page<T> paginate( String nativeSQL ,String nativeCountSQL, Class<T> clazz,int pageNumber, int pageSize, Object... params){
        return paginate( null, nativeSQL, nativeCountSQL, clazz, pageNumber, pageSize, params);
    }

    public <T> Page<T> paginate(String nativeSQL , Class<T> clazz ,int pageNumber, int pageSize,  String... params){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( null, nativeSQL, nativeCountSQL ,clazz ,pageNumber, pageSize, params);
    }

    /**----------------------------------------------JavaBean-maped-parameter---------------------------------------------------*/
    public <T> Page<T> paginate( String nativeSQL , Class<T> clazz ,int pageNumber, int pageSize, Map<String , Object> searchMap){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( null, nativeSQL, nativeCountSQL ,clazz ,pageNumber, pageSize, searchMap);
    }
    public <T> Page<T> paginate( Boolean isGroupBySql, String nativeSQL , Class<T> clazz,int pageNumber, int pageSize, Map<String , Object> searchMap){
        String nativeCountSQL = getCountSQL(nativeSQL);
        return paginate( isGroupBySql, nativeSQL, nativeCountSQL, clazz, pageNumber, pageSize,searchMap);
    }
    public <T> Page<T> paginate( String nativeSQL ,String nativeCountSQL, Class<T> clazz,int pageNumber, int pageSize, Map<String , Object> searchMap){
        return paginate( null, nativeSQL, nativeCountSQL, clazz, pageNumber, pageSize, searchMap);
    }

    /**
     *
     * @param pageNumber pageNumber
     * @param pageSize pageSize
     * @param isGroupBySql 是否包含Group by语句,影响总行数
     * @param nativeSQL 原生SQL语句 {@see QueryHelper}
     * @param nativeCountSQL 原生求总行数的SQL语句 {@see QueryHelper}
     * @param clazz JavaBean风格的DTO或者Record,需要用别名跟JavaBean对应
     * @param <T> 返回JavaBean风格的DTO或者Record
     * @param params 按照顺序给条件
     */
    public <T> Page<T> paginate( Boolean isGroupBySql, String nativeSQL, String nativeCountSQL , Class<T> clazz , int pageNumber, int pageSize, Object... params){
        if (pageNumber < 1 || pageSize < 1) {
            throw new IllegalArgumentException("pageNumber and pageSize must more than 0");
        }
        Query countQuery = entityManager.createNativeQuery(nativeCountSQL);

        //坑死人,1-Based
        for (int i = 1; i <= params.length; i++) {
            countQuery.setParameter(i , params[i-1]);
        }

        List countQueryResultList = countQuery.getResultList();
        int size = countQueryResultList.size();
        if (isGroupBySql == null) {
            isGroupBySql = size > 1;
        }

        long totalRow;
        if (isGroupBySql) {
            totalRow = size;
        } else {
            totalRow = (size > 0) ? ((Number)countQueryResultList.get(0)).longValue() : 0;
        }
        if (totalRow == 0) {
            return new Page<>(new ArrayList<>(0), pageNumber, pageSize, 0, 0);
        }

        int totalPage = (int) (totalRow / pageSize);
        if (totalRow % pageSize != 0) {
            totalPage++;
        }

        if (pageNumber > totalPage) {
            return new Page<>(new ArrayList<>(0), pageNumber, pageSize, totalPage, (int)totalRow);
        }

        Session session = entityManager.unwrap(Session.class);
        int offset = pageSize * (pageNumber - 1);
        org.hibernate.Query query = session.createSQLQuery(nativeSQL).setFirstResult(offset).setMaxResults(pageSize);

        //坑死人,0-Based
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i , params[i]);
        }

        final List list = getList(query, clazz);


        return new Page<T>(list, pageNumber, pageSize, totalPage, (int)totalRow);
    }
    /**
     *
     * @param pageNumber pageNumber
     * @param pageSize pageSize
     * @param isGroupBySql 是否包含Group by语句,影响总行数
     * @param nativeSQL 原生SQL语句 {@see QueryHelper}
     * @param nativeCountSQL 原生求总行数的SQL语句 {@see QueryHelper}
     * @param clazz JavaBean风格的DTO或者Record,需要用别名跟JavaBean对应
     * @param <T> 返回JavaBean风格的DTO或者Record
     * @param searchMap k-v条件
     */
    public <T> Page<T> paginate( Boolean isGroupBySql, String nativeSQL,String nativeCountSQL , Class<T> clazz ,int pageNumber, int pageSize, Map<String , Object> searchMap){
        if (pageNumber < 1 || pageSize < 1) {
            throw new IllegalArgumentException("pageNumber and pageSize must more than 0");
        }
        Query countQuery = entityManager.createNativeQuery(nativeCountSQL);

        if(null != searchMap) {
            searchMap.forEach(countQuery::setParameter);
        }

        List countQueryResultList = countQuery.getResultList();
        int size = countQueryResultList.size();
        if (isGroupBySql == null) {
            isGroupBySql = size > 1;
        }

        long totalRow;
        if (isGroupBySql) {
            totalRow = size;
        } else {
            totalRow = (size > 0) ? ((Number)countQueryResultList.get(0)).longValue() : 0;
        }
        if (totalRow == 0) {
            return new Page<>(new ArrayList<>(0), pageNumber, pageSize, 0, 0);
        }

        int totalPage = (int) (totalRow / pageSize);
        if (totalRow % pageSize != 0) {
            totalPage++;
        }

        if (pageNumber > totalPage) {
            return new Page<>(new ArrayList<>(0), pageNumber, pageSize, totalPage, (int)totalRow);
        }

        Session session = entityManager.unwrap(Session.class);
        int offset = pageSize * (pageNumber - 1);
        org.hibernate.Query query = session.createSQLQuery(nativeSQL).setFirstResult(offset).setMaxResults(pageSize);

        if(null != searchMap) {
            searchMap.forEach(query::setParameter);
        }

        final List list = getList(query, clazz);


        return new Page<T>(list, pageNumber, pageSize, totalPage, (int)totalRow);
    }

    private <T> List getList(org.hibernate.Query query, Class<T> clazz) {
        final List list;

        //Object[].class
        if(Object[].class == clazz){
            return query.list();
        }

        query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        List mapList = query.list();
        list = new ArrayList(mapList.size());
        mapList.forEach(map->{
            Map<String , Object> tmp = (Map<String , Object>) map;
            //Record.class
            if(Record.class == clazz){
                list.add(new Record(tmp));
                //Map及子类
            }else if(Map.class.isAssignableFrom(clazz)){
                list.add(tmp);
                //JavaBean风格
            }else {
                list.add(Map2Bean.convert(tmp , clazz));
            }
        });
        return list;
    }
    /*private <T> List getList(org.hibernate.Query query, Class<T> clazz) {
        final List list;

        if(Record.class == clazz){
            //返回Record
            query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
            List mapList = query.list();
            list = new ArrayList(mapList.size());
            mapList.forEach(map->{
                Map<String , Object> tmp = (Map<String , Object>) map;
                list.add(new Record(tmp));
            });
        }else {
            //返回JavaBean
            //只能返回简单的Javabean,不具备级联特性
            query.setResultTransformer(Transformers.aliasToBean(clazz));
            list = query.list();
        }
        return list;
    }*/






    private String getCountSQL(String sql){
        String countSQL = "SELECT COUNT(*) AS totalRow " + sql.substring(sql.toUpperCase().indexOf("FROM"));
        return  replaceOrderBy(countSQL);
    }

    protected static class Holder {
        private static final Pattern ORDER_BY_PATTERN = Pattern.compile(
                "order\\s+by\\s+[^,\\s]+(\\s+asc|\\s+desc)?(\\s*,\\s*[^,\\s]+(\\s+asc|\\s+desc)?)*",
                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    }

    public String replaceOrderBy(String sql) {
        return Holder.ORDER_BY_PATTERN.matcher(sql).replaceAll("");
    }
}
其中Page代表一个分页信息,感谢JFinal。

public class Page<T> implements Serializable {
   
   private static final long serialVersionUID = -5395997221963176643L;
   
   private List<T> list;           // list result of this page
   private int pageNumber;             // page number
   private int pageSize=10;            // result amount of this page
   private int totalPage;          // total page
   private int totalRow;           // total row


   public Page(int pageNumber) {
      this.pageNumber = pageNumber;
   }

   /**
    * Constructor.
    * @param list the list of paginate result
    * @param pageNumber the page number
    * @param pageSize the page size
    * @param totalPage the total page of paginate
    * @param totalRow the total row of paginate
    */
   public Page(List<T> list, int pageNumber, int pageSize, int totalPage, int totalRow) {
      this.list = list;
      this.pageNumber = pageNumber;
      this.pageSize = pageSize;
      this.totalPage = totalPage;
      this.totalRow = totalRow;
   }

   public Page(int pageNumber, int pageSize) {
      this.pageNumber = pageNumber;
      this.pageSize = pageSize;
   }
   
   /**
    * Return list of this page.
    */
   public List<T> getList() {
      return list;
   }
   
   /**
    * Return page number.
    */
   public int getPageNumber() {
      return pageNumber;
   }
   
   /**
    * Return page size.
    */
   public int getPageSize() {
      return pageSize;
   }
   
   /**
    * Return total page.
    */
   public int getTotalPage() {

      totalPage = totalRow / pageSize;
      if (totalRow % pageSize > 0) {
         totalPage++;
      }
      return totalPage;
   }
   
   /**
    * Return total row.
    */
   public int getTotalRow() {
      return totalRow;
   }
   
   public boolean isFirstPage() {
      return pageNumber == 1;
   }
   
   public boolean isLastPage() {
      return pageNumber == totalPage;
   }

   public void setList(List<T> list) {
      this.list = list;
   }

   public void setPageNumber(int pageNumber) {
      this.pageNumber = pageNumber;
   }

   public void setPageSize(int pageSize) {
      this.pageSize = pageSize;
   }

   public void setTotalPage(int totalPage) {
      this.totalPage = totalPage;
   }

   public void setTotalRow(int totalRow) {
      this.totalRow = totalRow;
   }

   @Override
   public String toString() {
      return "Page{" +
            "list=" + list +
            ", pageNumber=" + pageNumber +
            ", pageSize=" + pageSize +
            ", totalPage=" + totalPage +
            ", totalRow=" + totalRow +
            '}';
   }
}


猜你喜欢

转载自blog.csdn.net/xxssyyyyssxx/article/details/80696443