jdbc分页
分页是一个被讲到烂掉的话题,今天我再拾起来踹几脚吧
(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没提供获取记录总数的方法呢。
通过这两个参数能够计算出来总页数,同时也就可以判断用户给出的页码是否有效了(超出最后一页或者小于第一页),然后根据页码和每页记录数就可以算出来当前页的记录起止范围了。(这些个算术都是小学的,就不用说了吧,其实我也是想了半天的)
为了表示方便,就把这些参数写成一个类,同时把计算方法也写进去

- /**
- *
- * @author Lixor(at)live.cn
- *
- */
- public class Page {
- private int rowTotal;// 总记录数
- private int pageSize = 10;// 每页记录数
- private int count;// 当前页码
- private int total;// 总页数
- private int beginIndex;//起始记录下标
- private int endIndex;//截止记录下标
- /**
- * 使用总记录数、当前页码构造
- *
- * @param rowTotal
- * @param count
- * 页码,从1开始
- */
- public Page(int totalRow, int count) {
- this.rowTotal = totalRow;
- this.count = count;
- calculate();
- }
- /**
- * 使用总记录数、当前页码和每页记录数构造
- *
- * @param rowTotal
- * @param count
- * 页码,从1开始
- * @param pageSize
- * 默认30条
- */
- public Page(int totalRow, int count, int pageSize) {
- this.rowTotal = totalRow;
- this.count = count;
- this.pageSize = pageSize;
- calculate();
- }
- private void calculate() {
- total = rowTotal / pageSize + ((rowTotal % pageSize) > 0 ? 1 : 0);
- if (count > total) {
- count = total;
- } else if (count < 1) {
- count = 1;
- }
- beginIndex = (count - 1) * pageSize ;
- endIndex = beginIndex + pageSize ;
- if (endIndex > rowTotal) {
- endIndex = rowTotal;
- }
- }
- public int getCount() {
- return count;
- }
- public int getTotal() {
- return total;
- }
- public int getTotalRow() {
- return rowTotal;
- }
- public int getPageSize() {
- return pageSize;
- }
- public int getBeginIndex() {
- return beginIndex;
- }
- public int getEndIndex() {
- return endIndex;
- }
- }
好了,可别散扯了,直接来个纯JDBC分页的示例吧

- //从页面取得页码
- int pageCount = 1;
- try {
- pageCount = Integer.parseInt(request.getParameter("page.count"));
- } catch (Exception ex) {}
- //取得总记录数,创建Page对象
- int totalRow = productDao.getProductAmount();//通过select count 取得总记录数
- Page page = new Page(totalRow, pageCount);
- productDao.list(page);
ProductDao.java

- public List<Product> list(Page page) {
- List<Product> productList = new ArrayList<Product>();
- try {
- 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";
- Connection conn = null;
- try {
- conn = DbUtil.getConnection();
- Statement st = conn.createStatement();
- st.setMaxRows(page.getEndIndex());//关键代码,设置最大记录数为当前页记录的截止下标
- ResultSet rs = st.executeQuery(sql);
- if (page.getBeginIndex() > 0) {
- rs.absolute(page.getBeginIndex());//关键代码,直接移动游标为当前页起始记录处
- }
- while (rs.next()) {
- Product product = new Product();
- ……
- productList.add(product);
- }
- rs.close();
- st.close();
- } finally {
- if (conn != null) {
- conn.close();
- }
- }
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return productList;
- }
最后我试了一下在MySQL上产品表里有10W数据时,执行的日志大概是

- 第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)
- 第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)
- 第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)
- 第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)
- 第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)
- 第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)
- 第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)
- 第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分页的更多相关文章
- jdbc 分页
连接数据库 public class DbUtil { private String driver = "oracle.jdbc.OracleDriver"; private St ...
- jdbc分页查询
虽然现在db层的框架很多,用起来也非常的方便,像分页这种非常常用的功能也基本上都有对应的接口可以直接使用.但是有时候数据源不在配置的范围的时候,就必须要使用到jdbc来执行sql,jdbc执行的是原生 ...
- JDBC分页查询及实现
当数据过多时,一页之内是无法显示的,因此需要进行分页显示. (一)分页技术实现: 物理分页: - 在数据库执行查询时(实现分页查询),查询需要的数据--依赖数据库的SQL语句 - 在sql查询时,从数 ...
- Struts+jdbc+分页 实例
根据项目里分页实例,带有注解. package org.tarena.netctoss.dao.impl; import java.sql.Connection; import java.sql.Pr ...
- jdbc 实现分页
jdbc 实现分页,的实现 原理这个就不介绍了.. 总之是用jdbc 的游标移动 package com.sp.person.sql.util; import java.sql.Connection; ...
- Hibernate执行原生SQL返回List<Map>类型结果集
我是学java出身的,web是我主要一块: 在做项目的时候最让人别扭的就是hibernate查询大都是查询出List<T>(T指代对应实体类)类型 如果这时候我用的联合查询,那么返回都就是 ...
- Java 程序员在写 SQL 时常犯的 10 个错误
Java程序员编程时需要混合面向对象思维和一般命令式编程的方法,能否完美的将两者结合起来完全得依靠编程人员的水准: 技能(任何人都能容易学会命令式编程) 模式(有些人用“模式-模式”,举个例子,模式可 ...
- java各种概念 Core Java总结
Base: OOA是什么?OOD是什么?OOP是什么?{ oo(object-oriented):基于对象概念,以对象为中心,以类和继承为构造机制,来认识,理解,刻画客观世界和设计,构建相应的软件系统 ...
- Db2与Oracle的区别
这个部分是自己随便整理下. 在工作上需要,公司需要DB2兼容Oracle. 不懂DB2与Oracle的细节,这里努力整理,以后精通了再回来重新修改. https://www.2cto.com/data ...
随机推荐
- java并发编程实战笔记---(第二章)线程安全:正确性
ThreadA__________ 同步 ______________ 异步 ___________ 异步 ThreadB__________ ____________ ...
- JAVA封装消息中间件调用一(kafka生产者篇)
这段时间因为工作关系一直在忙于消息中间件的发开,现在趁着项目收尾阶段分享下对kafka的一些使用心得. kafka的原理我这里就不做介绍了,可参考http://orchome.com/kafka/in ...
- redis关闭保护模式
1. set key出现的报错 在192.168.56.57客户端登录192.168.56.56的redis服务器时,报错如下: [root@localhost src]# ./redis-cli - ...
- csu 1798(树上最远点对,线段树+lca)
1798: 小Z的城市 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 60 Solved: 16[Submit][Status][Web Board] ...
- hdu 5202(DFS)
Rikka with string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- 【hdoj_1250】Hat's Fibonacci(大数)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1250 思路:本题的Fibonacci数列是扩展的四阶的Fibonacci数列,用递推关系式求解即可. 题目 ...
- PHP 分割字串 Function 的速度比較(substr/sscanf/preg_match)---substr最快!
固定長度的字串(假設是 06481a63041b578d702f159f520847f8), 要照固定格式做切割, 使用 PHP 要怎麼切會比較快? 註: 要將此字串切成 => 06 / 48 ...
- thinkphp5.1使用phpstudy隐藏index.php
apache的重写规则如下: <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews RewriteEngine on R ...
- 【LOJ】#2017. 「SCOI2016」围棋
题解 考虑到状态数比较复杂,其实我们需要轮廓线dp-- 我们设置\(f[x][y][S][h][k]\)为考虑到第(x,y)个格子,S是轮廓线上的匹配状态,是二进制,如果一位是1表示这一位匹配第一行匹 ...
- intellij自动生成java代码注释(java文件注释和方法注释)
1定义java文件头部的注释 2给java类中的方法添加上注释 2.1第一步勾选Enable Live Templates 2.2第二步新建一个Group 2.3第三步新建一个Template 2. ...