MyBatis分页
本文最后更新于:2 年前
引言
在现代Web开发中,有效的数据管理和呈现是至关重要的,尤其是当涉及到大量数据时。分页是一种常用的技术,用于提高数据处理的效率和用户界面的响应性。MyBatis作为一个强大的持久层框架,提供了多种分页实现方法,本文将探讨MyBatis中常见的分页技术,旨在帮助开发者根据应用需求选择最合适的分页解决方案。
手动实现分页
最基本的方法是在SQL查询中手动实现分页逻辑。这通常涉及到修改SQL语句以包含数据库特定的分页语法。
MySQL/PostgreSQL:使用
LIMIT
和OFFSET
关键字。1
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20
Oracle:使用
ROWNUM
或在Oracle 12c及以上使用FETCH NEXT
1
SELECT * FROM ( SELECT a.*, ROWNUM rnum FROM (SELECT * FROM users ORDER BY id) a WHERE ROWNUM <= 30 ) WHERE rnum > 20
SQL Server:使用
OFFSET-FETCH
子句。1
SELECT * FROM users ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
这种方法虽然直接且容易理解,但它要求开发者对不同数据库的分页机制有足够的了解,并且在数据库切换时需要修改SQL代码。
PageHelper
PageHelper
是MyBatis社区提供的一个分页插件,这是一个非常流行的MyBatis分页插件,通过拦截器自动修改SQL实现分页,可以让开发者忽略手动编写分页SQL。
PageHelper
支持多种数据库,无需担心不同数据库分页语法的差异。
DEMO:
引入依赖
1
2
3
4
5<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.3</version>
</dependency>在MyBatis核心配置文件中添加拦截器
1
2
3<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>在Mapper文件中定义查询方法
1
2
3<select id="getAll" resultType="space.yangtao.domain.po.StudentPO">
select * from tt_student order by id desc
</select>对应的接口方法为
1
List<StudentPO> getAll();
调用
PageHelper
的startPage
方法,设置分页信息,同时将查询结果封装到一个PageInfo
对象中1
2
3
4
5
6
7SqlSession session = SqlSessionUtil.getSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
int pageNum = 1; // 页码
int pageSize = 2; // 每页记录数
PageHelper.startPage(pageNum, pageSize);
PageInfo<StudentPO> studentPOPageInfo = new PageInfo<>(studentMapper.getAll(), 10);
log.info("PageHelper分页查询:{}", studentPOPageInfo);运行结果如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14...
22:38:11.834 [main] DEBUG space.yangtao.mapper.StudentMapper.getAll_COUNT - ==> Preparing: SELECT count(0) FROM tt_student
22:38:11.849 [main] DEBUG space.yangtao.mapper.StudentMapper.getAll_COUNT - ==> Parameters:
22:38:11.861 [main] TRACE space.yangtao.mapper.StudentMapper.getAll_COUNT - <== Columns: count(0)
22:38:11.861 [main] TRACE space.yangtao.mapper.StudentMapper.getAll_COUNT - <== Row: 3
22:38:11.861 [main] DEBUG space.yangtao.mapper.StudentMapper.getAll_COUNT - <== Total: 1
22:38:11.862 [main] DEBUG space.yangtao.mapper.StudentMapper - Cache Hit Ratio [space.yangtao.mapper.StudentMapper]: 0.0
22:38:11.863 [main] DEBUG space.yangtao.mapper.StudentMapper.getAll - ==> Preparing: select * from tt_student order by id desc LIMIT ?, ?
22:38:11.863 [main] DEBUG space.yangtao.mapper.StudentMapper.getAll - ==> Parameters: 2(Long), 2(Integer)
22:38:11.864 [main] TRACE space.yangtao.mapper.StudentMapper.getAll - <== Columns: id, class_id, name, height, gender, birthday, create_time
22:38:11.865 [main] TRACE space.yangtao.mapper.StudentMapper.getAll - <== Row: 1, 1, zhangsan, 180.0, 男, 1990-01-01, 2024-07-01 22:10:22
22:38:11.866 [main] DEBUG space.yangtao.mapper.StudentMapper.getAll - <== Total: 1
22:38:11.866 [main] INFO space.yangtao.client.StudentMapperTest - PageHelper分页查询:PageInfo{pageNum=2, pageSize=2, size=1, startRow=3, endRow=3, total=3, pages=2, list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=3, pages=2, reasonable=false, pageSizeZero=false}[StudentPO(id=1, name=zhangsan, height=180.0, gender=男, birthday=Mon Jan 01 00:00:00 CST 1990, createTime=Mon Jul 01 22:10:22 CST 2024)], prePage=1, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=8, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}
...
由日志可知,PageHelper
已经帮我们自动设置了limit
参数,同时看到返回的分页对象中除了需要的列表外,还含有其他一些可能用到的属性,如当前页、页大小、页码、总数、前一页、后一页、是否最后一页等。
RowBounds
MyBatis也提供了一个RowBounds
类用于支持分页,通过在Mapper方法中使用RowBounds
参数,可以进行物理分页。
使用示例:
1 |
|
RowBounds
的一个缺点是它是通过在内存中过滤记录实现的,即首先拉取所有记录然后在内存中进行分页,这在数据量较大时会影响性能。
Web环境下的选择
简单总结下以上各个分页方案:
- 手动实现:
- 优点:
- 控制性强:完全控制SQL查询,可以针对性地优化查询。
- 灵活性高:可以根据具体需求调整SQL,满足复杂的分页需求。
- 缺点:
- 代码复杂:需要手动编写用于计算总数的查询以及分页的查询,增加了开发的复杂性。
- 维护成本高:每次分页逻辑调整或数据库迁移都需要手动修改SQL。
- 性能问题:如果处理不当,可能会因为两次查询(一次获取总数,一次获取分页数据)而导致性能下降。
- 优点:
- PageHelper:
- 优点:
- 简单易用:通过简单的 API 调用实现分页,无需修改原有 SQL。
- 自动化:自动处理总页数、总记录数的计算,对开发者透明。
- 高效:通常只需要进行一次额外的查询来获取总记录数,且插件内部可以有优化措施。
- 缺点:
- 依赖第三方:增加了对外部库的依赖,可能需要关注其兼容性和更新。
- 适应性问题:虽然支持多种数据库,但在一些特殊的分页需求下可能需要调整默认行为。
- 优点:
- RowBounds:
- 优点:
- 内置支持:无需额外引入库或工具,MyBatis内置支持。
- 简单使用:通过传递RowBounds对象为查询方法提供偏移量和限制即可实现分页。
- 缺点:
- 效率低:RowBounds 是通过在数据库查询全部数据后,在内存中进行分页,这在数据量大时效率极低。
- 不返回额外信息:不提供如总记录数等额外的分页信息,需要额外的查询和逻辑来实现。
- 优点:
总结
综合考虑Web
环境下分页的需求,使用分页插件(如PageHelper
)通常是最合适的选择,因为它既提供了开箱即用的分页功能,又能有效地返回必需的分页信息,同时保持了代码的简洁性和易维护性。如果对性能有极致要求,建议采用数据库自带的物理分页功能,并优化SQL查询。对于简单应用,RowBounds
提供了快速实现分页的方法,尽管它在大数据量处理上效率不高。除此之外,使用分页时还需要限制每页的最大记录数、优化查询相关索引、同时充分利用缓存,以保证应用的稳定和高效。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!