Java后端

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

2020-01-12  本文已影响0人  KICHUN

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. 单表分页列表方法实现思路

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
上一篇下一篇

猜你喜欢

热点阅读