分页的作用
分页功能在一个系统中也是不可缺少的,分页功能的作用如下:
- 减少系统资源的消耗,数据查询出来后是放在内存里的,如果在数据量很大的情况下一次性将所有内容都查询出来,会占用过多的内存,通过分页可以减少这种消耗;
- 提高性能,应用与数据库间通过网络传输数据,一次传输 10 条数据结果集与一次传输 20000 条数据结果集肯定是传输 10 条消耗更少的网络资源;
- 提升访问速度,浏览器与应用间的传输也是通过网络,返回 10 条数据明显那比返回 20000 条数据速度更快,因为数据包的大小有差别;
- 符合用户习惯,比如搜索结果或者商品展示,通常用户可能只看最近前 30 条,将所有数据都查询出来比较浪费;
- 基于展现层面的考虑,由于设备屏幕的大小比较固定,一个屏幕能够展示的信息并不是特别多,如果一次展现太多的数据,不管是排版还是页面美观度都有影响,一个屏幕的范围就是那么大,展示信息条数有限。
分页功能的使用可以提升系统性能,也比较符合用户习惯,符合页面设计,这也是为什么大部分系统都会有分页功能。
分页参数设计
前端分页
分页信息区的设计和展示如上图所示,前端分页区比较重要的几个信息是:
- 页码展示
- 当前页码
- 每页条数
当然,有些页面也会加上首页、尾页、跳转页码等功能,这些信息都根据功能需要和页面设计去做增加和删减。
后端功能设计
- 前端页面的工作是渲染数据和分页信息展示,而后端则需要按照前端传输过来的请求将分页所需的数据正确的查询出来并返回给前端。
- 两端的侧重点并不相同,比如前端需要展示所有页码,而后端则只需要提供总页数即可,并不需要对这个总页码进行其他操作。
- 前端需要根据用户操作记录当前页码这个参数以便对页码信息进行调整和限制,而后端则并不是这么注重当前页码,只需要接收前端传输过来的页码进行相应的判断和查询操作即可。
对于后端必不可少的是两个参数:
- 页码(需要第几页的数据)
- 每页条数(每次查询多少条数据,一般默认 10 条或者 20 条)
因为数据库查询语句如下,不同数据库可能关键字有些差别,比如 SQL Server 是通过 top 关键字、Oracle 通过 rownum 关键字,MySQL 实现分页功能基本都是使用 limit 关键字。
//下面是mysql的实现语句:
select * from users limit 10,20
分页功能的最终实现既是如此,通过页码和条数确定数据库需要查询的是从第几条到第几条的数据,比如查询第 1 页每页 20 条数据就是查询数据库中从 0 到 20 条数据,查询第 4 页每页 10 条数据就是查询数据库中第 30 到 40 条数据,因此对于后端来说页码和条数两个参数就显得特别重要,缺少这两个参数根本无法继续之后的查询逻辑,分页数据也就无从查起。
虽然如此,为了前端分页区展示还要将数据总量或者总页数返回给前端,数据总量是必不可少的,因为总页数可以计算出来,即数据总量除以每页条数,数据总量的获取方式:
select count(*) from tb_xxxx
之后将数据封装,并返回给前端即可。
原生方法实现分页功能
新建一个表,并插入数据
DROP TABLE IF EXISTS `tb_admin_user`;
CREATE TABLE `tb_admin_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) NOT NULL,
`password_md5` varchar(50) NOT NULL,
`user_token` varchar(50) NOT NULL,
`is_deleted` tinyint(4) DEFAULT '0',
`create_time` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DELETE FROM tb_admin_user;
INSERT INTO
`tb_admin_user`(`id`,`user_name`,`password_md5`,`user_token`,`is_deleted`,`create_time`)
VALUES
(1,'admin','e10adc3949ba59abbe56e057f20f883e',
'd87edfdd63674b9591602b26bfb7f93f',0,'2018-07-04 11:21:14'),
(2,'test2','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:28')
(3,'test3','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:32'
(4,'test4','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:32'
(5,'test5','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:33'
(6,'test6','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:34'
(7,'test7','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:35'),
(8,'test8','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:35'
(9,'test9','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:36'
(10,'test10','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:37'
(11,'test11','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:38'
(12,'test12','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:38'
(13,'test13','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:39'
(14,'test14','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:40'
(15,'test15','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:40'
(16,'test16','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:41'
(17,'test17','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:41'
(18,'test18','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:42'
(19,'test19','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:46'
(20,'admin2','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:28'
(21,'admin3','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:32'
(22,'admin4','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:32'
(23,'admin5','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:33'
(24,'admin6','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:34'
(25,'admin7','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:35'
(26,'admin8','098f6bcd4621d373cade4e832627b4f6','\'\'',0,'2018-07-09 17:22:35'),
编写代码
- 添加lombok依赖
- mysql驱动
实体类
AdminUser
@Data
public class AdminUser {
private Long id;
private String userName;
private String password;
private String userToken;
private int isDeleted;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date createTime;
}
dao层
- 添加@Mapper注解
AdminUserMapper
@Mapper
@Repository
public interface AdminUserMapper {
//分页查询用户信息
//使用万能的map传参
List<AdminUser> findUsers(Map<String,Integer> map);
//查询总用户数
int findAllUsers();
}
AdminUserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rm.dao.AdminUserMapper">
<!--分页查询功能-->
<select id="findUsers" parameterType="Map" resultType="adminUser">
select id,user_name,create_time from admin_user_test
where is_deleted=0
order by id asc
<if test="startIndex!=null and pageSize!=null">
limit #{startIndex},#{pageSize}
</if>
</select>
<!-- 查询用户总数 -->
<select id="findAllUsers" resultType="int">
select count(*) from admin_user_test
where is_deleted=0
</select>
</mapper>
Service层
AdminUserService
@Service
public interface AdminUserService {
//分页查询用户信息
//使用万能的map传参
List<AdminUser> findUsers(Map<String,Integer> map);
//查询总用户数
int findAllUsers();
}
AdminUserServiceImpl
@Service
public class AdminUserServiceImpl implements AdminUserService {
//绑定dao层
@Autowired
private AdminUserMapper adminUserMapper;
@Override
public List<AdminUser> findUsers(Map<String, Integer> map) {
List<AdminUser> users = adminUserMapper.findUsers(map);
return users;
}
@Override
public int findAllUsers() {
int i = adminUserMapper.findAllUsers();
return i;
}
}
Controller层
- 通过接口绑定,不需要通过实现类绑定
@Controller
public class MyController {
//绑定service层
@Autowired
private AdminUserService adminUserService;
@ResponseBody
@RequestMapping("/t1/{startIndex}/{pageSize}")
public List<AdminUser> t1(@PathVariable int startIndex,@PathVariable int pageSize){
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",startIndex);
map.put("pageSize",pageSize);
List<AdminUser> users = adminUserService.findUsers(map);
System.out.println(users);
return users;
}
@ResponseBody
@RequestMapping("/t2")
public String t2(){
int i = adminUserService.findAllUsers();
return "查询出用户总数为"+String.valueOf(i);
}
}
Springboot配置
application.yaml
- 由于数据库字段和属性名不一致,需要开启驼峰命名规范
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/my_blog_db?useUnicode=true&charsetEncoding=utf-8
username: root
password: root
mybatis:
type-aliases-package: com.rm.pojo
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
测试
PageHelper 分页插件实现分页功能
导入依赖
- 导入启动器,Spingboot会自动帮我们配置PageHelper对象
<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
配置插件
#分页插件
pagehelper:
helper-dialect: mysql
params: count=countSql
reasonable: true
support-methods-arguments: true
配置详情
helper-dialect
- 配置使用哪种数据库语言,不配置的话pageHelper也会自动检测
reasonable
- 配置分页参数合理化功能,默认是false。
- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>总页数会查询最后一页;
- 禁用合理化时,如果pageNum<1或pageNum>总页数会返回空数据。
params
- 为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值;
- 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,
- 不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
support-methods-arguments
- 支持通过Mapper接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。
编写Mapper
List<AdminUser> AllUsers();
Mapper.xml
<select id="AllUsers" resultType="adminUser">
select * from my_blog_db.admin_user_test
where is_deleted=0
order by id asc
<if test="startIndex!=null and pageSize!=null">
limit #{pageNum},#{pageSize}
</if>
</select>
Service层
List<AdminUser> AllUsers();
@Override
public List<AdminUser> AllUsers() {
return adminUserMapper.AllUsers();
}
Controller层
@GetMapping("/getAll")
public String getAll(
Model model,
@RequestParam(defaultValue = "1",value = "pageNum") Integer pageNum,
@RequestParam(defaultValue = "10",value = "pageSize") Integer pageSize){
PageHelper.startPage(pageNum,pageSize);
List<AdminUser> list = adminUserService.AllUsers();
PageInfo<AdminUser> pageInfo = new PageInfo<AdminUser>(list);
model.addAttribute("pageInfo",pageInfo);
return "list";
}
pageInfo
参数
前端页面
list.html
- 日期需要使用dates.format进行格式化
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div align="left">
<table border="1">
<tr>
<th>id</th>
<th>userName</th>
<th>isDeleted</th>
<th>createTime</th>
</tr>
<tr th:each="adminUser:${pageInfo.list}">
<td th:text="${adminUser.id}"></td>
<td th:text="${adminUser.userName}"></td>
<td th:text="${adminUser.isDeleted}"></td>
<td th:text="${#dates.format(adminUser.createTime,'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<p>当前 <span th:text="${pageInfo.pageNum}"></span> 页,总 <span th:text="${pageInfo.pages}"></span> 页,共 <span th:text="${pageInfo.total}"></span> 条记录</p>
<a th:href="@{/getAll}">首页</a>
<a th:href="@{/getAll(pageNum=${pageInfo.hasPreviousPage}?${pageInfo.prePage}:1)}">上一页</a>
<a th:href="@{/getAll(pageNum=${pageInfo.hasNextPage}?${pageInfo.nextPage}:${pageInfo.pages})}">下一页</a>
<a th:href="@{/getAll(pageNum=${pageInfo.pages})}">尾页</a>
</div>
</body>
</html>