分页插件—PageHelper

215 阅读4分钟

平常开发中,查询数据时会返回很多数据记录的情况,在前端展示时也不会全部展示出来,往往是采用分页的方式展示。若数据量很大时,一股脑返回全部数据就会导致浏览器的负载过大,因此在设计时往往采用请求数据时在后端就进行分页处理,然后再返回某一页的数据给前端展示。

1. 普通分页查询

提到分页,大家肯定是对数据的limit命令很熟悉,可以试着看如下几行sql语句:

 select * from user limit 0, 5;
 select * from user limit 5, 5;
 select * from user limit 10, 5;

对应查询数据如下:

image-20240407130431600

image-20240407130442532

image-20240407130456557

可以看到,其实这就是我们所需要的效果,再仔细看一下这几条sql语句,limit对应参数就是起始索引,数据数量,对应分页查询时,数据数量就是每页展示的数据量,这个是固定的,但是起始索引该怎么确定呢?其实:也是很好确定的,将SQL语句稍微做一点小改变,就可以看出其中逻辑了:

 -- 第1页
 select * from user limit (1-1)*5, 5;
 -- 第2页
 select * from user limit (2-1)*5, 5;
 -- 第3页
 select * from user limit (3-1)*5, 5;

其实就是起始索引 = (当前页码 - 1) * 数据数量;发现这个相信就不难了,将其在Spring Boot中实现就好了。

User与Result都是一个普通的pojo类。

controller层:

 @Slf4j
 @RestController
 public class UserController {
 ​
     @Autowired
     private UserService userService;
 ​
     // 若未传数据,@RequestParam设置默认值
     @GetMapping("/users")
     public Result page(@RequestParam(defaultValue = "1") Integer pageNumber,
                        @RequestParam(defaultValue = "10") Integer pageSize) {
         // 输出查询参数
         log.info("分页查询,page:{},pageSize:{}", pageNumber, pageSize);
         // 调用service层
         List<User> userList =  userService.page(pageNumber, pageSize);
         return Result.success(userList);
     }
 }

service实现类:

 @Service
 public class UserServiceImpl implements UserService {
 ​
     @Autowired
     private UserMapper userMapper;
     /**
      * 分页查询
      * @param pageNumber 页码
      * @param pageSize 每页数据数量
      */
     public List<User> page(Integer pageNumber, Integer pageSize) {
         // 获取分页查询的结果列表
         Integer start = (pageNumber - 1) * pageSize;
         List<User> userList = userMapper.page(start, pageSize);
 ​
         return userList;
     }

dao层:

 @Mapper
 public interface UserMapper {
     /**
      * 分页查询
      * @param start 每页起始索引
      * @param pageSize 每页包含数量
      * @return User集合
      */
     @Select("select * from user limit #{start},#{pageSize}")
     List<User> page(Integer start, Integer pageSize);
 }

查询结果:

image-20240407132327882

image-20240407132352338

这样就完成了分页查询的主要部分了,还有可以优化的地方,一般分页查询时还需要获取到数据总数,一般都是新建一个类,来封装保存所需要的数据:

PageResult.java

 /**
  * 分页结果类
  */
 @Data
 @NoArgsConstructor
 @AllArgsConstructor
 public class PageResult {
     /**
      * 数据总数量
      */
     private Long total;
     /**
      * 当前页数据
      */
     private List currentPageData;
 }

然后,在获取到数据后在进行封装返回就好了。

稍加修改之前的代码:

controller层:

 @Slf4j
 @RestController
 public class UserController {
 ​
     @Autowired
     private UserService userService;
 ​
     // 若未传数据,@RequestParam设置默认值
     @GetMapping("/users")
     public Result page(@RequestParam(defaultValue = "1") Integer pageNumber,
                        @RequestParam(defaultValue = "10") Integer pageSize) {
         // 输出查询参数
         log.info("分页查询,page:{},pageSize:{}", pageNumber, pageSize);
         // 调用service层
         PageResult pageResult =  userService.page(pageNumber, pageSize);
         return Result.success(pageResult);
     }
 }

service实现类:

 @Service
 public class UserServiceImpl implements UserService {
 ​
     @Autowired
     private UserMapper userMapper;
     /**
      * 分页查询
      * @param pageNumber 页码
      * @param pageSize 每页数据数量
      */
     public List<User> page(Integer pageNumber, Integer pageSize) {
         // 获取总记录数
         Long total = userMapper.total();
 ​
         // 获取分页查询的结果列表
         Integer start = (pageNumber - 1) * pageSize;
         List<User> userList = userMapper.page(start, pageSize);
 ​
         // 封装pageResult
         PageResult pageResult = new PageResult(total, userList);
 ​
         return pageResult;
     }

dao层:

@Mapper
public interface UserMapper {
	/**
	 * 分页查询
	 * @param start 每页起始索引
	 * @param pageSize 每页包含数量
	 * @return User集合
	 */
	@Select("select * from user limit #{start},#{pageSize}")
	List<User> page(Integer start, Integer pageSize);
    
    /**
	 * 获取总记录数
	 */
	@Select("select count(*) from user")
	Long total();
}

查询结果:

image-20240407133312031

2. PageHelper分页查询

上述的代码其实十分固定,但是写的还是比较啰嗦的,特别是在service层中的代码,而PageHelper就解决了该问题,下面,让我们看看PageHelper如何使用。

2.1 配置依赖

<!-- PageHelper分页 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>

2.2 PageHelper实现分页

@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private UserMapper userMapper;
	/**
	 * 分页查询
	 * @param pageNumber 页码
	 * @param pageSize 每页数据数量
	 */
	public List<User> page(Integer pageNumber, Integer pageSize) {
		// 使用PageHelper获取分页数据
		PageHelper.startPage(pageNumber, pageSize);

		// 获取数据 这里返回的是PageHelper中的Page对象
		Page<User> page = userMapper.list();

		// 封装pageResult
		PageResult pageResult = new PageResult(page.getTotal(), page.getResult());

		return pageResult;
    }
/**
 * 用户信息查询
 */
@Select("select * from user")
Page<User> list();

PageHelper插件就比上面普通的分页查询方便许多了。其中:

  • PageHelper.startPage方法用于获取当前页码和数据数量的数据,该方法就设定了分页查询的两个参数,不需要自己去计算。

  • Page,获取的数据存储在Page对象中,它包含一些方法:

    • getPageNum 获取当前的页码
    • getPages 获取总页数
    • getTotal 获取总记录数
    • getResult 获取当前页的数据记录,为一个List集合

结果封装到PageResult中再返回即可。

其实,还可以见将其写的简便一点,利用doSelectPage方法,PageHelper.startPage方法其实返回的是一个PageMethod对象,该对象就可调用doSelectPage方法,使用lambda表达式来省略一些语句,代码如下:

Page<User> userPage = PageHelper.startPage(pageNumber, pageSize).doSelectPage(() -> userMapper.list());

看见这里,有的或许可以联想到方法引用,对的,这里可以使用方法引用的写法。

Page<User> userPage = PageHelper.startPage(pageNumber, pageSize).doSelectPage(userMapper::list);

最后再利用getTotalgetResult方法进行封装就好了。