平常开发中,查询数据时会返回很多数据记录的情况,在前端展示时也不会全部展示出来,往往是采用分页的方式展示。若数据量很大时,一股脑返回全部数据就会导致浏览器的负载过大,因此在设计时往往采用请求数据时在后端就进行分页处理,然后再返回某一页的数据给前端展示。
1. 普通分页查询
提到分页,大家肯定是对数据的limit命令很熟悉,可以试着看如下几行sql语句:
select * from user limit 0, 5;
select * from user limit 5, 5;
select * from user limit 10, 5;
对应查询数据如下:
可以看到,其实这就是我们所需要的效果,再仔细看一下这几条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);
}
查询结果:
这样就完成了分页查询的主要部分了,还有可以优化的地方,一般分页查询时还需要获取到数据总数,一般都是新建一个类,来封装保存所需要的数据:
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();
}
查询结果:
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);
最后再利用getTotal与getResult方法进行封装就好了。