分页是一个被讲到烂掉的话题,今天我再拾起来踹几脚吧 
(Hibernate的分页做得很好很强大,用的人都知道 ,这个就不用再说了) 
1.为什么要分页? 
   首先是数据量太大会影响查询和传输的性能,关键是对用户来说一下看到数万条记录也不是那么友好。 
2.有哪些分页技术? 
a)存储过程分页 
   在数据库中创建一个存储过程,传入SQL和页码获得当前页的记录。需要对具体数据库的语法相当熟悉才能够编写,当然也可以直接google。性能最好,但不跨数据库平台。 
b)数据库专有sql特性分页 
   使用数据库专有的特性(MSSQL的top、Oracle的rownum、MySQL的limit等)实现当前页记录提取。性能也非常好,但也不跨数据库平台。 
(为什么非要跨数据库平台呢?好吧,如果你的项目确定是不会换数据库的那就这么写吧。) 
c)纯JDBC分页 
   通过Statement的setMaxRow(endIndex)和rs.absoulte(beginIndex)仅取得当前页范围内的记录。此种方式的性能依赖于厂商对JDBC规范的实现,如果厂商的rs读取是以流的形式进行的,性能还是有所保障的(好吧我承认是这不负责的说法)。这种方式的通用性是最好的,完全与数据库平台无关了(牺牲性能换取跨平台特性是Java平台的常用手法,嘿嘿嘿)。 
d)根据数据库类型自动生成数据库专有特性的sql语句   
   其实这就是Hibernate的实现方法,如果你觉得自己分析SQL语法然后将SQL转换为特定数据库语法比较麻烦,那就用Hibernate吧。如果你不想用Hibernate还是想用这种方法,那就麻烦你多辛苦一点,用力写出来给大家分享吧。

我们来理一理分页的逻辑吧 
首先,对用户而言他是不关心你是怎么分页的,他只要给你一个页码,然后你把那一页的记录给他就行了。 
为了能够取得指定页码所对应的记录,我们还需要两个关键的参数:每页记录数、总记录数。 
   每页记录数可以设个默认值,20、30都行吧。 
   总记录数就需要额外从数据库中取得了,这个也是没办法的事,谁让ResultSet没提供获取记录总数的方法呢。 
通过这两个参数能够计算出来总页数,同时也就可以判断用户给出的页码是否有效了(超出最后一页或者小于第一页),然后根据页码和每页记录数就可以算出来当前页的记录起止范围了。(这些个算术都是小学的,就不用说了吧,其实我也是想了半天的) 
为了表示方便,就把这些参数写成一个类,同时把计算方法也写进去

Java代码  
  1. /**
  2. *
  3. * @author Lixor(at)live.cn
  4. *
  5. */
  6. public class Page {
  7. private int rowTotal;// 总记录数
  8. private int pageSize = 10;// 每页记录数
  9. private int count;// 当前页码
  10. private int total;// 总页数
  11. private int beginIndex;//起始记录下标
  12. private int endIndex;//截止记录下标
  13. /**
  14. * 使用总记录数、当前页码构造
  15. *
  16. * @param rowTotal
  17. * @param count
  18. *            页码,从1开始
  19. */
  20. public Page(int totalRow, int count) {
  21. this.rowTotal = totalRow;
  22. this.count = count;
  23. calculate();
  24. }
  25. /**
  26. * 使用总记录数、当前页码和每页记录数构造
  27. *
  28. * @param rowTotal
  29. * @param count
  30. *            页码,从1开始
  31. * @param pageSize
  32. *            默认30条
  33. */
  34. public Page(int totalRow, int count, int pageSize) {
  35. this.rowTotal = totalRow;
  36. this.count = count;
  37. this.pageSize = pageSize;
  38. calculate();
  39. }
  40. private void calculate() {
  41. total = rowTotal / pageSize + ((rowTotal % pageSize) > 0 ? 1 : 0);
  42. if (count > total) {
  43. count = total;
  44. } else if (count < 1) {
  45. count = 1;
  46. }
  47. beginIndex = (count - 1) * pageSize ;
  48. endIndex = beginIndex + pageSize ;
  49. if (endIndex > rowTotal) {
  50. endIndex = rowTotal;
  51. }
  52. }
  53. public int getCount() {
  54. return count;
  55. }
  56. public int getTotal() {
  57. return total;
  58. }
  59. public int getTotalRow() {
  60. return rowTotal;
  61. }
  62. public int getPageSize() {
  63. return pageSize;
  64. }
  65. public int getBeginIndex() {
  66. return beginIndex;
  67. }
  68. public int getEndIndex() {
  69. return endIndex;
  70. }
  71. }

好了,可别散扯了,直接来个纯JDBC分页的示例吧

Java代码  
  1. //从页面取得页码
  2. int pageCount = 1;
  3. try {
  4. pageCount = Integer.parseInt(request.getParameter("page.count"));
  5. } catch (Exception ex) {}
  6. //取得总记录数,创建Page对象
  7. int totalRow = productDao.getProductAmount();//通过select count 取得总记录数
  8. Page page = new Page(totalRow, pageCount);
  9. productDao.list(page);

ProductDao.java

Java代码  
  1. public List<Product> list(Page page) {
  2. List<Product> productList = new ArrayList<Product>();
  3. try {
  4. String sql = "SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id";
  5. Connection conn = null;
  6. try {
  7. conn = DbUtil.getConnection();
  8. Statement st = conn.createStatement();
  9. st.setMaxRows(page.getEndIndex());//关键代码,设置最大记录数为当前页记录的截止下标
  10. ResultSet rs = st.executeQuery(sql);
  11. if (page.getBeginIndex() > 0) {
  12. rs.absolute(page.getBeginIndex());//关键代码,直接移动游标为当前页起始记录处
  13. }
  14. while (rs.next()) {
  15. Product product = new Product();
  16. ……
  17. productList.add(product);
  18. }
  19. rs.close();
  20. st.close();
  21. } finally {
  22. if (conn != null) {
  23. conn.close();
  24. }
  25. }
  26. } catch (SQLException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. return productList;
  31. }

最后我试了一下在MySQL上产品表里有10W数据时,执行的日志大概是

Java代码  
  1. 第1页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (0 milliseconds)
  2. 第3页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (0 milliseconds)
  3. 第5页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (0 milliseconds)
  4. 第10页 [http-8080-1] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (3 milliseconds)
  5. 第30页 [http-8080-1] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (6 milliseconds)
  6. 第100页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (15 milliseconds)
  7. 第1000页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (152 milliseconds)
  8. 第10000页 [http-8080-2] DEBUG org.logicalcobwebs.proxool.db_pms  - SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id;  (2030 milliseconds)

很显然纯JDBC分页的性能与页码有关(需要查询的数据范围,也就是说st.setMaxRow是有效的),越往后翻,性能越差。 
不过话说回来没有哪个用户去无聊的自己一页一页翻到第10000页去,一般都是翻两页找不到那就会进一步缩小查询条件的范围了。 
所以折衷的办法就是一方面页面记录数搞大一点,另外就是限制用户翻页的范围,超过10页就直接提示用户缩小查询范围,如果这样的话纯JDBC分页也就可以凑和着用了。

本文转自

jdbc分页的更多相关文章

  1. jdbc 分页

    连接数据库 public class DbUtil { private String driver = "oracle.jdbc.OracleDriver"; private St ...

  2. jdbc分页查询

    虽然现在db层的框架很多,用起来也非常的方便,像分页这种非常常用的功能也基本上都有对应的接口可以直接使用.但是有时候数据源不在配置的范围的时候,就必须要使用到jdbc来执行sql,jdbc执行的是原生 ...

  3. JDBC分页查询及实现

    当数据过多时,一页之内是无法显示的,因此需要进行分页显示. (一)分页技术实现: 物理分页: - 在数据库执行查询时(实现分页查询),查询需要的数据--依赖数据库的SQL语句 - 在sql查询时,从数 ...

  4. Struts+jdbc+分页 实例

    根据项目里分页实例,带有注解. package org.tarena.netctoss.dao.impl; import java.sql.Connection; import java.sql.Pr ...

  5. jdbc 实现分页

    jdbc 实现分页,的实现 原理这个就不介绍了.. 总之是用jdbc 的游标移动 package com.sp.person.sql.util; import java.sql.Connection; ...

  6. Hibernate执行原生SQL返回List<Map>类型结果集

    我是学java出身的,web是我主要一块: 在做项目的时候最让人别扭的就是hibernate查询大都是查询出List<T>(T指代对应实体类)类型 如果这时候我用的联合查询,那么返回都就是 ...

  7. Java 程序员在写 SQL 时常犯的 10 个错误

    Java程序员编程时需要混合面向对象思维和一般命令式编程的方法,能否完美的将两者结合起来完全得依靠编程人员的水准: 技能(任何人都能容易学会命令式编程) 模式(有些人用“模式-模式”,举个例子,模式可 ...

  8. java各种概念 Core Java总结

    Base: OOA是什么?OOD是什么?OOP是什么?{ oo(object-oriented):基于对象概念,以对象为中心,以类和继承为构造机制,来认识,理解,刻画客观世界和设计,构建相应的软件系统 ...

  9. Db2与Oracle的区别

    这个部分是自己随便整理下. 在工作上需要,公司需要DB2兼容Oracle. 不懂DB2与Oracle的细节,这里努力整理,以后精通了再回来重新修改. https://www.2cto.com/data ...

随机推荐

  1. acm专题---最小生成树

    kruscal(eloge): 题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1102 Problem Description There are N ...

  2. 关于函数strtok和strtok_r的使用要点和实现原理

    strtok函数的使用是一个老生常谈的问题了.该函数的作用很大,争议也很大.以下的表述可能与一些资料有区别或者说与你原来的认识有差异,因此,我尽量以实验为证.交代一下实验环境是必要的,winxp+vc ...

  3. html 简单学习

    通过记事本,依照以下四步来创建您的第一张网页. 步骤一:启动记事本 如何启动记事本: 开始    所有程序        附件            记事本 步骤二:用记事本来编辑 HTML 在记事本 ...

  4. iOS控制器与视图加载方法

    转载记录, 请看原文: 1. iOS中的各种加载方法(initWithNibName,loadNibNamed,initWithCoder,awakeFromNib等等)简单使用   http://w ...

  5. windows下怎样使用md命令一次建立多级子目录

    在Windows系统中一次只能够创建一个子目录,在命令提示符窗口则可以一次性创建多个子目录,例如如果想在f盘创建多级子目录,则md 23\13\65\45,后面的数字随便都可以.如果想一次性删除多级目 ...

  6. git 免密码配置

    1.cd ~/ 2.touch .git-credentials   (注意文件名前面有个  ”点”) 3.打开刚刚创建的文件,写入 https://username:password@github. ...

  7. centos7 更改时区

    Linux 系统(我特指发行版, 没说内核) 下大部分软件的风格就是不会仔细去考虑向后 的兼容性, 比如你上个版本能用这种程序配置, 没准到了下一个版本, 该程序已经不见了. 比如 sysvinit ...

  8. hdu 1690 Bus System (有点恶心)

    Problem Description Because of the huge population of China, public transportation is very important ...

  9. 【BZOJ 4631】4631: 踩气球 (线段树)

    4631: 踩气球 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 316  Solved: 153 Description 六一儿童节到了, SHUX ...

  10. Linux下burg引导

    用得比较久了,比grub顺手: 安装: sudo add-apt-repository ppa:n-muench/burg; sudo apt-get update; sudo apt-get ins ...