0. 什么时候会用到分页
在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。
-
前端分页
一次性请求数据表格中的所有记录(ajax),然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。
特点是:简单,很适合小规模的web平台;当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长。 -
后端分页
在ajax请求中指定页码(pageNum)和每页的大小(pageSize),后端查询出当页的数据返回,前端只负责渲染。
特点是:复杂一些;性能瓶颈在MySQL的查询性能,这个当然可以调优解决。一般来说,web开发使用的是这种方式。
通常我们说的也是后端分页。 MySQL对分页的支持
简单来说MySQL对分页的支持是通过limit子句。请看下面的例子。offset是相对于首行的偏移量(首行是0),rows是返回条数。 # 每页10条记录,取第一页,返回的是前10条记录 select * from tableA limit 0,10; # 每页10条记录,取第二页,返回的是第11条记录,到第20条记录, select * from tableA limit 10,10;
这里简单说一下,MySQL在处理分页的时候是这样的:
limit 1000,10 - 过滤出1010条数据,然后丢弃前1000条,保留10条。当偏移量大的时候,性能会有所下降。
limit 100000,10 - 会过滤10w+10条数据,然后丢弃前10w条。如果在分页中发现了性能问题,可以根据这个思路调优。
1. Mybatis实现分页的方式
1.1 分页的分类:
-
物理分页:只从数据库中查询当前页的数据
优点:不占用很多内存 缺点:效率比价低(相比于逻辑分页) -
逻辑分页:从数据库将所有记录查询出来,存储到内存中,展示当前页,然后数据再直接从内存中获取
优点:效率高 缺点:占用内存比较高
大多数情况下,我们用的都是物理分页。
物理分页:
- 1.直接用jdbc完成:使用滚动结果集
-
优点:跨数据库 缺点:性能低
- 2.使用数据库本身提供的分页操作:使用每一个数据库特定的分页函数
-
优点:性能高 缺点:不能跨数据库
2. Mybatis具体实现分页
关于分页,基本上在后台查询数据的时候都会用到,我在做项目的时候也遇到需要实现分页,用到了一个非常简单的插件工具PageHeper。这里我以我的项目为例具体说下怎么使用,写的不好还请见谅。
2.1 引入分页插件的方式
引入汾阳王插件的方式有以下两种方式:
1)直接导入Jar包,用eclipse做开发的基本都是直接下载jar包,然后导入到WEB-INF/lib文件夹下。直接点击下面链接下载最新版即可。
插件下载
2)使用Maven项目资源管理器(推荐使用)
对于没有用过Maven的我分享一个学习资源。也是我学习的方式。慕课网项目管理利器——Maven
在pom.xml中添加如下依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>最新版本</version>
</dependency>
2.2 配置拦截器插件
下面是我的项目中Mybatis的配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置别名 -->
<typeAliases>
<typeAlias alias="User" type="com.ldu.pojo.User"/>
<typeAlias alias="Goods" type="com.ldu.pojo.Goods"/>
</typeAliases>
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 4.0.0以后版本可以不设置该参数 -->
<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
<!-- 和startPage中的pageNum效果一样-->
<property name="offsetAsPageNum" value="true"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true"/>
<!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
<property name="pageSizeZero" value="true"/>
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
<property name="reasonable" value="true"/>
<!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
<!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,orderBy,不配置映射的用默认值 -->
<!-- 不理解该含义的前提下,不要随便复制该配置 -->
<property name="params" value="pageNum=start;pageSize=limit;"/>
<!-- 支持通过Mapper接口参数来传递分页参数 -->
<property name="supportMethodsArguments" value="true"/>
<!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
<property name="returnPageInfo" value="check"/>
</plugin>
</plugins>
</configuration>
2.3 编写dao层接口(实际过程中已经写好了POJO实体类)
下面以我项目中的admin实体类为例:
package com.ldu.dao;
import com.ldu.pojo.Admin;
public interface AdminMapper {
public Admin findAdmin(Long phone, String password);
public Admin findAdminById(Integer id);
public void updateAdmin(Admin admins);
}
(实际开发过程中可以吧service层接口也写了,因为dao层接口和service层接口是一样的。
这时候有人就会疑惑一样为什么还要写,代码都是重复的。当然这样设计是有他自己的道理的,单独独立出一个service接口层,是为了降低Sevice类的耦合性,服务于service.impl层)下面附上我的adminServiceImpl类:
package com.ldu.service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.ldu.dao.AdminMapper;
import com.ldu.pojo.Admin;
import com.ldu.service.AdminService;
@Service(value="adminService")
public class AdminServiceImpl implements AdminService {
@Resource
private AdminMapper am;
@Override
public Admin findAdmin(Long phone, String password) {
// TODO Auto-genera