单表CRUD通用分页service方法抽取(MP、JPA支持)

1. ORM单表CRUD

在流行的JAVA ORM框架中有Mybatis以及JPA,其中JPA提供了JpaRepository JpaSpecificationExecutor等接口,提供了大量单表基础CRUD方法。而在Mybatis中,虽然官方提供的功能有限,但实际的第三方插件例如Mybatis-Plus 、通用Mapper等也提供极其丰富的方法。

以Jpa为例,一个继承JpaRepository JpaSpecificationExecutor接口的BaseRepository就包含如下方法

image.png

2. 单表复杂分页列表需求

如下图,我们常见的一个分页列表需求


image.png

分析请求参数如下:

  • 分页参数:起始页,页大小
  • 查询条件参数:属性名,属性值,逻辑条件
  • 排序参数 :属性名,排序类型

返回参数:

  • 分页统计数据:当前页,页大小,记录数 ,页数
  • 数据列表

根据上述需求,可以发现,这类单表的高级分页列表查询的需求很常见,JPA提供的抽象方法均可组合实现其功能,但没有提供一个“万能”的方法。

3. 单表分页列表方法实现思路

  • 提供自定义请求参数的封装,如下


    image.png
  • 提供自定义统一分页返回数据


    image.png

    注意:其中返回数据UserDTO为泛型数据

  • 根据实际项目框架,开发BaseController BaseService BaseServiceImpl BaseRepository等基础MVC类,并添加Page方法,Page分页逻辑在BaseServiceImpl中实现,具体代码如下

package com.tba.sc.common.base.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.kichun.common.base.assembler.BaseAssembler;
import com.kichun.common.base.dto.BaseDTO;
import com.kichun.common.base.dto.page.EnumOperateType;
import com.kichun.common.base.dto.page.PageParameter;
import com.kichun.common.base.dto.page.PageResponse;
import com.kichun.common.base.entity.BaseEntity;
import com.kichun.common.base.repository.BaseRepository;
import com.kichun.common.base.service.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.JpaSort;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class BaseServiceImpl implements BaseService {
    @Autowired
    private BaseRepository baseRepository;

    @Autowired
    private BaseAssembler baseAssembler;

    @Override
    public PageResponse page(PageParameter pageParam) {
        //初始化当前页及页大小参数
        Integer page = ObjectUtil.isNotNull(pageParam) ? ObjectUtil.isNotNull(pageParam.getPageidx()) ? pageParam.getPageidx() > 0 ? pageParam.getPageidx() : 0 : 0 : 0;
        Integer size = ObjectUtil.isNotNull(pageParam) ? ObjectUtil.isNotNull(pageParam.getPagesize()) ? pageParam.getPagesize() > 0 ? pageParam.getPagesize() : 10 : 10 : 10;

        //将自定义排序SORT参数封装为JPA Sort.Order对象
        List orders = new ArrayList<>();
        if (CollUtil.isNotEmpty(pageParam.getSort())) {
            pageParam.getSort().forEach(sort -> {
                if (Sort.Direction.DESC.toString().equals(sort.getSortType())) {
                    orders.add(Sort.Order.desc(sort.getField()));
                } else {
                    orders.add(Sort.Order.asc(sort.getField()));
                }
            });
        }
        //根据当前页,页大小,排序参数封装JPA PageRequest对象
        PageRequest pageRequest = PageRequest.of(page, size, JpaSort.by(orders));
        Page pageResult = null;

        //封装查询条件
        if (CollUtil.isNotEmpty(pageParam.getQuery())) {
            pageResult = baseRepository.findAll(new Specification() {
                @Override
                public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    List predicates = new ArrayList();
                    pageParam.getQuery().forEach(query -> {
                        if (StrUtil.isBlank(query.getField()) || StrUtil.isBlank(query.getValue()) || StrUtil.isBlank(query.getOperateType())) {
                            return;
                        }
                        if (EnumOperateType.EQ.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.equal(root.get(query.getField()), query.getValue()));
                        } else if (EnumOperateType.GT.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.gt(root.get(query.getField()), Long.valueOf(query.getValue())));
                        } else if (EnumOperateType.LT.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.lt(root.get(query.getField()), Long.valueOf(query.getValue())));
                        } else if (EnumOperateType.NOT_EQ.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.notEqual(root.get(query.getField()), query.getValue()));
                        } else if (EnumOperateType.LIKE.toString().equals(query.getOperateType())) {
                            predicates.add(criteriaBuilder.like(root.get(query.getField()).as(String.class), "%" + query.getValue() + "%"));
                        } else if (EnumOperateType.IN.toString().equals(query.getOperateType())) {
                            String[] split = query.getValue().split(",");
                            if (ArrayUtil.isNotEmpty(split)) {
                                predicates.add(criteriaBuilder.in(root.get(query.getField()).in(split)));
                            }
                        }
                        if (query.isOr()) {
                            criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
                        } else {
                            criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                        }
                    });
                    return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                }
            }, pageRequest);
        } else {
            pageResult = baseRepository.findAll(pageRequest);
        }

        //封装自定义分页返回参数
        PageResponse pageResponse = new PageResponse<>();
        pageResponse.setPageIndex(new Long(pageResult.getPageable().getPageNumber()));
        pageResponse.setTotal(new Long(pageResult.getTotalElements()));
        pageResponse.setTotalPage(new Long(pageResult.getTotalPages()));
        if (CollUtil.isNotEmpty(pageResult.getContent())) {
            pageResponse.setPageData(baseAssembler.toDTOs(pageResult.getContent()));
        }
        return pageResponse;
    }
}

4. 参数实体代码

EnumOperateType

public enum EnumOperateType {
    EQ,
    NOT_EQ,
    LIKE,
    GT,
    LT,
    IN,
    NOT_IN;
    EnumOperateType() {
    }
}

PageParameter

@Data
public class PageParameter implements Serializable {

    @ApiModelProperty(value = "请求页,默认为0")
    private Integer pageidx = 0;

    @ApiModelProperty(value = "分页大小,默认为10")
    private Integer pagesize = 10;

    @ApiModelProperty(value = "查询参数")
    private List query;

    @ApiModelProperty(value = "排序参数")
    private List sort;
}

QueryParam

/**
 * 查询参数dto,封装一个查询的字段,支持且或条件,支持EQ NOT_EQ LIKE GT LT IN NOT_IN等判断
 */
@Data
public class QueryParam implements Serializable {

    @ApiModelProperty(value = "查询条件是否为或,默认为且")
    private boolean or;

    @ApiModelProperty(value = "查询的字段名")
    private String field;

    @ApiModelProperty(value = "匹配类型:EQ 等于,NOT_EQ 不等于,LIKE 模糊查询,GT大于,LT小于 ,IN在其中,NOT_IN不在其中value为,分隔字符串")
    private String operateType;

    @ApiModelProperty(value = "匹配值")
    private String value;
}

SortParam

@Data
public class SortParam implements Serializable {

    @ApiModelProperty(value = "排序字段")
    private String field;

    @ApiModelProperty(value = "排序类型:ASC DESC 默认ASC")
    private String sortType = "ASC";
}

PageResponse

public class PageResponse {

    /**
     * 总页数
     * 当前页页码
     * 每一页的数据
     */
    private Long total;
    private Long totalPage;
    private Long pageIndex;
    private List pageData;
}

5 效果展示

请求参数示例


image.png

返回参数示例


image.png

你可能感兴趣的:(单表CRUD通用分页service方法抽取(MP、JPA支持))