JPA不定项分页查询初学者分享
初学JPA,遇见分页查询需求:不定项、模糊、排序要求,如果没有不定项的需求,会很容易搞定:直接findByClassNameLikeAndParentIdAndHide(),传入分页参数Pageable就可以搞定,但是加上不定项只能自己手动写sql,手忙脚乱,网上查询了各位大神的文章,经整理实践,得出以下两种实现方式。Controller层我只写了参数合法性验证,Model层不需要写任何代码,service层代码如下:
/**
* 分页查询系统分类列表(方法一)
*
* @param className
* @param parentId
* @param hide
* @param pageNum
* @param pageSize
* @return
*/
@Transactional
public Map getList(String className, Integer parentId, Integer hide, int pageNum, int pageSize) {
//返回结果
Map resultMap = new HashMap<>();
StringBuilder jpql = new StringBuilder(" from SysClass s where 1=1 ");
List<Object> args = new ArrayList<>();//参数集合
int i = 0;//参数位置标记
//模糊查询
if(className != null){
// jpqlBuilder.append(" and s.className like %" + className + "% "); //存在sql注入风险
jpql.append(" and s.className like CONCAT('%',?").append(i).append(",'%') ");
args.add(className);
i++;
}
//指定查询
if(parentId != null){
jpql.append(" and s.parentId = ?").append(i++).append(" ");
args.add(parentId);
}
//controller层中对参数hide进行了验证,确保为0~2的整数,数据库中只保存0、1,全部查询参数为2
if(hide != 2){
jpql.append(" and s.hide = ?").append(i).append(" ");
args.add(hide);
}
//拼接排序方式
String orderJpql = " order by s.sort desc ,s.classId ";
//列表查询
Query query = em.createQuery(jpql.toString() + orderJpql );
//加入参数
for(int j = 0; j < args.size(); j++){
query.setParameter(j,args.get(j));
}
//加入分页信息
query.setFirstResult((pageNum - 1) * pageSize);
query.setMaxResults(pageSize);
List sysClassList = query.getResultList();
//结果处理,将需要展示的字段返回前端
List<JSONObject> resultList = new ArrayList<>();
for(Object obj :sysClassList){
//自定义工具类,将对象中需要返回的字段以json形式返回
resultList.add(Util.getCustomInfo(obj));
}
resultMap.put("data", resultList);
//查询总数量
String countJpql = "select count(s.classId) ";
Query countQuery = em.createQuery(countJpql + jpql.toString());
//加入参数
for(int j = 0; j < args.size(); j++){
countQuery.setParameter(j,args.get(j));
}
List countResult = countQuery.getResultList();
//分页信息处理
int total = Integer.valueOf(countResult.get(0).toString());
int totalPage = total/pageSize;
if( total%pageSize !=0 ){
totalPage++;
}
//将查询结果填入结果集中
resultMap.put("pageNum", pageNum);
resultMap.put("pageSize", pageSize);
resultMap.put("total",total);
resultMap.put("totalPage", totalPage);
return resultMap;
}
以下为方法二:
/**
* 分页查询系统分类列表(方法二)
*
* @param className
* @param parentId
* @param hide
* @param pageNum
* @param pageSize
* @return
*/
@Transactional
public Map getList(String className, Integer parentId, Integer hide, int pageNum, int pageSize) {
//返回结果
Map resultMap = new HashMap<>();
CriteriaBuilder cb = em.getCriteriaBuilder(); //获取CriteriaBuilder实体类
CriteriaQuery<Tuple> criteriaQuery = cb.createTupleQuery();//列表查询
CriteriaQuery<Tuple> criteriaQueryCount = cb.createTupleQuery();//统计查询
Root<SysClass> root = criteriaQuery.from(SysClass.class); //列表查询的实体对象
Root<SysClass> rootCount = criteriaQueryCount.from(SysClass.class); //统计查询的实体对象
//加载查询的列
criteriaQuery.multiselect(
root.get("classId").alias("classId"),
root.get("className").alias("className"),
root.get("icon").alias("icon"),
root.get("parentId").alias("parentId")
);
//加载查询统计的内容
criteriaQueryCount.multiselect(
cb.count(rootCount.get("classId")).alias("total") //统计查询总条数,作为页面信息返回
);
//条件集合
List<Predicate> predicates = new ArrayList<>();
//设定查询条件
if (className != null) { //模糊搜索
Path<String> classNamePath = root.get("className");
predicates.add(cb.like(classNamePath.as(String.class), "%" + className + "%"));
}
if (parentId != null) { //匹配搜索
Path<Integer> parentIdPath = root.get("parentId");
predicates.add(cb.equal(parentIdPath.as(Integer.class), parentId));
}
if (hide != 2) { //0.查询隐藏内容 1.查询显示隐藏内容 2.查询全部内容(不加查询条件)
Path<Integer> hidePath = root.get("hide");
predicates.add(cb.equal(hidePath.as(Integer.class), hide));
}
//加载查询条件
criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()]));
criteriaQueryCount.where(predicates.toArray(new Predicate[predicates.size()]));
//加载排序条件
Path<Integer> sortPath = root.get("sort");
Path<Integer> classIdPath = root.get("classId");
criteriaQuery.orderBy(cb.desc(sortPath.as(Integer.class)),cb.asc(classIdPath.as(Integer.class)));
//获取查询工具类实体
TypedQuery query = em.createQuery(criteriaQuery);
// int totalRows = query.getResultList().size(); //不建议使用该方式,效率差,使用统计查询
//加入分页信息
query.setFirstResult((pageNum - 1) * pageSize);
query.setMaxResults(pageSize);
List<Tuple> list = query.getResultList();//获取查询结果
//查询结果解析
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()) {
itemmap.put(element.getAlias(), tu.get(element.getAlias()));
}
dataList.add(itemmap);
}
}
//将查询结果填入结果集中
resultMap.put("data", dataList);
resultMap.put("pageNum", pageNum + 1);
resultMap.put("pageSize", pageSize);
resultMap.put("totalPage", dataList.size());
//将统计结果加入结果map中
TypedQuery queryCount = em.createQuery(criteriaQueryCount);
List<Tuple> listcount = queryCount.getResultList();
if (listcount != null && !listcount.isEmpty()) {
Tuple tu = listcount.get(0);
for (TupleElement element : tu.getElements()) {
//将统计查询结果填入结果集中
resultMap.put(element.getAlias(), tu.get(element.getAlias()));
}
}
return resultMap;
}
个人对比意见:
1.方法一代码量较少,但是需要自己拼接jpql,易出错,如果对于自己jpql语法很有信心,推荐使用(毕竟我是个懒人);
2.方法二支持查询指定项,而方法一将对象的全部信息都查了出来,后期自己做了处理后返回给前端。
参考文章: 《JPA Criteria Query 的 变态例子》光影路西法,网址: https://www.jianshu.com/p/69fa02602904
ps:本人第一次发布博文,十分粗糙,望大家多多包涵,取之精华,弃之糟粕。特别鸣谢好友鹏飞关于JPA的指点。