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
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 简易实现