分页是一个被讲到烂掉的话题,今天我再拾起来踹几脚吧 
(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. CEPH 使用SSD日志盘+SATA数据盘, 随OSD数目递增对性能影响的递增测试

    最近建设新机房,趁项目时间空余较多,正好系统的测试一下CEPH集群性能随OSD数目的变化情况, 新ceph集群测试结果如下: 1)4k随机读在3/6/9osd host下的性能差不多,吞吐量约50~6 ...

  2. golang基础之三-字符串,时间,流程控制,函数

    strings和strconv的使用 strings strings.HasPrefix(s string,preffix string) bool:判断字符串s是否以prefix开头 stirngs ...

  3. Flyweight模式(亨元模式)

    这应该算是最好理解的一个设计模式了吧·················· 面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,字处理软件,如果以每个文字都作为一个 ...

  4. Hadoop案例(八)辅助排序和二次排序案例(GroupingComparator)

    辅助排序和二次排序案例(GroupingComparator) 1.需求 有如下订单数据 订单id 商品id 成交金额 0000001 Pdt_01 222.8 0000001 Pdt_05 25.8 ...

  5. 趣味js【练习题】

    1.无限极函数递归,使每次的参数相乘 需求:add(1)(2)(3)(4)(5) 1.1首先要知道一个东西,就是function每次调用,都会默认执行tosting 1.2利用递归,每次返回的都是函数 ...

  6. Redis实战(六)

    查询数据 1.使用Linq匹配关键字查询 using (var redisClient = RedisManager.GetClient()) { var user = redisClient.Get ...

  7. 今日头条、Face++开发岗面经

    今日头条.Face++开发岗面经 [头条] 两个栈实现一个队列.怎么优化 数组每一个元素找出数组右边第一个大于自己的数 实现LRU TCP四次握手 滑动窗口.窗口大小 线程与进程区别 什么是线程安全 ...

  8. Group_Concat函数示例

    1. 函数定义: GROUP_CONCAT([DISTINCT] expr [,expr ...] [ORDER BY {unsigned_integer | col_name | expr} [AS ...

  9. salt 常用命令整理

    salt 常用命令整理 ***********模块*********** 查看模块列表module salt 'minion' sys.list_modules 查看指定module的function ...

  10. 【记录】Mysql 5.7 解压版的安装

    1.解压 2.打开my_default.ini 将basedir修改为MySQL的解压目录 将datadir修改为MySQL的解压目录\data 3.更改环境变量 系统变量里面添加MYSQL_HOME ...