用户列表显示
ID | 手机号 | 昵称 | 创建时间 | 状态 | 操作 | ||
---|---|---|---|---|---|---|---|
${item.id} | ${item.phone} | ${item.username} | ${item.qq} | ${item.createAt} |
|
在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条。如果在分页中发现了性能问题,可以根据这个思路调优。
物理分页:只从数据库中查询当前页的数据
优点:不占用很多内存 缺点:效率比价低(相比于逻辑分页)
逻辑分页:从数据库将所有记录查询出来,存储到内存中,展示当前页,然后数据再直接从内存中获取
优点:效率高 缺点:占用内存比较高
大多数情况下,我们用的都是物理分页。
物理分页:
优点:跨数据库 缺点:性能低
优点:性能高 缺点:不能跨数据库
关于分页,基本上在后台查询数据的时候都会用到,我在做项目的时候也遇到需要实现分页,用到了一个非常简单的插件工具PageHeper。这里我以我的项目为例具体说下怎么使用,写的不好还请见谅。
引入汾阳王插件的方式有以下两种方式:
1)直接导入Jar包,用eclipse做开发的基本都是直接下载jar包,然后导入到WEB-INF/lib文件夹下。直接点击下面链接下载最新版即可。
插件下载
2)使用Maven项目资源管理器(推荐使用)
对于没有用过Maven的我分享一个学习资源。也是我学习的方式。慕课网项目管理利器——Maven
在pom.xml中添加如下依赖
com.github.pagehelper
pagehelper
最新版本
下面是我的项目中Mybatis的配置文件mybatis-config.xml
下面以我项目中的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-generated method stub
return am.findAdmin(phone,password);
}
@Override
public Admin findAdminById(Integer id) {
// TODO Auto-generated method stub
return am.findAdminById(id);
}
@Override
public void updateAdmin(Admin admins) {
am.updateAdmin(admins);
}
}
一般把该类映射文件放在src/mapper文件夹下,下面附上我的adminMapper.xml文件
update admin set password=#{password,jdbcType=VARCHAR} where id=#{id,jdbcType=INTEGER}
前面的流程一般的项目开发都一样,需要分页的话加一个拦截器Pageintercetor,改一下mybatis配置文件即可。
整个开发过程中最复杂的就是controller类的编写,下面附上我的代码片段:
/*********************************************************
* 用户管理 1.查找所有用户 5.查询用户
*
**********************************************************/
/* 查找所有用户 */
@RequestMapping(value = "/userList")
@ResponseBody
public ModelAndView getUserList(@RequestParam("pageNum") int pageNum) {
ModelAndView modelAndView = new ModelAndView();
int pageSize = 10;
int total = userService.getUserNum();
List rows = userService.getPageUser(pageNum, pageSize);
UserGrid userGrid = new UserGrid();
userGrid.setCurrent(pageNum);
userGrid.setRowCount(pageSize);
userGrid.setRows(rows);
userGrid.setTotal(total);
modelAndView.addObject("userGrid", userGrid);
modelAndView.setViewName("admin/user/user_list");
return modelAndView;
}
/////我用到了XMLRootElement,
package com.ldu.util;
import javax.xml.bind.annotation.XmlRootElement;
import com.ldu.pojo.User;
import java.util.List;
/**
*
*/
@XmlRootElement
public class UserGrid {
private int current;//当前页面号
private int rowCount;//每页行数
private int total;//总行数
private List rows;
public int getCurrent() {
return current;
}
public void setCurrent(int current) {
this.current = current;
}
public int getRowCount() {
return rowCount;
}
public void setRowCount(int rowCount) {
this.rowCount = rowCount;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List getRows() {
return rows;
}
public void setRows(List rows) {
this.rows = rows;
}
}
附上XMLRootElemen教程
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;
在user_list.jsp文件中使用了bootstrap实现响应式页面设计,bootstrap教程
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
用户列表
用户列表显示
ID
手机号
昵称
QQ
创建时间
状态
操作
${item.id}
${item.phone}
${item.username}
${item.qq}
${item.createAt}
正常
禁用
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
后台管理系统
PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。
如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。
但是如果你写出下面这样的代码,就是不安全的用法:
PageHelper.startPage(1, 10);
List list;
if(param1 != null){
list = countryMapper.selectIf(param1);
} else {
list = new ArrayList();
}
这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
上面这个代码,应该写成下面这个样子:
List list;
if(param1 != null){
PageHelper.startPage(1, 10);
list = countryMapper.selectIf(param1);
} else {
list = new ArrayList();
}
这种写法就能保证安全。
如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:
List list;
if(param1 != null){
PageHelper.startPage(1, 10);
try{
list = countryMapper.selectAll();
} finally {
PageHelper.clearPage();
}
} else {
list = new ArrayList();
}
这么写很不好看,而且没有必要。