`
newpeter
  • 浏览: 39293 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

对ibatis分页功能的改进

阅读更多

 

今天无意间看到了一篇关于这方面的文章,觉得是网上改进ibatis分页方面比较好的文章,这里转摘一下,希望能让更多的人用的到,也希望别人能把更好的解决方案贡献出来!

使ibatis支持hibernate式的物理分页

一直以来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内置的分页策略与特定数据库的物理分页效率差距 就在于物理分页查询与不分页查询在数据库中的执行效率的差距了。因为查询执行后读取数据前数据库并未把结果全部返回到内存,所以本身在存储占用上应该差距 不大,如果都使用索引,估计执行速度也差不太多。

 

继续我们的话题。其实只要在executeQuery执行前组装sql,然后将其传给executeQuery,并告诉handleResults 我们不需要逻辑分页即可。拦截executeQuery可以采用aop动态实现,也可直接继承SqlExecutor覆盖executeQuery来静态 地实现,相比之下后者要简单许多,而且由于SqlExecutor没有实现任何接口,比较易变,动态拦截反到增加了维护的工作量,所以我们下面来覆盖 executeQuery:

 

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

    public void setDialect(Dialect dialect) {
        this.dialect = dialect;
     }

    public boolean isEnableLimit() {
        return enableLimit;
     }

    public void setEnableLimit(boolean enableLimit) {
        this.enableLimit = enableLimit;
     }

     @Override
    public void executeQuery(RequestScope request, Connection conn, String sql,
             Object[] parameters, int skipResults, int maxResults,
             RowHandlerCallback callback) throws SQLException {
        if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)
                && supportsLimit()) {
             sql = dialect.getLimitString(sql, skipResults, maxResults);
            if(logger.isDebugEnabled()){
                 logger.debug(sql);
             }
             skipResults = NO_SKIPPED_RESULTS;
             maxResults = NO_MAXIMUM_RESULTS;            
         }
        super.executeQuery(request, conn, sql, parameters, skipResults,
                 maxResults, callback);
     }

    public boolean supportsLimit() {
        if (enableLimit && dialect != null) {
            return dialect.supportsLimit();
         }
        return false;
     }

}

 

 其中:

 

skipResults = NO_SKIPPED_RESULTS;
maxResults = NO_MAXIMUM_RESULTS;

 

 告诉handleResults不分页(我们组装的sql已经使查询结果是分页后的结果了),此处引入了类似hibenate中的数据库方言接口Dialect,其代码如下:

 

package com.aladdin.dao.dialect;

public interface Dialect {
    
    public boolean supportsLimit();

    public String getLimitString(String sql, boolean hasOffset);

    public String getLimitString(String sql, int offset, int limit);
}

 下面为Dialect接口的MySQL实现:package com.aladdin.dao.ibatis;

import java.io.Serializable;
import java.util.List;

import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.aladdin.dao.ibatis.ext.LimitSqlExecutor;
import com.aladdin.domain.BaseObject;
import com.aladdin.util.ReflectUtil;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;

public abstract class BaseDaoiBatis extends SqlMapClientDaoSupport {

    private SqlExecutor sqlExecutor;

    public SqlExecutor getSqlExecutor() {
        return sqlExecutor;
     }

    public void setSqlExecutor(SqlExecutor sqlExecutor) {
        this.sqlExecutor = sqlExecutor;
     }

    public void setEnableLimit(boolean enableLimit) {
        if (sqlExecutor instanceof LimitSqlExecutor) {
             ((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);
         }
     }

    public void initialize() throws Exception {
        if (sqlExecutor != null) {
             SqlMapClient sqlMapClient = getSqlMapClientTemplate()
                     .getSqlMapClient();
            if (sqlMapClient instanceof ExtendedSqlMapClient) {
                 ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)
                         .getDelegate(), "sqlExecutor", SqlExecutor.class,
                         sqlExecutor);
             }
         }
     }

     ...

}

 

 

 

分享到:
评论
1 楼 vasuer 2010-08-29  
te

相关推荐

    对IBatis分页的改进,使ibatis支持hibernate式的物理分页.doc

    物理分页是直接在SQL语句中添加LIMIT或OFFSET等关键字,利用数据库自身的分页功能,减少了数据处理的量,提高了查询速度。Hibernate框架就采用了这种策略,其分页效率相对较高。 针对iBatis的这一局限,我们可以...

    spring3.0.3+ibatis2.3.4.7分页

    标题中的“spring3.0.3+ibatis2.3.4.7分页”指的是在Spring 3.0.3版本与iBATIS 2.3.4.7版本结合下实现的一种分页功能。这是一个常见的Java Web开发场景,其中Spring作为依赖注入和框架管理工具,iBATIS作为数据访问...

    ibatis-2.3.4.zip

    三、Ibatis 2.3.4 版本的更新与改进 虽然没有给出具体的版本更新日志,但通常每个新版本都会修复已知的bug,提高稳定性和兼容性,可能还包含一些性能优化或新功能的添加。例如,2.3.4版本可能对SQL执行效率进行了...

    iBatis简明教程及快速入门

    - **配置简单**:最新的iBatis版本(例如2.0)改进了XML配置文件,使其更加直观易懂,即使是没有深入学习过iBatis的新手也能够快速理解配置文件的结构和意义。 #### 三、环境搭建与基本配置 1. **安装iBatis**:...

    ibatis-sqlmap-2.3.4.741-sources.zip_4 3 2 1_ibatis-sqlm_ibatis-s

    描述中提到了针对iBATIS-2.3.4.726的改进,包括物理分页功能、支持缓存以及读写分离,同时还增加了对多种数据库的透明支持。 iBATIS SQLMap 是一个开源的Java框架,它将SQL语句与Java代码分离,提供了更灵活的...

    ibatis-in-action

    - **分页查询**:讲解如何实现分页查询功能。 - **多表查询**:介绍处理多表联接查询的方法。 #### 2.5 事务管理 - **事务概念**:解释事务的基本原理及其在iBATIS中的应用。 - **配置方法**:指导如何配置iBATIS以...

    iBATIS 3 试用手记三

    例如,通过自定义Executor,可以实现批量插入、分页查询等功能,满足不同场景的需求。 在《iBATIS 3 试用手记三》中,作者可能还会分享一些实战经验,如如何解决常见的问题,如何优化性能,以及如何利用iBATIS 3的...

    iBATIS DataMapper1.6 中文翻译

    iBATIS DataMapper 1.6 是一个针对Java和.NET平台的数据访问框架,它主要功能是简化数据库操作,将SQL语句与业务逻辑代码分离。iBATIS DataMapper 使用XML文件来定义数据库交互,包括存储过程和SQL语句,使得开发者...

    ibatis的教程

    iBATIS DataMapper在新版本中有许多改进之处,例如: 1. 解决了使用group by的Select语句中出现的N+1问题,这涉及到性能优化。 2. 通过节点标识,支持了SQL片段的复用,使得SQL代码更加模块化。 3. 增加了对字典查询...

    asp.net MVC3+ iBATIS +jgrid代码生成器

    它在前端提供了丰富的功能,如分页、排序、过滤、编辑等,与后端的iBATIS配合,可以实现动态加载和实时数据更新。在jGrid中,你可以配置列定义、数据源等参数,以适应不同的数据展示需求。 "DaoEntity.cmt"可能是这...

    Mybatis与Ibatis的区别

    总的来说,Mybatis在Ibatis的基础上进行了多方面的改进,包括接口绑定、对象关系映射优化、OGNL表达式以及注解支持,这些都提升了开发者的体验并增强了框架的功能。然而,每个改进都有其适用场景,开发者应根据项目...

    SpringBoot 2.0.2.RELEASE以注解的形式整合 Mybatis+PageHelper (分页)

    本篇文章将详细讲解如何在该版本中以注解的形式整合Mybatis与PageHelper实现分页功能。 首先,我们需要引入必要的依赖。在`pom.xml`文件中,添加SpringBoot对Mybatis和PageHelper的支持: ```xml &lt;groupId&gt;org....

    ibator1.2.1

    Ibator 1.2.1是其一个稳定版本,包含了多项改进和优化,使得代码生成更加智能化,更易于集成到项目中。 二、Ibator 1.2.1主要功能 1. 数据库表扫描:Ibator可以自动扫描数据库中的表,根据表结构生成相应的Java...

    MyBatis 3.4.1 jar包和源代码

    9. **PageHelper分页插件**:虽然MyBatis本身不直接支持分页,但可以配合PageHelper这样的第三方插件实现高效的分页查询,自动处理各种数据库的分页语法。 10. **异常处理**:MyBatis抛出的异常类,如`org.apache....

    mybatis 最新jar包

    MyBatis 是一款深受开发者喜爱的持久层框架,它简化了Java应用程序与数据库之间的交互,尤其是在复杂...mybatis-3.0.2版本在当时是MyBatis的一个稳定版本,提供了许多重要的功能和改进,为开发者提供了更好的开发体验。

    mybatis-3.2.4

    例如,3.2.4可能改进了SQL动态语句的执行效率,增强了对多种数据库的支持,或者提供了更丰富的注解来简化开发工作。 在MyBatis的3.2.4版本中,一些关键知识点可能包括: 1. **XML配置与注解映射**:MyBatis允许...

    iuhyiuhkjh908u0980

    28. **分页技术**:PageBean是一种Java中用于实现分页功能的对象,封装了分页的相关信息,如当前页、总页数、每页记录数等,简化了分页逻辑。 这些知识点涵盖了Java开发的多个方面,包括构建工具、框架、数据库、...

Global site tag (gtag.js) - Google Analytics