使ibatis支持hibernate式的物理分页(1)

一 直以来ibatis的分页都是通过滚动ResultSet实现的,应该算是逻辑分页吧。逻辑分页虽然能很干净地独立于特定数据库,但效率在多数情况下不及 特定数据库支持的物理分页,而hibernate的分页则是直接组装sql,充分利用了特定数据库的分页机制,效率相对较高。本文讲述的就是如何在不重新 编译ibatis源码的前提下,为ibatis引入hibernate式的物理分页机制。

基本思路就是找到ibatis执行sql的地方, 截获sql并重新组装sql。通过分析ibatis源码知道,最终负责执行sql的类是 com.ibatis.sqlmap.engine.execution.SqlExecutor,此类没有实现任何接口,这多少有点遗憾,因为接口是相 对稳定契约,非大的版本更新,接口一般是不会变的,而类就相对易变一些,所以这里的代码只能保证对当前版本(2.1.7)的ibatis有效。下面是 SqlExecutor执行查询的方法:

   /**
   * Long form of the method to execute a query
   *
   * 
@param  request - the request scope
   * 
@param  conn - the database connection
   * 
@param  sql - the SQL statement to execute
   * 
@param  parameters - the parameters for the statement
   * 
@param  skipResults - the number of results to skip
   * 
@param  maxResults - the maximum number of results to return
   * 
@param  callback - the row handler for the query
   *
   * 
@throws  SQLException - if the query fails
   
*/
  
public   void  executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters,
                           
int  skipResults,  int  maxResults, RowHandlerCallback callback)
      
throws  SQLException {
    ErrorContext errorContext 
=  request.getErrorContext();
    errorContext.setActivity(
" executing query " );
    errorContext.setObjectId(sql);

    PreparedStatement ps 
=   null ;
    ResultSet rs 
=   null ;

    
try  {
      errorContext.setMoreInfo(
" Check the SQL Statement (preparation failed). " );

      Integer rsType 
=  request.getStatement().getResultSetType();
      
if  (rsType  !=   null ) {
        ps 
=  conn.prepareStatement(sql, rsType.intValue(), ResultSet.CONCUR_READ_ONLY);
      } 
else  {
        ps 
=  conn.prepareStatement(sql);
      }

      Integer fetchSize 
=  request.getStatement().getFetchSize();
      
if  (fetchSize  !=   null ) {
        ps.setFetchSize(fetchSize.intValue());
      }

      errorContext.setMoreInfo(
" Check the parameters (set parameters failed). " );
      request.getParameterMap().setParameters(request, ps, parameters);

      errorContext.setMoreInfo(
" Check the statement (query failed). " );

      ps.execute();
      rs 
=  getFirstResultSet(ps);

      
if  (rs  !=   null ) {
        errorContext.setMoreInfo(
" Check the results (failed to retrieve results). " );
        handleResults(request, rs, skipResults, maxResults, callback);
      }

      
//  clear out remaining results
       while  (ps.getMoreResults());

    } 
finally  {
      
try  {
        closeResultSet(rs);
      } 
finally  {
        closeStatement(ps);
      }
    }

  }

 

其 中handleResults(request, rs, skipResults, maxResults, callback)一句用于处理分页,其实此时查询已经执行完毕,可以不必关心handleResults方法,但为清楚起见,下面来看看 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()) {
              
break ;
            }
          }
        }

        
//  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 );
    }
  }

 

此 处优先使用的是ResultSet的absolute方法定位记录,是否支持absolute取决于具体数据库驱动,但一般当前版本的数据库都支持该方 法,如果不支持则逐条跳过前面的记录。由此可以看出如果数据库支持absolute,则ibatis内置的分页策略与特定数据库的物理分页效率差距就在于 物理分页查询与不分页查询在数据库中的执行效率的差距了。因为查询执行后读取数据前数据库并未把结果全部返回到内存,所以本身在存储占用上应该差距不大, 如果都使用索引,估计执行速度也差不太多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值