MyBatis 最佳实践篇 1:分页

1 默认分页(逻辑分页)

MyBatis 默认提供了分页功能,即 RowBounds 类,该类提供两个参数:offset 和 limit。offset 为起始行数,limit 为要查询的行数。

public class RowBounds {
  //other...
  public static final int NO_ROW_OFFSET = 0;
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;

  private final int offset;
  private final int limit;

  public RowBounds() {
    this.offset = NO_ROW_OFFSET;
    this.limit = NO_ROW_LIMIT;
  }

  public RowBounds(int offset, int limit) {
    this.offset = offset;
    this.limit = limit;
  }
  //other...
}

但使用 RowBounds 的默认分页是逻辑分页:从数据库一次性查询出所有的记录,再通过传入的 RowBounds 的 offset 和 limit 的值,使用 for 循环进行过滤。源码如下:
org.apache.ibatis.executor.resultset.DefaultResultSetHandler 类:

 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext resultContext = new DefaultResultContext();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }
 
 
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
      if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
        rs.absolute(rowBounds.getOffset());
      }
    } else {
      for (int i = 0; i < rowBounds.getOffset(); i++) {
        rs.next();
      }
    }
  }

为了验证上面的逻辑,我们执行下面的测试方法:

public void testDefaultPaging() {
        SqlSession session = FactoryBuildByXML.getFactory().openSession(true);
        try {
            RowBounds rowBounds = new RowBounds(2, 11);
            List list = session.selectList("com.zhaoxueer.learn.dao.AuthorMapper.selectAllTest", null, rowBounds);
            System.out.println("查询的结果数:" + list.size());
        } finally {
            session.close();
        }
}
 
 

AuthorMapper 中 selectAllTest 方法为:

@Select("select id, name, sex, phone from author")
List selectAllTest();

执行结果为:

DEBUG [main] - ==>  Preparing: select id, name, sex, phone from author 
DEBUG [main] - ==> Parameters: 
查询的结果数:11

这种分页方式不能满足我们对于减轻数据库压力的要求,因此不建议使用。

2 使用 pagehelper 插件(物理分页)

为了简单地实现真正的物理分页,我们可以使用插件去实现,网上有很多这样的插件,例如比较常用的 pagehelper 插件。
只需要简单的三步:

  • 第一步:maven 引入依赖:

    com.github.pagehelper
    pagehelper
    latest version

注:修改 version 为最新的版本号(如 5.1.7),可点击上面链接查看最新版本。

  • 第二步:mybatis-config.xml 注册该 plugin:

        
            
            
        

  • 第三步:代码中使用:
public void testPageHelper() {
        SqlSession session = FactoryBuildByXML.getFactory().openSession(true);
        try {
            AuthorMapper authorMapper = session.getMapper(AuthorMapper.class);

            // 只需要在查询语句前插入该语句即可实现分页
            PageHelper.startPage(1, 10);
            List authorList = authorMapper.selectAllTest();
            System.out.println("查询的结果数:" + authorList.size());
        } finally {
            session.close();
        }
    }

执行结果为:

DEBUG [main] - ==>  Preparing: SELECT count(0) FROM author 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 1
DEBUG [main] - ==>  Preparing: select id, name, sex, phone from author LIMIT ? 
DEBUG [main] - ==> Parameters: 10(Integer)
DEBUG [main] - <==      Total: 10
查询的结果数:10

注意:注入分页插件 plugin 元素后,执行前面默认分页举例中的方法,也会实现物理分页,因为该插件对该方法也进行了拦截处理。

pagehelper 插件的 PageInterceptor 类对以下方法进行拦截:

@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)

默认分页举例中的语句:

RowBounds rowBounds = new RowBounds(2, 11);
List list = session.selectList("com.zhaoxueer.learn.dao.AuthorMapper.selectAllTest", null, rowBounds);
 
 

对上面两行代码的执行结果为:

DEBUG [main] - ==>  Preparing: select id, name, sex, phone from author LIMIT ?, ? 
DEBUG [main] - ==> Parameters: 2(Integer), 11(Integer)
DEBUG [main] - <==      Total: 11
查询的结果数:11

因此,若要测试默认分页的逻辑分页方式,请注释掉 plugin 代码块。

更多使用方法查看:MyBatis PageHelper 文档

附:

当前版本:mybatis-3.5.0
官网文档:MyBatis
项目实践:MyBatis Learn
手写源码:MyBatis 简易实现

你可能感兴趣的:(MyBatis 最佳实践篇 1:分页)