赞
踩
在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。
前端分页
一次性请求数据表格中的所有记录(ajax),然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。
特点是:简单,很适合小规模的web平台;当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长。
后端分页
在ajax请求中指定页码(pageNum)和每页的大小(pageSize),后端查询出当页的数据返回,前端只负责渲染。
特点是:复杂一些;性能瓶颈在MySQL的查询性能,这个当然可以调优解决。一般来说,web开发使用的是这种方式。
我们说的也是后端分页。
简单来说MySQL对分页的支持是通过limit子句。请看下面的例子。
limit关键字的用法是 LIMIT [offset,] rows 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条。如果在分页中发现了性能问题,可以根据这个思路调优。
在使用Java Spring开发的时候,Mybatis算是对数据库操作的利器了。不过在处理分页的时候,Mybatis并没有什么特别的方法,一般需要自己去写limit子句实现,成本较高。好在有个PageHelper插件。
1、POM依赖
Mybatis的配置就不多提了。PageHelper的依赖如下。需要新的版本可以去maven上自行选择
1 2 3 4 5 |
|
2、Mybatis对PageHelper的配置
打开Mybatis配置文件,一般在Resource路径下。我这里叫mybatis-config.xml。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
这里要注意的是PageHelper相关的配置。
如果你没有加载Mybatis配置文件,那么使用的是Mybatis默认的配置。如何加载Mybatis配置文件呢?
到你的dataSrouce配置中。
在配置sqlSessionFactory的时候,指定Mybatis核心配置文件和mapper的路径,代码如下
1 2 3 4 5 6 7 8 9 |
|
说明:
这里配置的mapper.xml存放路径,在Resource/mybatis-mapper文件夹下
这里配置的mybatis-config.xml文件,在Resource/下
3、分页
准备一个mapper.xml,测试就随便写一个吧,干脆就用工程里的一个。
这里这个查询,是一个典型的多条件查询,我们要做的是对多条件匹配到的记录进行分页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
在Mapper.java接口中编写对应的方法
1 |
|
先上分页代码,后面再说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
说明:
1、PageHelper的优点是,分页和Mapper.xml完全解耦。实现方式是以插件的形式,对Mybatis执行的流程进行了强化,添加了总数count和limit查询。属于物理分页。
2、Page page = PageHelper.startPage(pageNum, pageSize, true); - true表示需要统计总数,这样会多进行一次请求select count(0); 省略掉true参数只返回分页数据。
1)统计总数,(将SQL语句变为 select count(0) from xxx,只对简单SQL语句其效果,复杂SQL语句需要自己写)
Page<?> page = PageHelper.startPage(1,-1);
long count = page.getTotal();
2)分页,pageNum - 第N页, pageSize - 每页M条数
A、只分页不统计(每次只执行分页语句)
PageHelper.startPage([pageNum],[pageSize]);
List<?> pagelist = queryForList( xxx.class, "queryAll" , param);
//pagelist就是分页之后的结果
B、分页并统计(每次执行2条语句,一条select count语句,一条分页语句)适用于查询分页时数据发生变动,需要将实时的变动信息反映到分页结果上
Page<?> page = PageHelper.startPage([pageNum],[pageSize],[iscount]);
List<?> pagelist = queryForList( xxx.class , "queryAll" , param);
long count = page.getTotal();
//也可以 List<?> pagelist = page.getList(); 获取分页后的结果集
3)使用PageHelper查全部(不分页)
PageHelper.startPage(1,0);
List<?> alllist = queryForList( xxx.class , "queryAll" , param);
4)PageHelper的其他API
String orderBy = PageHelper.getOrderBy(); //获取orderBy语句
Page<?> page = PageHelper.startPage(Object params);
Page<?> page = PageHelper.startPage(int pageNum, int pageSize);
Page<?> page = PageHelper.startPage(int pageNum, int pageSize, boolean isCount);
Page<?> page = PageHelper.startPage(pageNum, pageSize, orderBy);
Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable); //isReasonable分页合理化,null时用默认配置
Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable, isPageSizeZero); //isPageSizeZero是否支持PageSize为0,true且pageSize=0时返回全部结果,false时分页,null时用默认配置
5)、默认值
//RowBounds参数offset作为PageNum使用 - 默认不使用
private boolean offsetAsPageNum = false;
//RowBounds是否进行count查询 - 默认不查询
private boolean rowBoundsWithCount = false;
//当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果
private boolean pageSizeZero = false;
//分页合理化
private boolean reasonable = false;
//是否支持接口参数来传递分页参数,默认false
private boolean supportMethodsArguments = false;
3、有一个安全性问题,需要注意一下,不然可能导致分页错乱。我这里直接粘贴了这篇博客里的一段话。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
1.首先需要在项目中添加PageHelper的依赖,这里我用的Maven添加
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
2.在mybatis的配置文件中添加对pagehelper 的配置
<configuration>
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 4.0.0以后版本可以不设置该参数 -->
<property name="dialect" value="mysql"/>
<!-- 该参数默认为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>
3.添加一个PageBean的类来储存分页的信息
public class PageBean<T> implements Serializable {
private static final long serialVersionUID = 1L;
private long total; //总记录数
private List<T> list; //结果集
private int pageNum; //第几页
private int pageSize; //每页记录数
private int pages; // 总页数
private int size; //当前页的数量<=pageSize
public PageBean(List<T> list){
if (list instanceof Page){
Page<T> page = (Page<T>) list;
this.pageNum = page.getPageNum();
this.pageSize = page.getPageSize();
this.total = page.getTotal();
this.pages = page.getPages();
this.list = page;
this.size = page.size();
}
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
}
下面就是业务逻辑的代码了
4.首先从mapper.xml文件写起,操作数据库的sql,查出我们所需要的数据
<select id="selectallList" parameterType="com.alarm.bean.AlarmParamModel"
resultMap="AlarmMap">
select message_id, seqnum, message_type, process_status,
distribute_status, processor, occur_time, close_time,
system_id, group_id, warn_level, message_content
from td_alarm_info
</select>
5.mapper的接口方法
public List<AlarmParamModel> selectallList(AlarmParamModel model);
6.service的接口方法
Datagrid selectallList(AlarmParamModel model,int pageNum, int pageSize);
7.service的实现类
这里需要注意下,是分页的主要逻辑。pageNum表示页码,pageSize表示每页显示的数目,startPag方法是初始的页面,orderBy方法是将数据按某个字段进行排序,这里我用的是occr_time的降序(desc)
public Datagrid selectallList(AlarmParamModel model,int pageNum, int pageSize){
PageHelper.startPage(pageNum, pageSize);
PageHelper.orderBy("occur_time desc");
List<AlarmParamModel> list = this.alarmMgrMapper.selectallList(model);
PageInfo<AlarmParamModel> pageInfo = new PageInfo<AlarmParamModel>(list);
Datagrid datagrid = new Datagrid(pageInfo.getTotal(),pageInfo.getList());
return datagrid;
}
8.注意到我这边用了一个Datagrid类,是用于向前台传数据用的类,包括total(总数)和rows(数据)
public class Datagrid {
private long total;
private List rows = new ArrayList<>();
public Datagrid() {
super();
}
public Datagrid(long total, List rows) {
super();
this.total = total;
this.rows = rows;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public List getRows() {
return rows;
}
public void setRows(List rows) {
this.rows = rows;
}
}
9.开始写controller层,调用之前写的方法
这里需要注意的是,offset和limit 是前台传来的页码和每页显示的数量,区别于bootstraptable 的offset和limit,那个offset表示的是偏移量,即如果每页显示10条数据,那么bootstrap中的第二页表示的offset就是10,第一页和第三页分别是0和20。而我这里的offset就是指代的pageNum。
@RequestMapping(value = "/AlarmInfo/list", method = {RequestMethod.GET,RequestMethod.POST})
@ResponseBody
public Datagrid alarmInfo(AlarmParamModel model,@RequestParam(value="offset",defaultValue="0",required=false)Integer pageNum,
@RequestParam(value="limit",defaultValue="10",required=false)Integer pageSize)
{
Datagrid datagrid = this.alarmMgrService.selectallList(model,pageNum, pageSize);
return datagrid;
}
10. 到现在前台的请求已经可以获取后台的数据并且分页了,我再将我的前台bootstrap table 的配置贴一下
$('#tb_departments').bootstrapTable({
url: 'http://10.1.234.134:8088/api/AlarmInfo/list', //请求后台的URL(*)
method: 'get', //请求方式(*)
striped: false, //是否显示行间隔色
cache: false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
pagination: true, //是否显示分页(*)
onlyInfoPagination:true, //设置为 true 只显示总数据数,而不显示分页按钮。需要 pagination='True'
sortable: true, //是否启用排序
sortOrder: "asc", //排序方式
queryParams: oTableInit.queryParams,//传递参数(*)
sidePagination: "server", //分页方式:client客户端分页,server服务端分页(*)
pageNumber:1, //初始化加载第一页,默认第一页
pageSize: 10, //每页的记录行数(*)
pageList: [10, 25, 50, 100], //可供选择的每页的行数(*)
search: false, //是否显示表格搜索,此搜索是客户端搜索,不会进服务端,所以,个人感觉意义不大
strictSearch: true,
showColumns: false, //是否显示所有的列
showRefresh: false, //是否显示刷新按钮
minimumCountColumns: 2, //最少允许的列数
clickToSelect: true, //是否启用点击选中行
checkboxHeader:true, //add
height: 500, //行高,如果没有设置height属性,表格自动根据记录条数觉得表格高度
uniqueId: "id", //每一行的唯一标识,一般为主键列
showToggle:false, //是否显示详细视图和列表视图的切换按钮
cardView: false, //是否显示详细视图
detailView: true,
detailFormatter:detailFormatter ,
paginationHAlign:"left",
paginationDetailHAlign:"right",
这里我没有用bootstrap自带的分页按钮,我是自己用jq写的按钮组,在下一篇文章我会把按钮代码贴出来,这样可自定义的程度会高一些~ 你也可以直接用bootstraptable子带的分页按钮,把配置改下就好。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。