对IBatis分页的改善,使ibatis支持hibernate式的物理分页
对IBatis分页的改进,使ibatis支持hibernate式的物理分页?转载地址:http://pengfeng.iteye.com/blog/200772
对IBatis分页的改进,使ibatis支持hibernate式的物理分页
?
转载地址:http://pengfeng.iteye.com/blog/200772对IBatis分页的改进,使ibatis支持hibernate式的物理分页关键字: ibatis spring 分页 物理分页,幸好google了一下有很多人已经收藏了,这里转载一下
,以便再找不到了.
?
转载地址:http://www.blogjava.net/libin2722/articles/192504.html
?
一直以来ibatis的分页都是通过滚动ResultSet实现的,应该算是逻辑分页吧。逻辑分页虽然能很干净地独立于特定数据库,但效率在多数情况下不及特定数据库支持的物理分页,而hibernate的分页则是直接组装sql,充分利用了特定数据库的分页机制,效率相对较高。本文讲述的就是如何在不重新编译ibatis源码的前提下,为ibatis引入hibernate式的物理分页机制。
基本思路就是找到ibatis执行sql的地方,截获sql并重新组装sql。通过分析ibatis源码知道,最终负责执行sql的类是 com.ibatis.sqlmap.engine.execution.SqlExecutor,此类没有实现任何接口,这多少有点遗憾,因为接口是相对稳定契约,非大的版本更新,接口一般是不会变的,而类就相对易变一些,所以这里的代码只能保证对当前版本(2.1.7)的ibatis有效。下面是 SqlExecutor执行查询的方法:
Java代码?
- /**?
- ???*?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的实现:
Java代码?
- 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内置的分页策略与特定数据库的物理分页效率差距就在于物理分页查询与不分页查询在数据库中的执行效率的差距了。因为查询执行后读取数据前数据库并未把结果全部返回到内存,所以本身在存储占用上应该差距不大,如果都使用索引,估计执行速度也差不太多。
继续我们的话题。其实只要在executeQuery执行前组装sql,然后将其传给 executeQuery,并告诉handleResults我们不需要逻辑分页即可。拦截executeQuery可以采用aop动态实现,也可直接继承SqlExecutor覆盖executeQuery来静态地实现,相比之下后者要简单许多,而且由于SqlExecutor没有实现任何接口,比较易变,动态拦截反到增加了维护的工作量,所以我们下面来覆盖executeQuery:
Java代码?
- package?com.aladdin.dao.ibatis.ext;??
- ??
- import?java.sql.Connection;??
- import?java.sql.SQLException;??
- ??
- import?org.apache.commons.logging.Log;??
- import?org.apache.commons.logging.LogFactory;??
- ??
- import?com.aladdin.dao.dialect.Dialect;??
- import?com.ibatis.sqlmap.engine.execution.SqlExecutor;??
- import?com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;??
- import?com.ibatis.sqlmap.engine.scope.RequestScope;??
- ??
- public?class?LimitSqlExecutor?extends?SqlExecutor?{??
- ??
- ????private?static?final?Log?logger?=?LogFactory.getLog(LimitSqlExecutor.class);??
- ??????
- ????private?Dialect?dialect;??
- ??
- ????private?boolean?enableLimit?=?true;??
- ??
- ????public?Dialect?getDialect()?{??
- ????????return?dialect;??
- ????}??
- ??
- ????color: #7f0055; font-weig