`

ibatis分页分析之二

阅读更多

转自:http://www.iteye.com/topic/566605

 

 

关于iBatis的分页性能问题,网上的讨论也很多,经过验证,我的结论是:只有在表的数据量很大,并且是从很后面的一个位置取一页数据的时候(比如从1000000条开始取100条),性能问题才比较明显。分析如下。

 

    首先看一下iBatis的分页代码。iBatis中,具体负责执行sql的类是com.ibatis.sqlmap.engine.execution.SqlExecutor。负责分页查询的方法是executeQuery —>handleMultipleResults —> 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()) {
              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分页查询的逻辑是首先判断ResulteSet的类型,如果ResultSet的类型是ResultSet.TYPE_FORWARD_ONLY,则使用ResultSet对象的next()方法,一步一步地移动游标到要取的第一条记录的位置,然后再采用next()方法取出一页的数据;如果ResultSet的类型不是ResultSet.TYPE_FORWARD_ONLY,则采用ResultSet对象的absolute()方法,移动游标到要取的第一条记录的位置,然后再采用next()方法取出一页的数据。
ResultSet的类型,是在iBatis的配置文件中配置的,如:


     <select id="queryAllUser" resultMap="user" resultSetType="FORWARD_ONLY">
            select id,name from user_tab
    </select>

 

      其中resultSetType的可选值为FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE,如果没有配置,默认值为FORWARD_ONLY,FORWARD_ONLY类型的ResultSet 不支持absolute方法,所以是通过next方法定位的。一般情况下,我们都使用FORWARD_ONLY类型的ResultSet,SCROLL类型ResultSet的优点是可向前,向后滚动,并支持精确定位(absolute),但缺点是把结果集全部加载进缓存(如果查询是从1000000条开始取100条,会把前100万条数据也加载进缓存),容易造成内存溢出,性能也很差,除非必要,一般不使用。


     可见,iBatis的分页完全依赖于JDBC ResultSet的next方法或absolute方法来实现,而Hibernate在分页查询方面,比iBatis要好很多,Hibernate可以根据不同的数据库,对sql做不同的优化加工,然后再执行优化后的sql。比如,对于Oracle数据库来说,原始sql为select * form user_tab, 从1000001条开始取100条,则hibernate加工后的sql为:


  select *
  from (select row_.*, rownum rownum_
          from (SELECT * FROM user_tab) row_
         where rownum <= 1000100)
 where rownum_ > 1000000

 

写一个程序,对比一下两种方式下的查询效率。程序如下:

 

 

public class Test{
 public static void main(String[] args) throws Exception {
  Class.forName("oracle.jdbc.driver.OracleDriver");
  Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:db", "db",
    "xwdb");
  long a = System.currentTimeMillis();
  testPageQuery1(conn);
  //testPageQuery2(conn);
  long b = System.currentTimeMillis();
  System.out.println(b-a);
 }

 
 
 public static void testPageQuery1(Connection conn) throws Exception{
  String sql = "SELECT * FROM user_tab ";
  
  Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
   
  ResultSet rs = stmt.executeQuery(sql);
  int j=0;
  //游标移动到1000001条数据的位置
  while(rs.next() && j++<1000000){
  
  }
  int i=0;
  //依次取出100条数据
  while(rs.next() && i++<100){
   
  }
   
 }
 public static void testPageQuery2(Connection conn) throws Exception{
  String sql = "SELECT * FROM user_tab ";
  
  StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
  pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
  pagingSelect.append(sql);
  pagingSelect.append(" ) row_ where rownum <= 1000100) where rownum_ > 1000000");

  Statement stmt = conn
     .createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
  ResultSet rs = stmt.executeQuery(pagingSelect.toString());
  
  while(rs.next()){
   
  }  
 }
}

 

 

 

  testPageQuery1方法是用ibatis采用的分页方法查询,testPageQuery2是用Hibernate采用的分页方法查询,发现testPageQuery1需要执行十几秒,而testPageQuery2仅需要执行零点几秒,差异很大。而如果改成从1000条开始取100条,甚至更靠前,则2者的差别是非常小的。


     综上所述,如果系统中查询的数据量很大,并且用户会选择查询非常靠后的数据,那么我们就应该替换iBatis的分页实现,如果不存在这种情况,那我们就不需要替换iBatis的分页实现,一般情况下,用户不可能去查询那么靠后的页,这也是iBatis一直不修改分页实现的原因吧。


      如果我们选择替换的话,有三种办法,一种是自己写一个类,继承iBatis的SqlExecutor,然后把这个类注入到com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient中,由于SqlExecutor是ExtendedSqlMapClient的私有变量,没有public类型的set方法,所以需要采用reflect机制注入;第二种方法是在自己的工程里写一个和iBatis的SqlExecutor的包名和类名完全一样的类,web工程中,WEB-INF/classes下的java类,先于WEB-INF/lib下jar包的加载,所以就巧妙了覆盖了iBatis的SqlExecutor类;第三种办法是弃用iBatis的分页查询方法queryForList(String sql,Object obj,int maxResult,int  skipResult),而用普通查询方法,queryForList(String sql,Object obj)。只不过把maxResult和skipResult都作为obj的变量传到sql里去。如下:

<select id="queryAllUser" resultMap="user">
select *
  from (select row_.*, rownum rownum_
          from (SELECT * FROM user_tab) row_
         where rownum <= #_maxResult#)
where rownum_ > #_skipResult#
    </select>

分享到:
评论

相关推荐

    ibatis 之分页

    2. **动态SQL**:Ibatis的动态SQL功能可以进一步优化分页查询。通过`&lt;if&gt;`、`&lt;choose&gt;`等标签,可以在SQL中根据条件动态插入分页语句,使得SQL更加灵活。 3. **PageHelper插件**:为了简化分页操作,社区开发了...

    ibatis分页

    标题中的“ibatis分页”指的是在使用iBATIS(一个SQL映射框架)时,如何实现数据库查询结果的分页显示。iBATIS通过XML配置文件或注解方式将Java代码与SQL语句分离,提供了更灵活的数据库操作方式。在处理大量数据时...

    ibatis mybatis 分页 crud 完整代码

    本资源"ibatis mybatis 分页 crud 完整代码"包含了这两个框架在CRUD(创建、读取、更新、删除)操作以及分页功能上的实现,下面将详细介绍相关知识点。 1. **Ibatis**: Ibatis 是一款轻量级的Java ORM(对象关系...

    spring-ibatis-ext-plugin.1.0.0 扩展ibaits原生SQL

    一直以来ibatis的分页都是通过滚动ResultSet实现的,应该算是逻辑分页吧。逻辑分页虽然能很干净地独立于特定数据库,但效率 在多数情况下不及特定数据库支持的物理分页,而hibernate的分页则是直接组装sql,充分...

    mysql,jdbc详解,与ibatis对比。批量查询,分页处理。

    ### MySQL、JDBC详解及与iBatis对比 ...通过对MySQL的基本操作、JDBC的工作原理以及iBatis与JDBC的对比分析,我们可以更全面地理解数据库编程的关键概念和技术要点。希望本文能够帮助开发者们更加熟练地掌握这些技能。

    ibatis-2-mybatis-2.3.5.zip

    2. 分页查询:iBatis支持通过传入参数实现分页查询,提高查询效率。 3. 动态SQL:利用、、、等标签,实现条件判断和选择性执行的SQL语句。 4. 批量操作:BatchExecutor适用于批量插入、更新和删除操作,提高性能。 ...

    ibatis学习资料汇总

    iBatis 2.x版本是iBatis历史上的一个里程碑,其中包含了许多基础库和核心组件。这个jar包包含了以下主要部分: - iBatis核心库:提供SQL映射和数据访问的功能。 - SQLMapClient:用于创建和管理SQLSession对象。 - ...

    IBATIS_IN_ACTION

    ### iBATIS最佳实践与案例分析 - **最佳实践**:总结iBATIS使用过程中的常见问题及解决策略,分享成功经验。 - **综合应用**:通过实例演示iBATIS在复杂项目中的集成与应用,帮助读者将理论知识转化为实践能力。 #...

    Ibatis 练习Demo和笔记

    5. **缓存机制**:分析Ibatis的一级和二级缓存,以及如何自定义缓存策略。 6. **事务管理**:讨论Ibatis的自动和手动事务控制,以及如何与Spring事务管理结合使用。 7. **插件机制**:介绍如何编写和使用Ibatis的...

    关于Ibatis的jar包

    2. **Ibatis包**: 这个文件很可能是Ibatis的完整jar包,包含了Ibatis运行所需的全部类库。这个jar包通常包括以下部分: - **核心库**:如org.apache.ibatis包,包含SqlSession、SqlSessionFactory、Mapper接口等...

    ibatis插件

    Ibatis插件则是为了增强Ibatis功能而设计的一系列扩展工具,它们能够帮助开发者在使用Ibatis时提高开发效率,提供诸如日志、缓存、自动填充、性能分析等功能。在本话题中,我们将主要讨论Ibatis插件的安装和使用。 ...

    ibatis 框架原理实现

    同时,Ibatis也支持结果集的分页和延迟加载,进一步提升性能。 9. **插件扩展**: Ibatis允许开发者自定义插件,通过拦截器模式增强SqlSession或Executor的行为,例如日志记录、性能分析等。 这个自己编写的...

    ajax和ibatis的综合应用例子

    2. **服务器处理**:服务器接收到请求后,解析参数,交由iBatis执行对应的SQL。iBatis根据配置的XML文件或注解来动态生成SQL语句。 3. **数据库操作**:iBatis执行SQL,与数据库进行交互,获取数据或完成数据更新。...

    SSI+EXT (Struts2+Spring+Ibatis+Ext)

    通过与后台数据的交互,它可以动态加载和展示由Ibatis分页查询返回的数据。 **数据库文件** 这部分通常包含项目使用的数据库脚本,可能包括创建表的DDL语句、初始化数据的DML语句等。在本例中,数据库是SQL Server ...

    asp.net MVC和IBatis.net整合demo数据库部分

    ASP.NET MVC(Model-View-Controller)是一种广泛用于构建Web应用程序的设计模式,它鼓励分离关注点,使得开发者能够更...通过分析和实践这个DEMO,你可以深入理解MVC架构和Ibatis.net的工作原理,提高自己的开发技能。

    一个用ibatis框架开发的示例项目

    通过分析和实践,我们可以掌握如何配置Ibatis,编写动态SQL,管理事务,以及如何在Spring环境中优雅地使用Ibatis。这不仅有助于我们提升开发效率,也能使我们在实际项目中更好地应对复杂的数据访问需求。

    ibatis学习笔记.txt

    #### 三、iBatis 的优势分析 **3.1 SQL 语句的灵活性** - iBatis 允许开发者直接编写 SQL 语句,这为处理复杂查询提供了极大的便利。 - 示例 SQL: ```sql UPDATE TABLE_A SET column_1 = #column_1# WHERE id = ...

    ibatis-in-action

    - **概念介绍**:映射语句是iBATIS的核心概念之一,用于关联SQL语句与Java对象。 - **示例展示**:通过具体的例子来说明如何定义和使用映射语句。 #### 2.3 非查询语句执行 - **执行机制**:介绍如何在iBATIS中执行...

    mybatis分页插件源码

    2. **Configuration**:插件可能会扩展 MyBatis 的配置类,以支持自定义的分页配置项,如默认每页记录数、是否开启分页等。 3. **XML 配置**:在 MyBatis 的 XML 映射文件中,用户可能需要配置插件,指定需要拦截的...

    iBATIS入门讲解和程序源码+iBATIS开发指南电子书

    源码分析部分,我们可以看到iBATIS如何解析XML配置文件,如何通过反射机制调用Java方法,以及如何将查询结果自动映射到Java对象上。这对于理解iBATIS的工作流程及其内部机制至关重要。 在"iBATIS开发指南电子书"中...

Global site tag (gtag.js) - Google Analytics