我们在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 + '}'; } }