美文网首页Java后端
单表CRUD通用分页service方法抽取(MP、JPA支持)

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

作者: KICHUN | 来源:发表于2020-01-12 15:12 被阅读0次

    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<D extends BaseDTO, E extends BaseEntity, ID extends Serializable> implements BaseService<D, E, ID> {
        @Autowired
        private BaseRepository<E, ID> baseRepository;
    
        @Autowired
        private BaseAssembler<D, E> baseAssembler;
    
        @Override
        public PageResponse<D> 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<Sort.Order> 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<E> pageResult = null;
    
            //封装查询条件
            if (CollUtil.isNotEmpty(pageParam.getQuery())) {
                pageResult = baseRepository.findAll(new Specification<E>() {
                    @Override
                    public Predicate toPredicate(Root<E> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                        List<Predicate> 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<D> 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<QueryParam> query;
    
        @ApiModelProperty(value = "排序参数")
        private List<SortParam> 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<T> {
    
        /**
         * 总页数
         * 当前页页码
         * 每一页的数据
         */
        private Long total;
        private Long totalPage;
        private Long pageIndex;
        private List<T> pageData;
    }
    

    5 效果展示

    请求参数示例


    image.png

    返回参数示例


    image.png

    相关文章

      网友评论

        本文标题:单表CRUD通用分页service方法抽取(MP、JPA支持)

        本文链接:https://www.haomeiwen.com/subject/wnoyactx.html