IBatis的分页研究
摘自:
http://cpu.iteye.com/blog/311395
在看JPetStore的代码时,发现它的分页处理主要是通过返回PaginatedList对象来完成的。如:在CatalogService类中
return productDao.getProductListByCategory(categoryId);
}
分页是操作数据库型系统常遇到的问题。分页实现方法很多,但效率的差异就很大了。iBatis是通过什么方式来实现这个分页的了。查看它的实现部分:
返回的PaginatedList实际上是个接口,实现这个接口的是PaginatedDataList类的对象,查看PaginatedDataList类发现,每次翻页的时候最后都会调用下面这段函数
return sqlMapExecutor.queryForList(statementName, parameterObject, (idx) * pageSize, localPageSize);
}
由于
所以实际的调用次序如下:
-> SqlMapExecutorDelegate.queryForPaginatedList -> GeneralStatement.executeQueryForList
-> GeneralStatment.executeQueryWithCallback -> GeneralStatment.executeQueryWithCallback
-> SqlExecutor.executeQuery -> SqlExecutor.handleMultipleResults() -> SqlExecutor.executeQuery -> handleResults
分页处理的函数如下
try {
request.setResultSet(rs);
ResultMap resultMap = request.getResultMap();
if (resultMap != null ) {
// Skip Results
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (skipResults > 0 ) {
rs.absolute(skipResults);
}
} else {
for ( int i = 0 ; i < skipResults; i ++ ) {
if ( ! rs.next()) {
return ;
}
}
}
// Get Results
int resultsFetched = 0 ;
while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs);
callback.handleResultObject(request, columnValues, rs);
resultsFetched ++ ;
}
}
} finally {
request.setResultSet( null );
}
}
由 此可见,iBatis的分页主要依赖于jdbcdriver的如何实现以及是否支持rs.absolute(skipResults)。它并不是一个好的 分页方式。它先要取出所有的符合条件的记录存入ResultSet对象,然后用absolute方法进行定位,来实现分页。当记录数较大(比如十万条) 时,整体的查询速度将会变得很慢。
所以分页还是要考虑采用直接操作sql语句来完成。当然小批量的可以采用iBatis的分页模式。一般分页的sql语句与数据库的具体实现有关
select * from A limit startRow,endRow
oracle:
select b. * from ( select a. * ,rownum as linenum from ( select * from A) a where rownum <= endRow) b where linenum >= startRow
Hibernate的Oracle分页采用的就是是拼凑RowNum的Sql语句来完成的。参考代码如下:
int m = pageIndex * pageSize;
int n = m + pageSize;
return " select * from ( select row_.*, rownum rownum_ from ( " + sql
+ " ) row_ where rownum <= " + n
+ " ) where rownum_ > " + m;
}
例如:我要查询 UserInfo 表中的第 11 - 20 条的数据
select * from (select row_.*, rownum rownum_ from (select * from UserInfo order by sort desc) row_ where rownum <= 20) where rownum_ > 10
select t2.* from (select t1.*,rownum rownum_ from userinfo t1 where rownum <= 20) t2 where t2.rownum_ > 10;
其中最内层的查询 SELECT * FROM UserInfo 表示不进行翻页的原始查询语句。 ROWNUM <= 20 和 ROWNUM_ > 10控制分页查询的每页的范围。
上面给出的这个分页查询语句,在大多数情况拥有较高的效率。分页的目的就是控制输出结果集大小,将结果尽快的返回。 在上面的分页查询语句中,这种考虑主要体现在 WHERE ROWNUM <= 20 这句上。
选择第 10 到2 0 条记录存在两种方法,一种是上面例子中展示的在查询的第二层通过 ROWNUM <= 20 来控制最大值,在查询的最外层控制最小值。而另一种方式是去掉查询第二层的 WHERE ROWNUM <= 20 语句,在查询的最外层控制分页的最小值和最大值。这是,查询语句如下:
SELECT * FROM
(
SELECT A.*, ROWNUM ROWNUM_
FROM (SELECT * FROM TABLE_NAME) A
)
WHERE ROWNUM_ BETWEEN 11 AND 20
对比这两种写法,绝大多数的情况下,第一个查询的效率比第二个高得多。
这是由于 CBO 优化模式 下, Oracle 可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。对于第一个查询语句,第二层的查询条件 WHERE ROWNUM <= 20 就可以被 Oracle 推入到内层查询中,这样 Oracle 查询的结果一旦超过了 ROWNUM 限制条件,就终止查询将结果返回了。
而第二个查询语句,由于查询条件 BETWEEN 11 AND 20 是存在于查询的第三层,而 Oracle 无法将第三层的查询条件推到最内层(即使推到最内层也没有意义,因为最内层查询不知道 ROWNUM_ 代表什么)。因此,对于第二个查询语句, Oracle 最内层返回给中间层的是所有满足条件的数据,而中间层返回给最外层的也是所有数据。数据的过滤在最外层完成,显然这个效率要比第一个查询低得多。
四.关于ibatis自己提供的分页API
PaginatedList paginatedList=sqlMap.queryForPaginatedList(statementName, parameterObject, pageSize);
这个是基于内存的分页,就是已经把所有数据load到内存了,才实现的伪分页。不会减少load的负荷。
综上所述,小批量(<2w)可以采用ibatis自带的分页类,大批量的还是直接操纵sql,当然也可以将这些sql自己进行封装,或在包中封装都可以。包封装的示例代码如下:
一个封装了分页功能的Oracle Package
PROCEDURE GET_DATA(pi_sql in varchar ,pi_whichpage in integer ,pi_rownum in integer ,
po_cur_data out cur_DATA,po_allrownum out integer ,pio_succeed in out integer )
as
v_cur_data cur_DATA;
v_cur_temp cur_TEMP;
v_temp integer ;
v_sql varchar ( 5000 );
v_temp1 integer ;
v_temp2 integer ;
begin
pio_succeed : = 1 ;
v_sql : = ' select count( '' a '' ) from ( ' || pi_sql || ' ) ' ;
execute immediate v_sql into v_temp;
po_allrownum: = ceil(v_temp / pi_rownum);
v_sql : = '' ;
v_temp : = pi_whichpage * pi_rownum + 1 ;
v_temp1: = (pi_whichpage - 1 ) * pi_rownum + 1 ;
v_temp2: = pi_whichpage * pi_rownum;
v_sql: = ' select * from (select rownum as rn,t.* from ( ' || pi_sql || ' ) t where rownum< ' || to_char(v_temp) || ') where rn between ' || to_char(v_temp1) || ' and ' || to_char(v_temp2);
open v_cur_data for v_sql;
if v_cur_data % notfound
then
pio_succeed: =- 1 ;
return ;
end if ;
po_cur_DATA : = v_cur_data;
end ;
IBatis的分页研究的更多相关文章
- Ibatis的分页机制的缺陷
我们知道,Ibatis为我们提供了可以直接实现分页的方法 queryForList(String statementName, Object parameterObject, int skipResu ...
- IBatis.Net使用总结(三)-- IBatis实现分页返回数据和总数
IBatis 分页,这里没有使用其他插件,只使用最原始的方法. 输入参数: int currentPage 当前页 int pageSize 每页大小 Hashtable findCondition ...
- ibatis实现分页查询
最近在做老项目改造,分享一个之前写的ibatis(这里特指ibatis2.x的版本)分页插件. 大致原理就是通过重写SqlExecutor的executeQuery方法,实现分页查询,支持mysql和 ...
- 一、iBatis进行分页查询
1.ibatis理解: iBatis属于半自动化的ORM框架,我们需要编写SQL语句,由iBatis进行数据库访问,返回结果.而iBatis可以为我们做的更多,比如对查询参数集合.结果.分页查询.事务 ...
- ibatis核心内容概述
核心提示:SqlMap的配置是iBatis中应用的核心.这部分任务占据了iBatis开发的70的工作量. 1.命名空间: sqlMap namespace=Account,在此空间外要引用此空间的元素 ...
- iBatis2之SqlMap配置总结(18条)
iBatis2之SqlMap配置总结(18条) SqlMap的配置是iBatis中应用的核心.这部分任务占据了iBatis开发的70的工作量. 1.命名空间: <sqlMap names ...
- iBatis2 SqlMap中经常使用sql语句
本来我也不喜欢iBatis,那是由于我当时还不怎么会用它,如今我想说,iBatis是个好东西,不信你试试看.以下是我在项目实践中对iBatis的一个小总结.希望帮助众多在疲于iBatis编码而无暇思考 ...
- 对SSH三大框架的理解
SSH框架一般指的是Struts.Spring.Hibernate,后来Struts2代替了Struts.最近5年,Struts2已经被Spring MVC代替,而Hibernate基本也被iBati ...
- IBatis按条件分页查询
XML中代码 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE sqlMap PUBLIC & ...
随机推荐
- dataTables 添加行内操作按钮
在上一篇博客中我们提到了用dataTables 做一个分页表格.今天进一步进阶,做一个行内带操作按钮的表格.效果如图. 记得最基础的实现方式是,我们要在js 中拼接字符串,嵌入一个带按钮的语句.但是现 ...
- CF # 369 D2 D、E
D,只要抓住每个点只有一个出度,那么图就能分成几个部分,而且可以发现,一个部分最多一个环. #include <iostream> #include <cstdio> #inc ...
- Android Wear - App Structure for Android Wear(应用结构)
---------------------------------------------------------------------------------------------------- ...
- linux 下使用genymotion
在官网下载genymotion http://www.genymotion.cn/ 然后进行下面操作 1.假设本机没有virtualbox 下载一个 能够通过指令 sudo apt-get inst ...
- node-V8
Node是基于V8引擎的,所以我们运行Js文件不会存在问题,下面演示一下: 因为安装Vscode后,会自动添加本路径到path,所以可以用 code yes.js 来执行创建文件 Vscode是基于N ...
- MongoDB Helper的简单封装
db.properties #mongodb数据库配置文件 #数据库server所在的ip地址 ip=127.0.0.1 #mongodb服务port号 port=27017 #要连接的库 dbNa ...
- Linux bash: scp: command not found的问题记录
,总结 scp成功,须要两个server都安装了scp服务才行.
- list集合去重复元素
//set集合去重,不打乱顺序 public static void main(String[] args){ List<String> list = new ArrayList<S ...
- 【POJ 1679】 The Unique MST
[题目链接] 点击打开链接 [算法] 先求出图的最小生成树 枚举不在最小生成树上的边,若加入这条边,则形成了一个环,如果在环上且在最小生成树上的权值最大的边等于 这条边的权值,那么,显然最小生成树不唯 ...
- EasyUI Validatebox 验证框
转自:http://www.jeasyui.net/plugins/167.html 通过 $.fn.validatebox.defaults 重写默认的 defaults. 验证框(validate ...