分页的目的
- 前台分页
1、提高性能,在某些情况下,我们并不需要查询很大的数据量,在这种情况下,我们在程序中开辟一个内存(其实就是自己定义一个变量~),专门用来存放查询数据,这样每次点击上一页,下一页,就不必每次和数据库进行交互 - 后台分页
1、提高性能,一次查20个,比一次查20000个性能肯定更好;另外如果数据量很大,一次性将内容都查询出来,查询出来的结果是放在内存里面的,内存没有这么大
2、不需要这么多数据,如新闻,一般人可能只看最近前20条;如果我们将后面的也都查询出来了,就是浪费
3、展现层面的考虑:如果一次展现太多的数据,不管是排版,还是美观上都不好 - 任何工具都存在优点与缺点,关键在于如何根据实际情况选择恰当的分页方式~
这里是前台页面~
前台分页
原理:前台分页也叫假分页,具体实现原理就是一次性从数据库中将需要的数据全部查询出来,放入程序开辟的一个内存 (其实就是自己定义的一个变量) 中,然后每次取数据都从内存 (还是那个变量)中取,不用频繁与数据库进行交互,适用于查询数据较少的情况。
首先来解释一下下面代码的大体意思:
- 在前台JSP页面JS脚本中,定义一个userMgr 对象(自己命名),体现Java面向对象思想,这里看不懂也没关系,后面的前台分页思想看明白就好….首先定义四个变量,用来初始化分页时,其中userDate就是开辟出的内存(自己定义的那个变量~)
- 然后是四个函数,分别对应首页,上一页,下一页和尾页功能,这四个函数里的对查询函数和遍历函数的调用就是前台分页的关键,如果不这样做,那么每次查询还是会和数据库进行交互,就失去了前台分页的意义
- 然后是查询函数,利用ajax和后台进行数据交互
- 最后是数据遍历函数,用来遍历每页数据
代码实现:
这里的后台Sql语句与普通查询无异,就不多赘述。
<script type="text/javascript">
var userMgr = {
currPage : 1, //当前页
pageSize : 2, //每页记录数
totalPage :null, //总页数
userData :null, //用于缓存数据的变量
//对应的按钮写对应的方法响应
gotoFirst : function(){
if(userMgr.currPage==1){
alert("已经是第一页了");
return;
}
userMgr.currPage =1;
//如果缓存里面有数据,则不进行数据访问,直接利用缓存数据填充页面
if(userMgr.userData!=null){
//就不能调用tzQueryUser();
userMgr.tzFillUserListTable(userMgr.userData);
}else{
userMgr.tzQueryUser();
}
},
gotoBefore : function(){
userMgr.currPage--;
if(userMgr.currPage<1){
alert("已经第一页了");
return;
}
//如果缓存里面有数据,则不进行数据访问,直接利用缓存数据填充页面
if(userMgr.userData!=null){
//就不能调用tzQueryUser();
userMgr.tzFillUserListTable(userMgr.userData);
}else{
userMgr.tzQueryUser();
}
},
gotoNext : function(){
userMgr.currPage++;
if(userMgr.currPage>userMgr.totalPage){
alert("已经是最后一页了");
return;
}
//如果缓存里面有数据,则不进行数据访问,直接利用缓存数据填充页面
if(userMgr.userData!=null){
//就不能调用tzQueryUser();
userMgr.tzFillUserListTable(userMgr.userData);
}else{
userMgr.tzQueryUser();
}
},
gotoLast : function(){
if(userMgr.currPage==userMgr.totalPage){
alert("已经是最后一页了");
return;
}
userMgr.currPage = userMgr.totalPage;
//如果缓存里面有数据,则不进行数据访问,直接利用缓存数据填充页面
if(userMgr.userData!=null){
//就不能调用tzQueryUser();
userMgr.tzFillUserListTable(userMgr.userData);
}else{
userMgr.tzQueryUser();
}
},
//查询用户方法
tzQueryUser :function(){
//这里是模糊查询,获得参数传给后台
var userNameQuery = $("#userNameQuery").val();
$.ajax({
type:'post',//请求方式
url:'<%=path%>/pages/user/userAction?method=queryUserList',
dataType:'json', //有几种格式 xml html json text 等常用
//data传值的另外一种方式 form的序列化
data:{"userNameQuery":userNameQuery},//传递给服务器的参数
success:function(data){//与服务器交互成功调用的回调函数
//data就是out.print输出的内容
if(data=='error'){
alert("查询用户记录失败");
}else{
userMgr.userData = data;
userMgr.tzFillUserListTable(data);
}
}
});
},
tzFillUserListTable : function(data){
var htmlTable = "";
if(data.length>0){
//页数:currPage = 1 每页记录条数:pageSize = 2
//通过页数和每页记录条数,计算显示返回数据数组的那几条记录
userMgr.totalPage = Math.ceil(data.length/userMgr.pageSize);
var beginRow = (userMgr.currPage-1)*userMgr.pageSize;
var endRow = userMgr.currPage*userMgr.pageSize;
//如果通过页数和页记录计算出来的endRow大于数据总记录 ,则讲endRo缩小至data.length
if(endRow>data.length){
endRow = data.length;
}
//for(var i=0;i<data.length;i++){
for(var i = beginRow;i<endRow;i++){
htmlTable = htmlTable+"<tr>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+(i+1);
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+data[i].userName;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+data[i].deptName;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+data[i].roleName;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+data[i].remark;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+getFormatDateByLong(data[i].tvUpdate.time,null);
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
var editButton = "<button type='button' class='btn btn-default' id = 'editBtn' onclick='userMgr.tzEditUser(2,"+data[i].userId+");'>修改</button>";
var delButton = "<button type='button' class='btn btn-default ' id = 'delBtn' onclick='userMgr.tzDelUser("+data[i].userId+");'>删除</button>";
htmlTable = htmlTable+editButton+delButton;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"</tr>";
}
}else{
htmlTable = "暂时没有查询到记录 ";
}
$('#userListTable').find('tbody').html(htmlTable);
}
}
</script>
后台查询
原理:后台分页也叫真分页,具体实现原理就是根据不同的数据库,编写不同的分页Sql语句,比如,MySql数据库是根据limit进行分页,而Oracle是根据Rownumber进行分页,这样每次都和数据库进行交互,每次传输数据都不相同。
代码实现:
补充:
一般的分页查询使用简单的 limit 子句就可以实现。limit 子句声明如下:
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
LIMIT 子句可以被用于指定 SELECT 语句返回的记录数。需注意以下几点:
第一个参数指定第一个返回记录行的偏移量
第二个参数指定返回记录行的最大数目
如果只给定一个参数:它表示返回最大的记录行数目
第二个参数为 -1 表示检索从某一个偏移量到记录集的结束所有的记录行
初始记录行的偏移量是 0(而不是 1)
下面是一个应用实例:
select * from orders_history where type=8 limit 1000,10;
该条语句将会从表 orders_history 中查询第1000条数据之后的10条数据,也就是第1001条到第1010条数据。
然后就是后台分页代码部分了~
package com.tz.jspstudy.framework.page.dto;
/**
* 分页对象,注意其中init()方法是为了校正前台分页传来的分页属性
* totalRow 某次查询的总记录数 -1就表示这个分页查询是第一次使用
* pageNo 当前查询页码
* pageSize 每页记录条数 -1 代表查所有的数据
* begRow 页起始记录行数
* endRow 页的截止记录行数
* 类描述:
* 类名称:com.tz.jspstudy.framework.page.dto.PageObject
* 创建人:keven
* 创建时间:2016年8月10日 下午8:44:58
* @version V1.0
*/
public class PageObject implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private int totalRow=-1;
private int pageNo;
private int pageSize;
private int begRow;
private int endRow;
private final int DEFAULT_PAGESIZE=40;
private final int MAX_PAGESIZE=1000;
/**
* @return the mAX_PAGESIZE
*/
public int getMAX_PAGESIZE() {
return MAX_PAGESIZE;
}
public PageObject(int pageNo,int totalRows){
this.totalRow = totalRows;
this.pageNo = pageNo;
init();
}
public PageObject(int pageNo,int pageSize,int totalRows){
this.totalRow = totalRows;
this.pageNo = pageNo;
this.pageSize = pageSize;
init();
}
/**
* 根据构造数据初始页对象
*/
private void init() {
//校正totalRows && pageNo && pageSize
if (pageNo <= 0) pageNo = 1;
if (pageSize == 0) pageSize = DEFAULT_PAGESIZE;
if (pageSize > MAX_PAGESIZE) pageSize = MAX_PAGESIZE;
if (pageSize > 0){
begRow = (pageNo - 1 ) * pageSize + 1;
endRow = pageNo * pageSize;
} else {
begRow = 1;
endRow = MAX_PAGESIZE;
}
}
/**
* @return the totalRow
*/
public int getTotalRow() {
return totalRow;
}
/**
* @param totalRow the totalRow to set
*/
public void setTotalRow(int totalRow) {
this.totalRow = totalRow;
}
/**
* @return the pageNo
*/
public int getPageNo() {
return pageNo;
}
/**
* @param pageNo the pageNo to set
*/
public void setPageNo(int pageNo) {
this.pageNo = pageNo;
}
/**
* @return the pageSize
*/
public int getPageSize() {
return pageSize;
}
/**
* @param pageSize the pageSize to set
*/
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
/**
* @return the begRow
*/
public int getBegRow() {
return begRow;
}
/**
* @param begRow the begRow to set
*/
public void setBegRow(int begRow) {
this.begRow = begRow;
}
/**
* @return the endRow
*/
public int getEndRow() {
return endRow;
}
/**
* @param endRow the endRow to set
*/
public void setEndRow(int endRow) {
this.endRow = endRow;
}
/**
* @return the dEFAULT_PAGESIZE
*/
public int getDEFAULT_PAGESIZE() {
return DEFAULT_PAGESIZE;
}
}
下面来解释一下代码大意:
- 首先定义四个变量,用来初始化分页,当totalRows 为-1时代表是第一次查询,首先会根据分页查询语句从数据库查询出记录总个数,然后和pageSize 一起用来计算totalPage
- 然后是四个函数,分别对应首页、上一页、下一页、尾页,这里和上面前台分页的区别就在于没有了缓存变量和遍历函数,每次都会调用查询方法和后台进行交互
- 然后是普通的查询方法
<script type="text/javascript">
//顶一个js对象
var userMgr = {
currPage : 1, //当前页
pageSize : 2, //每页记录数
totalRows :-1, //总的记录数
totalPage :null, //总页数
//对应的按钮写对应的方法响应
gotoFirst : function(){
if(userMgr.currPage==1){
alert("已经是第一页了");
return;
}
userMgr.currPage =1;
userMgr.tzQueryUser();
},
gotoBefore : function(){
userMgr.currPage--;
if(userMgr.currPage<1){
alert("已经第一页了");
return;
}
userMgr.tzQueryUser();
},
gotoNext : function(){
userMgr.currPage++;
if(userMgr.currPage>userMgr.totalPage){
alert("已经是最后一页了");
return;
}
userMgr.tzQueryUser();
},
gotoLast : function(){
if(userMgr.currPage==userMgr.totalPage){
alert("已经是最后一页了");
return;
}
userMgr.currPage = userMgr.totalPage;
userMgr.tzQueryUser();
},
//查询用户方法
tzQueryUser :function(){
var userNameQuery = $("#userNameQuery").val();
$.ajax({
type:'post',//请求方式
url:'<%=path%>/pages/user/userAction?method=queryUserList',
dataType:'json', //有几种格式 xml html json text 等常用
//data传值的另外一种方式 form的序列化
data:{"userNameQuery":userNameQuery,"pageNo":userMgr.currPage,
"pageSize" :userMgr.pageSize,"totalRows" :userMgr.totalRows},//传递给服务器的参数
success:function(data){//与服务器交互成功调用的回调函数
//data就是out.print输出的内容
if(data=='error'){
alert("查询用户记录失败");
}else{
var htmlTable = "";
//页数:currPage = 1 每页记录条数:pageSize = 2
//通过页数和每页记录条数,计算显示返回数据数组的那几条记录
userMgr.totalPage = Math.ceil(data.totalRows/userMgr.pageSize);//json对象里面的总记录数
var userList = data.dataList;//json对象里面详细记录信息
var dataNo = (userMgr.currPage-1)*userMgr.pageSize+1;
for(var i = 0;i<userList.length;i++){
htmlTable = htmlTable+"<tr>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+(i+dataNo);
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+userList[i].userName;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+userList[i].deptName;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+userList[i].roleName;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+userList[i].remark;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
htmlTable = htmlTable+getFormatDateByLong(userList[i].tvUpdate.time,null);
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"<td>";
var editButton = "<button type='button' class='btn btn-default' id = 'editBtn' onclick='userMgr.tzEditUser(2,"+userList[i].userId+");'>修改</button>";
var delButton = "<button type='button' class='btn btn-default ' id = 'delBtn' onclick='userMgr.tzDelUser("+userList[i].userId+");'>删除</button>";
htmlTable = htmlTable+editButton+delButton;
htmlTable = htmlTable+"</td>";
htmlTable = htmlTable+"</tr>";
}
$('#userListTable').find('tbody').html(htmlTable);
}
}
});
}
</script>
总结
前台分页与后台分页各有适用的情况,要做到灵活运用,我还有一段路要走~