JPA分页
当请求的数据总量很大时,这时候前端往往都会要求后端将数据分页返回。本文介绍SpringBoot下后端数据层使用JPA+MySQL时,如何分页返回数据(除了当前页面的数据,往往还要返回总页数这项数据)。
一、从头到尾自己实现分页:
Controller层:使用@RequestParam绑定page和pageSize参数,调用Service
Service层:
- 接收page、pageSize参数,调用Dao获得当前页数据;
- 调用Dao获得总数据量,再除以pageSize,获得总页数;
- 将当前页数据和总页数包装成VO返回给Controller
DAO层:
- 自己写SQL,使用limit语句获取当前页数据;
- 使用 select count(1)获得数据总量
可以看到,自己实现分页还是比较麻烦的,不详细写具体代码了,重点介绍下一种方法:
二、使用JPA自带的Pageable和Page:
Controller层:使用Pageable接收参数
@GetMapping("/byEnterprise")
public Response<PageVO<QuestionBankVO>> getQuestionBanksByEnterpriseId(@PageableDefault(page = 0, value = 6, sort = {"createdTime"}, direction = Sort.Direction.DESC) Pageable pageable) {
return ResponseFactory.okResponse(questionBankService.getQuestionBanksByEnterpriseId(pageable, 1));
}
- Pageable不仅仅支持分页,还支持排序(单字段/多字段均支持)
- @PageableDefault注解可以为pageable对象设置默认参数,即pageable参数非必须传入,可以利用该注解设置默认值。其中page是页数(从0开始),value是每页数据数量(即pageSize),sort是被排序的字段,direction是升序/降序
- 访问该RESTful接口时,使用例子:http://localhost:8882/api/v1/questionBanks/byEnterprise?sort=id%2Cdesc&page=0&size=2 (传direction是在字段后加[,asc/desc],eg:id,desc,代表按id降序排列。也可以只写id不写direction,只写sort字段不写direction时,direction默认为asc)
Service层:利用DAO层获得的Page对象,可以获得当前页数据、总页数等信息
@Override
public PageVO<QuestionBankVO> getQuestionBanksByEnterpriseId(Pageable pageable, int enterpriseId) {
Page<QuestionBank> result = questionBankDao.findByEnterpriseIdAndDisabledFalse(pageable, enterpriseId);
List<QuestionBankVO> bankVOs = result.stream().map(b -> (QuestionBankVO) Converter.map(b, QuestionBankVO.class)).collect(Collectors.toList());
return new PageVO<>(pageable.getPageNumber(), result.getTotalPages(), bankVOs);
}
DAO层:传入Pageable,返回Page<T>
public interface QuestionBankDao extends JpaRepository<QuestionBank, Integer> {
Page<QuestionBank> findByEnterpriseIdAndDisabledFalse(Pageable pageable, int enterpriseId);
}
自定义PageVO:
package com.yuantu.education.vo;
import lombok.Data;
import java.util.List;
/**
* @author deng
* @date 2018/12/13
*/
@Data
public class PageVO<T> {
private int currentPage;
private int totalPage;
private List<T> data;
public PageVO(){
super();
}
public PageVO(int currentPage, int totalPage, List<T> data) {
this.currentPage = currentPage;
this.totalPage = totalPage;
this.data = data;
}
}
如果使用Swagger2.8.0,此时Swagger不会自动显示出Pageable参数。
想要让Swagger2能够针对Pageable参数显示接口参数,增加如下配置即可:
package com.yuantu.education.config;
import com.fasterxml.classmate.TypeResolver;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.data.domain.Pageable;
import springfox.documentation.schema.AlternateTypeRule;
import springfox.documentation.schema.AlternateTypeRuleConvention;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
@Configuration
@SuppressWarnings("SpringJavaAutowiringInspection")
public class PageableParamConfig {
@Bean
public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) {
return new AlternateTypeRuleConvention() {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public List<AlternateTypeRule> rules() {
return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class)));
}
};
}
@ApiModel
@Data
static class Page {
@ApiModelProperty("第page页,从0开始计数")
private Integer page;
@ApiModelProperty("每页数据数量")
private Integer size;
@ApiModelProperty("按属性排序,格式:属性(,asc|desc)")
private List<String> sort;
}
}
配置后Swagger2显示效果如图:
Ps: Swagger2的配置学习自:http://blog.51cto.com/7308310/2082742
后话:方法二对JPA框架的依赖性很强(从Controller到Service到DAO都依赖JPA框架);而方法一仅仅在DAO层依赖JPA。比如以后如果因为需求变更而想将框架从JPA迁移至MyBatis的话,方法一只需要修改DAO层代码,而方法二则从头到尾全要改。