1. ORM單表CRUD
在流行的JAVA ORM框架中有Mybatis以及JPA玉组,其中JPA提供了JpaRepository
JpaSpecificationExecutor
等接口谎柄,提供了大量單表基礎(chǔ)CRUD方法。而在Mybatis中惯雳,雖然官方提供的功能有限朝巫,但實(shí)際的第三方插件例如Mybatis-Plus
、通用Mapper等也提供極其豐富的方法吨凑。
以Jpa為例捍歪,一個繼承JpaRepository
JpaSpecificationExecutor
接口的BaseRepository就包含如下方法
image.png
2. 單表復(fù)雜分頁列表需求
如下圖户辱,我們常見的一個分頁列表需求
image.png
分析請求參數(shù)如下:
- 分頁參數(shù):起始頁,頁大小
- 查詢條件參數(shù):屬性名糙臼,屬性值庐镐,邏輯條件
- 排序參數(shù) :屬性名,排序類型
返回參數(shù):
- 分頁統(tǒng)計數(shù)據(jù):當(dāng)前頁变逃,頁大小必逆,記錄數(shù) ,頁數(shù)
- 數(shù)據(jù)列表
根據(jù)上述需求揽乱,可以發(fā)現(xiàn)名眉,這類單表的高級分頁列表查詢的需求很常見,JPA提供的抽象方法均可組合實(shí)現(xiàn)其功能凰棉,但沒有提供一個“萬能”的方法损拢。
3. 單表分頁列表方法實(shí)現(xiàn)思路
-
提供自定義請求參數(shù)的封裝,如下
image.png -
提供自定義統(tǒng)一分頁返回數(shù)據(jù)
image.png
注意:其中返回數(shù)據(jù)UserDTO為泛型數(shù)據(jù)
根據(jù)實(shí)際項目框架撒犀,開發(fā)
BaseController BaseService BaseServiceImpl BaseRepository
等基礎(chǔ)MVC類福压,并添加Page方法,Page分頁邏輯在BaseServiceImpl
中實(shí)現(xiàn)或舞,具體代碼如下
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) {
//初始化當(dāng)前頁及頁大小參數(shù)
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參數(shù)封裝為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()));
}
});
}
//根據(jù)當(dāng)前頁荆姆,頁大小,排序參數(shù)封裝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);
}
//封裝自定義分頁返回參數(shù)
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. 參數(shù)實(shí)體代碼
EnumOperateType
public enum EnumOperateType {
EQ,
NOT_EQ,
LIKE,
GT,
LT,
IN,
NOT_IN;
EnumOperateType() {
}
}
PageParameter
@Data
public class PageParameter implements Serializable {
@ApiModelProperty(value = "請求頁,默認(rèn)為0")
private Integer pageidx = 0;
@ApiModelProperty(value = "分頁大小映凳,默認(rèn)為10")
private Integer pagesize = 10;
@ApiModelProperty(value = "查詢參數(shù)")
private List<QueryParam> query;
@ApiModelProperty(value = "排序參數(shù)")
private List<SortParam> sort;
}
QueryParam
/**
* 查詢參數(shù)dto胆筒,封裝一個查詢的字段,支持且或條件诈豌,支持EQ NOT_EQ LIKE GT LT IN NOT_IN等判斷
*/
@Data
public class QueryParam implements Serializable {
@ApiModelProperty(value = "查詢條件是否為或仆救,默認(rèn)為且")
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 默認(rèn)ASC")
private String sortType = "ASC";
}
PageResponse
public class PageResponse<T> {
/**
* 總頁數(shù)
* 當(dāng)前頁頁碼
* 每一頁的數(shù)據(jù)
*/
private Long total;
private Long totalPage;
private Long pageIndex;
private List<T> pageData;
}
5 效果展示
請求參數(shù)示例
image.png
返回參數(shù)示例
image.png