`

jdbc分页

阅读更多

数据库分页查询一般分为两步,
(1)根据查询条件,count 记录总数
(2)根据当前页的数据范围(起始位置offset, 每页数据个数span),从符合查询条件的记录集 取出对应范围的数据。

一、根据范围取数据的方法
如果单纯用JDBC从ResultSet中取出一个指定范围(offset, span)的数据,可以采用这样的方法。

ps = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setMaxRows(offset + span);
rs = ps.executeQuery();
rs.absolute(offset);
while(rs.next())...


数据量大的时候,页数很多,offset很大,这种方法不太适合。这时候,需要使用各数据库的native SQL特性。
我们来看Hibernate dialect package的类,支持了各种数据库的getLimitString方法。这里举Mysql和Oracle的例子。假设查询语句为

Select * from message where forum_id = ? and created_time > ? order by created_time desc


Mysql 的limit SQL为

Select * from message where forum_id = ? and created_time > ? order by created_time desc
limit ?, ?

后面的两个limit ?, ? 分别为 offset, span。

Oracle的limit SQL为

select * from ( select row_.*, rownum rownum_ from (
Select * from message where forum_id = ? and created_time > ? order by created_time desc
) row_ where rownum <= ?) where rownum_ > ?

后面的两个limit ?, ? 分别为 offset + span, offset。

二、缓存 & QueryKey
count语句可以根据查询语句自动生成,比如

Select count(*) from (
Select * from message where forum_id = ? and created_time > ? order by created_time desc
)


这样的自动count语句有些浪费,用了子查询不说,还保留了没有必要的order by。最好还是另外提供一个count语句。

Select count(*) from message where forum_id = ? and created_time > ?


在多页翻动的情况下,这个count语句要被反复执行。为了提高效率,我把这个count结果保存在全局缓存中,不仅本Session用户可以重复使用,其他用户在根据同样条件翻找message的时候,也可以重复使用这个结果。

我在持久层中使用通用的QueryKey做为缓存键值。
QueryKey分成三个部分,SQL, Parameters, Range。比如:

Query Key:
SQL : Select count(*) from message where forum_id = ? and created_time > ?
Parameters : [buaawhl, time long value]
Range: (0, 1)


这个QueryKey的效率很关键。主要是hashCode和equals两个方法的效率。
我们知道,当key放在Map等Hash数据结构中,首先hashCode,然后用equals比较hashCode后面的一串key。
举个例子。Key1和key2 的hashCode一样,都和key3的hashCode不一样。


[ 101 ] -> key1 -> key2

[ 666 ] -> key3



可以看到,hashCode,equals,这两个方法都是每次查找缓存都要调用的方法。尤其是equals方法更是重中之重,很可能需要被调用多次。
hashCode的优化实现相对来说比较简单,只要根据QueryKey中各部分的不同,尽量实现hashCode取值的扩散化,降低hashCode的重复率就可以了。
关键是equals的实现方案。这里有个原则,越小的结构越先比较,可以提高比较速度。
QueryKey中的parameters和range比较好办。每次equals比较的时候,先比较range,如果不相等,返回false; 如果相等,再比较Parameters,如果有一个parameter value不相等,返回false。这样,我们可以用很短的时间开销 过滤掉一大批不相等的QueryKey。
但是parameters和range都相等的时候,我们还是无可避免的要比较SQL。String的equals方法如下:
  // from jdk src
//这个方法没有比较hashCode,直接比较长度和字符
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}

我们看到,当SQL String很长的时候,长度相等,前面大部分字符相同的时候,(最极端的情况下,两个不同reference的String的字符完全相等),这个比较是相当消耗时间的。比如,

Select * from message where forum_id = ? and created_time > ? order by created_time desc



Select * from message where forum_id = ? and created_time > ? order by updated_time desc

两个String的长度相等,前面大部分也相等,只有走到cre 和 upd 的时候,才能比较出不相同。如果两个字符串内容一样,那更是要走到头,才能判断出两个字符串完全一样了。

我的第一个做法就是,尽量使用static final String做为QueryKey的SQL。这样两个SQL的reference如果相等,那么可以迅速判断出两个SQL相同。
这个做法只能处理事先定义好的SQL语句,但实际需求中,存在很多需要动态拼接SQL的情况,不可能做到所有相同的SQL具有相同的reference。
当然大部分不同的SQL都具有不同的长度,即使长度相同,前面走不了几个字符,就可以判断出不相同。所以做法一已经能够解决95%以上的SQL效率问题。

不过,为了解决这剩下的5%情况,我又采取了第二个做法:分而治之,把一个SQL String拆分成多个SQL常量的数组;泛化SQL的类型,SQL不限制为String类型,也可以是String[]类型。
比如。

String[] sql1 = {
“Select * from message where forum_id = ?”,
“ and created_time > ?”,
“ order by ”,
“created_time”,
“desc”
};



String[] sql2 = {
“Select * from message where forum_id = ?”,
“ and created_time > ?”,
“ order by ”,
“created_time”,
“desc”
};



String[] sql3 = {
“Select * from message where forum_id = ?”,
“ and created_time > ?”,
“ order by ”,
“updated_time”,
“desc”
};


这个时候,比较sql1和sql2和sql3的效率就会大大提高,虽然sql1 和 sql2两个数组的长度相等,还是要一个元素一个元素的比较,但由于里面大量用到了String常量,相同的String常量具有相同的 reference,所以5步下来,就可以判断出sql1和sql2数组的元素是完全相等的;4步下来,加上第一个字符的比较,就可以判断sql1和 sql3的第4个元素是不相等的。

我们看到,做法1和做法2,能够100%的提高SQL的比较效率,大部分情况下,也许比parameters的比较还快。

分享到:
评论

相关推荐

    JDBC分页查询(MySQL的)

    jdbc分页查询,利用mysql的limit实现分页查询。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。...

    JDBC分页

    **JDBC分页详解** 在数据库操作中,分页是一种非常常见的需求,它允许用户以较小的数据量逐步浏览大量的数据,提高用户体验并减轻服务器压力。本文将深入探讨JDBC(Java Database Connectivity)如何实现分页查询,...

    jdbc分页查询源码

    在Java开发中,JDBC(Java Database ...总的来说,JDBC分页查询是Java数据库操作中的常见需求,通过合理设计和优化,可以有效提升应用的性能和用户体验。给定的源码应当提供了具体的实现细节,供开发者参考学习。

    jdbc分页实例sqlserver2000

    标题中的“jdbc分页实例sqlserver2000”指的是使用Java Database Connectivity (JDBC) API来实现数据库分页查询的示例,特别是在SQL Server 2000这个特定的数据库管理系统上。JDBC是Java中用于与各种类型数据库交互...

    jsp jdbc分页原代码

    **JSP与JDBC分页实现详解** 在Java Web开发中,数据的分页展示是一项常见且重要的功能,尤其在处理大量数据时,分页能够有效地提高用户体验,避免一次性加载过多数据导致页面响应慢。本篇文章将围绕"JSP与JDBC分页...

    jdbc 分页 sql语句

    ### JDBC分页SQL语句详解 #### 一、引言 在数据库操作中,分页查询是非常常见且重要的一个功能。对于大型应用而言,一次性加载大量数据不仅会消耗过多资源,还可能导致用户体验下降。因此,合理地进行分页处理显得...

    JavaWeb+JSP+Servlet+JDBC分页查询和查询后分页界面优化最终版

    在JavaWeb开发中,"JavaWeb+JSP+Servlet+JDBC分页查询和查询后分页界面优化"是一个常见的需求,特别是在构建大型的、数据密集型的学生管理系统中。这个主题涵盖了许多关键知识点,让我们逐一深入探讨。 首先,...

    java基于原生的MySql的JDBC分页组件

    本篇将详细介绍如何基于原生的MySQL JDBC实现一个简单的分页组件,这对于初学者理解数据库操作和分页原理非常有帮助。 1. **JDBC基础**: JDBC是Java访问数据库的标准接口,它提供了连接数据库、执行SQL语句、处理...

    java jdbc 分页

    Java JDBC分页是一种在Java应用程序中实现数据库查询结果分页显示的技术。JDBC(Java Database Connectivity)是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,提供了诸如连接数据库、发送SQL语句以及...

    JavaWeb+JSP+Servlet+JDBC分页查询和查询后分页

    项目主体结构是dao+db+filter+pojo+servlet, 使用技术Servlet转发,代码中有注释帮助学者理解,数据库为MySQL资源...实现的数据库内容分页,查询分页,对初学者难点是根据get请求的中的url地址进行查询后的分页效果。

    jdbc分页demo

    在这个"jdbc分页demo"中,主要涵盖了JDBC连接数据库、预编译SQL、执行查询、处理结果集以及在JSP页面上展示数据等步骤。通过这个例子,开发者可以了解如何在实际项目中实现基于JDBC的分页查询,提升Web应用的性能和...

    分页大全(含JDBC分页 struts分页)

    这里我们将深入探讨“分页大全”,包括JDBC分页、Struts分页以及分页标签的使用。 首先,让我们了解什么是分页。在网页或应用中,分页是指将大量数据分割成若干小部分,每次只加载一部分,用户可以逐页浏览,而不是...

    oracle-jdbc分页实现(只需传入sql语句即可实现分页)

    Oracle JDBC分页实现是数据库操作中的一个重要环节,尤其是在处理大量数据时,为了提高用户体验和系统性能,分页查询显得尤为重要。Oracle数据库提供了多种方法来实现分页查询,其中包括使用ROWNUM伪列、游标...

    java jdbc 分页例子

    Java JDBC 分页查询是数据库操作中的常见需求,用于在大量数据中实现高效的页面导航。...这个例子对于初学者理解JDBC分页查询和数据源管理非常有帮助,同时也提醒我们在实际开发中应关注代码的可扩展性和复用性。

    Jdbc分页方法

    jdbc数据库通用分页方法,用时只须传值即可

    Java JDBC分页工具包

    一个用于支持JDBC分页的工具包,提供了一些封装好的JDBC操作方法,方便开发人员进行开发,提供了此工具包的API文档

    高性能jdbc分页处理

    高性能jdbc分页处理,使用PreparedStatement方式

    Java jdbc分页工具类

    java jdbc 分页工具类,以及返回集合数据的封装, private int limit = 10;//每页的个数 /** * 当前页 */ private int page; // /** * 总行数 */ private int totalRows; // /** * 总页数 */ private ...

    JAVA_JDBC面向对象分页(初步设计二之oracle)

    总结来说,面向对象的Java JDBC分页查询涉及到创建Page类来存储分页信息,构造Oracle兼容的SQL语句,以及使用PreparedStatement执行查询。在实际开发中,我们还需要关注性能优化、异常处理和代码的可维护性,确保...

    JDBC分页 absolute实现

    "JDBC分页 absolute实现" 主要指的是使用`Statement`或`PreparedStatement`对象的`absolute()`方法来实现数据库查询的分页功能。 在传统的SQL查询中,分页通常通过`LIMIT`和`OFFSET`子句来实现,但这在大型数据集上...

Global site tag (gtag.js) - Google Analytics