IBatis的分页研究

在看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;

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