IBatis的分页研究

引用 http://www.blogjava.net/mintqzy/archive/2007/01/18/94694.html

本文是在参阅了http://ivanl.iteye.com/blog/24739基础上完成的
在看JPetStore的代码时,发现它的分页处理主要是通过返回PaginatedList对象来完成的。如:在CatalogService类中

public PaginatedListgetProductListByCategory(StringcategoryId) {
returnproductDao.getProductListByCategory(categoryId);
}

分页是操作数据库型系统常遇到的问题。分页实现方法很多,但效率的差异就很大了。iBatis是通过什么方式来实现这个分页的了。查看它的实现部分:

返回的PaginatedList实际上是个接口,实现这个接口的是PaginatedDataList类的对象,查看PaginatedDataList类发现,每次翻页的时候最后都会调用下面这段函数
private ListgetList( int idx, int localPageSize) throws SQLException {
returnsqlMapExecutor.queryForList(statementName,parameterObject,(idx)*pageSize,localPageSize);
}

由于
public interface SqlMapClient extends SqlMapExecutor,SqlMapTransactionManager {……}

所以实际的调用次序如下:
SqlMapClientImpl.queryForPaginatedList -> SqlMapSessionImpl.queryForPaginatedList
-> SqlMapExecutorDelegate.queryForPaginatedList -> GeneralStatement.executeQueryForList
-> GeneralStatment.executeQueryWithCallback -> GeneralStatment.executeQueryWithCallback
-> SqlExecutor.executeQuery -> SqlExecutor.handleMultipleResults() -> SqlExecutor.executeQuery -> handleResults
分页处理的函数如下
private void handleResults(RequestScoperequest,ResultSetrs, int skipResults, int maxResults,RowHandlerCallbackcallback) throws SQLException {
try{
request.setResultSet(rs);
ResultMapresultMap
=request.getResultMap();
if(resultMap!=null){
//SkipResults
if(rs.getType()!=ResultSet.TYPE_FORWARD_ONLY){
if(skipResults>0){
rs.absolute(skipResults);
}

}
else{
for(inti=0;i<skipResults;i++){
if(!rs.next()){
return;
}

}

}


//GetResults
intresultsFetched=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语句与数据库的具体实现有关
mysql:
select * from AlimitstartRow,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语句来完成的。参考代码如下:

public StringcreateOraclePagingSql(Stringsql, int pageIndex, int pageSize) {
intm=pageIndex*pageSize;
intn=m+pageSize;
return"select*from(selectrow_.*,rownumrownum_from("+sql
+")row_whererownum<="+n
+")whererownum_>"+m;
}
综上,小批量(<2w)可以采用ibatis自带的分页类,大批量的还是直接操纵sql,当然也可以将这些sql自己进行封装,或在包中封装都可以。包封装的示例代码如下:
一个封装了分页功能的Oracle Package
create or replace packagebodyFMW_FY_HELPER is
PROCEDURE GET_DATA(pi_sql in varchar ,pi_whichpage in integer ,pi_rownum in integer ,
po_cur_dataoutcur_DATA,po_allrownumout
integer ,pio_succeed in out integer )
as
v_cur_datacur_DATA;
v_cur_tempcur_TEMP;
v_temp
integer ;
v_sql
varchar ( 5000 );
v_temp1
integer ;
v_temp2
integer ;
begin
pio_succeed:
= 1 ;
v_sql:
= ' selectcount( '' a '' )from( ' || pi_sql || ' ) ' ;
execute immediatev_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(selectrownumasrn,t.*from( ' || pi_sql || ' )twhererownum< ' || to_char(v_temp) || ' )wherernbetween ' || 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 ;

你可能感兴趣的:(oracle,sql,Hibernate,mysql,ibatis)