public PaginatedList getProductListByCategory(String categoryId) { return productDao.getProductListByCategory(categoryId); }
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;