分页是一个被讲到烂掉的话题,今天我再拾起来踹几脚吧 
(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. Nginx - 隐藏或修改版本号

    1. 前言 无论是修改 Nginx 版本还是隐藏 Nginx 版本号,都是很简单的操作,对外来说,相对更安全些. 2. 修改 Nginx 版本号 对于修改 Nginx 版本号来说,需要在源码的基础上进 ...

  2. Java基础1,入门基础知识

    本文知识点(目录): 1.java简介    2.环境的搭建    3.关键字    4.标识符    5.注释    6.常量    7.进制的转换    8.变量    9.数据类型的转换    ...

  3. 产生随机数 random

    int rand(void); 返回 0 ------- RAND_MAX 之间的一个 int 类型整数,该函数为非线程安全函数.并且生成随机数的性能不是很好,已经不推荐使用.        void ...

  4. MySQL-开发规范升级版

    一.基础规范 表存储引擎必须使用InnoDB   表字符集默认使用utf8,必要时候使用utf8mb4 解读: (1)通用,无乱码风险,汉字3字节,英文1字节 (2)utf8mb4是utf8的超集,有 ...

  5. IntelliJ IDEA 创建maven项目一次后,然后删除,再次保存到此目录下,提供此目录已经被占用的问题。

    -------------------2017-02-14补充: 你看既然是创建过一次 不允许再次创建了,那么请问 第一次创建的 跑哪里去了,不仅仅是保存到了你指定的目录里,其实也默认安装到了 mav ...

  6. 关于IdByName 为什么一个消息主题要有 Id和 Name的解释

  7. 学习Leader选举算法

    读书笔记:<从Paxos到Zookeeper 分布式一致性原理与实践> 选举的前提约定 观察者不参与选举,只有跟随者才参与选举. 优先选事务ID(ZXID)大的,事务Id相同再优先选服务器 ...

  8. python开发学习-day06(模块拾忆、面向对象)

    s12-20160130-day06 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...

  9. Apache+PHP环境搭建

    第一次搭建Apache+PHP+MySQL的开发环境,发现Apache与PHP的整合非常麻烦,先整理记录如下: 一.安装Apache 1.登录http://httpd.apache.org/downl ...

  10. 洛谷P2147 [SDOI2008] 洞穴勘探 [LCT]

    题目传送门 洞穴勘探 题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道 ...