IBatis的分页研究

(转)IBatis的分页研究
本文是在参阅了http://ivanl.javaeye.com/blog/24739基础上完成的
在看JPetStore的代码时,发现它的分页处理主要是通过返回PaginatedList对象来完成的。如:在CatalogService类中
public  PaginatedList getProductListByCategory(String categoryId) 
    
return productDao.getProductListByCategory(categoryId); 
  }
 

分页是操作数据库型系统常遇到的问题。分页实现方法很多,但效率的差异就很大了。iBatis是通过什么方式来实现这个分页的了。查看它的实现部分:
 
返回的PaginatedList实际上是个接口,实现这个接口的是PaginatedDataList类的对象,查看PaginatedDataList类发现,每次翻页的时候最后都会调用下面这段函数
private  List getList( int  idx,  int  localPageSize)  throws  SQLException 
    
return sqlMapExecutor.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(RequestScope request, ResultSet rs,  int  skipResults,  int  maxResults, RowHandlerCallback callback)  throws  SQLException 
    
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语句与数据库的具体实现有关
mysql: 
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语句来完成的。参考代码如下: 
 
         public  String createOraclePagingSql(String sql,  int  pageIndex,  int  pageSize)
            
int m = pageIndex * pageSize; 
            
int n = m + pageSize; 
            
return "select * from ( select row_.*, rownum rownum_ from ( " + sql 
                    
+ " ) row_ where rownum <= " + n  
                    
+ ") where rownum_ > " + m; 
        }
 
综上,小批量(<2w)可以采用ibatis自带的分页类,大批量的还是直接操纵sql,当然也可以将这些sql自己进行封装,或在包中封装都可以。包封装的示例代码如下:
一个封装了分页功能的Oracle Package
create   or   replace  package body FMW_FY_HELPER  is
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的分页研究)