Java Web 之分页技术
本文包括:
1、分页技术概述
2、实现分页
3、完善分业——分页工具条
4、几种常见的分页工具条
1、分页技术概述
-
物理分页
-
在SQL查询时,从数据库只查询分页需要的数据
-
通常,对于不同数据库有不同的物理分页语句
MySQL 使用limit;
SQLServer 使用top;
Oracle使用rowNum -
对于MySQL,采用limit关键字
-
例如:查询第11-20条数据,SQL语句:
select * from user limit 10,10;
-
demo:
@Test public void demo2() throws SQLException { // 物理分页 ,根据数据库关键字 limit 查询需要数据 查询150-200条 String sql = "select * from customer order by name limit ?,?"; int start = 150 - 1; // 开始索引 开始条数-1 int len = 200 - 150 + 1; // 结束条数-开始条数 +1 QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); List<Customer> customers = queryRunner.query(sql, new BeanListHandler<Customer>(Customer.class), start, len); System.out.println("size:" + customers.size()); for (Customer customer : customers) { System.out.println(customer.getName()); } }
-
-
逻辑分页
-
在SQL查询时,先从数据库查询出所有数据的结果集
-
在Java代码中通过逻辑语句获得分页需要的数据
-
例如:查询第11-20条数据:
userList.subList(10,20)
-
demo:
@Test public void demo3() throws SQLException { // 逻辑分页 150 - 200 String sql = "select * from customer order by name"; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); List<Customer> customers = queryRunner.query(sql, new BeanListHandler<Customer>(Customer.class)); customers = customers.subList(150 - 1, 200); System.out.println("size:" + customers.size()); for (Customer customer : customers) { System.out.println(customer.getName()); } }
-
-
性能上,物理分页明显好于逻辑分页,尽量使用物理分页。
2、实现分页
-
分类查询UML图
UML绘制软件:Jude(Java and UML Developer's Environment)
Jude教程:http://blog.csdn.net/shesunshine/article/details/5670862
-
在JSP页面新增a链接,其中pageQuery为PageQueryServlet的URL:
<a href="XX/pageQuery?pNum=1">分页查询</a>
-
PageQueryServlet:
public class PageQueryServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获得客户端提交页码 String pNumStr = request.getParameter("pNum"); int pNum = Integer.parseInt(pNumStr);// 如果不是数字报错 // 将页码传递 业务层 CustomerService customerService = new CustomerService(); List<Customer> customers = customer Service.pageQuery(pNum); // 传递结果进行显示 request.setAttribute("customers", customers); request.getRequestDispatcher("/list.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
-
CustomerService中设置常量、新增pageQuery(int pNum)方法:
public static final int NUMBERPAGE = 10; // 设置每页条数为常量 public List<Customer> pageQuery(int pNum){ // 根绝页码和每页条数计算开始索引 int start = (pNum - 1) * NUMBERPAGE; // 调用DAO进行分页查询 CustomerDAO customerDAO = new CustomerDAO(); return customerDAO.findByPage(start, NUMBERPAGE); }
-
CustomerDAO中新增findByPage(int pNum, int numberPage)方法:
public List<Customer> findByPage(int start, int numberPage){ String sql = "select * from customer limit ?,?"; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); // 利用DBUtils开源工具进行JDBC编程 try{ return queryRunner.query(sql,new BeanListHandler<Customer>(Customer.class)); } catch(SQLException e){ e.printStackTrace(); } return null; }
3、完善分页——分页工具条
-
实现分页中的虽然能提供分页,但是需要手动在地址栏输入第几页,这显然对用户极不友好,不过别急,上面只是实现了分页的效果。
-
很多网站都提供了分页功能,分页页面效果:
首页 上一页 1 2 3 4 5 6 7 下一页 尾页
-
上面的工具条只适用页数很少的业务,google查询的页数有上万页,不可能全部显示在页面上,也不可能提供“尾页”这个选项,所以以当前页为中心,提供前后5页的跳转链接,下面是一种可借鉴的分页工具条(假设当前页数为10):
上一页 5 6 7 8 9 10 11 12 13 14 15 下一页
谷歌的分页工具条:
-
现在一般的做法,分页查询都会用单独类来封装查询结果
PageBean ----- 在业务层返回数据返回PageBean对象
public class PageBean { public static final int NUMPERPAGE = 10; // 每页多少条 private int pNum; // 当前第几页 private int totalPageNum; // 总页数 private int totalRecordNum; // 总记录数 private List<Customer> customers; // 结果数据 public int getpNum() { return pNum; } public void setpNum(int pNum) { this.pNum = pNum; } public int getTotalPageNum() { return totalPageNum; } public void setTotalPageNum(int totalPageNum) { this.totalPageNum = totalPageNum; } public int getTotalRecordNum() { return totalRecordNum; } public void setTotalRecordNum(int totalRecordNum) { this.totalRecordNum = totalRecordNum; } public List<Customer> getCustomers() { return customers; } public void setCustomers(List<Customer> customers) { this.customers = customers; } }
-
于是,在CustomerService修改pageQuery(int pNum)方法:
public static final int NUMBERPAGE = 10; // 设置每页条数为常量 public PageBean pageQuery(int pNum) { // 根据页码 和 每页条数 计算开始索引 int start = (pNum - 1) * NUMPERPAGE; PageBean bean = new PageBean(); // 封装当前页码 bean.setpNum(pNum); // 调用DAO进行分页查询 --- 结果数据 CustomerDAO customerDAO = new CustomerDAO(); List<Customer> customers = customerDAO.findByPage(start, PageBean.NUMPERPAGE); bean.setCustomers(customers); // 封装总记录条数,findTotalRecordNum()方法见下文 int totalRecordNum = customerDAO.findTotalRecordNum(); bean.setTotalRecordNum(totalRecordNum); // 计算总页数,很常用!!! int totalPageNum = (totalRecordNum + PageBean.NUMPERPAGE - 1) / PageBean.NUMPERPAGE; bean.setTotalPageNum(totalPageNum); return bean; }
-
在CustomerDAO中新增findTotalRecordNum()方法:
// 查询总记录条数 public int findTotalRecordNum() { String sql = "select count(*) from customer"; QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); // ScalarHandler try { // 因为结果集只有一行一列,所以这里应该用ScalarHandler long totalRecordNum = (Long) queryRunner.query(sql, new ScalarHandler(1)); return (int) totalRecordNum; // int表示的范围足够了 } catch (SQLException e) { e.printStackTrace(); } return 0; }
-
于是,在PageQueryServlet中修改:
修改前:
List<Customer> customers = customer Service.pageQuery(pNum); // 传递结果进行显示 request.setAttribute("customers", customers); request.getRequestDispatcher("/list.jsp").forward(request, response);
修改后:
PageBean pageBean = customerService.pageQuery(pNum); // 传递结果进行显示 request.setAttribute("pageBean", pageBean); // ${pageBean} request.getRequestDispatcher("/page_list.jsp").forward(request, response);
-
接下来就是编写JSP页面:
-
预期效果:
-
现在的问题是:根本不知道哪个是当前页,所以还要改进一下foreach中的代码:
<!-- 当前页不显示链接,即可知道哪个是当前页 --> <!-- 利用foreach循环输出 --> <c:forEach begin="${begin}" end="${end}" var="i"> <c:if test="${pageBean.pNum==i}"> ${i } </c:if> <c:if test="${pageBean.pNum!=i}"> <a href="/pageQuery?pNum=${i }">${i } </a> </c:if> </c:forEach>
-
现在即可清晰的显示当前页了(可用CSS/JavaScript进一步美化界面,功能实现到此为止)
-
实现输入页码跳转,在尾页代码的后面加入input:
<input type="text" id="pNum" size="2"/><input type="button" value="go" onclick="jump();"/>
对应的JavaScript代码:
<script type="text/javascript"> function jump(){ // 获得用户输入页码 var pNum = document.getElementById("pNum").value; location.href="/pageQuery?pNum=" + pNum; } </script>
-
4、几种常见的分页工具条
-
百度
强迫症看着难受,为什么前面显示5页,后面显示4页???
-
必应
必应的分页工具条很简洁。
-
CSDN博客:http://blog.csdn.net/
这种分页工具栏比较有意思,我们来分析一下:
- 利用Chrome浏览器的检查功能:
-
它的分页工具条只显示5页,最左边的页码是
m*5+1 (m为非负整数)
,最右边的页码是(m+1)*5
,点击左侧的...
,上述的m
变为m-1
,点击右侧的...
,上述的m
变为m+1
。 -
它的重点在于计算当前页所属的
m
值,稍微思考一下,可以得出当前页pNum
与当前页所属的m
值的关系:int m = pNum/5;
-
注意分页工具条左侧的
...
,当前页为6时,它会跳转到第1页,所以无论是左侧还是右侧的...
,都将会跳转到对应m
值的第1页。