java 物理分页和逻辑分页

本文深入探讨了数据库分页技术中的逻辑分页与物理分页两种方法。逻辑分页通过ResultSet的游标操作实现,适用于所有数据库但效率较低;物理分页则依赖于特定数据库的特性(如MySQL的LIMIT),效率较高但不通用。文章还详细介绍了在MyBatis和iBatis中如何实现这两种分页方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


A.逻辑分页利用游标分页,好处是所有数据库都统一,坏处就是效率低。
1.逻辑分页的第一种方式,利用ResultSet的滚动分页。这种分页方式依靠的是对结果集的算法来分页,因此通常被称为“逻辑分页”。步骤如下:
  a.根据条件sql查询数据库。
  b.得到ResultSet的结果集,由于ResultSet带有游标,因此可以使用其next()方法来指向下一条记录。
  c.利用next()方法,得到分页所需的结果集。
  2.逻辑分页的第二种方式,利用Scrollable ResultSets(可滚动结果集合)来快速定位到某个游标所指定的记录行,所使用的是ResultSet的absolute()方法。虽然和第一种方式区别不大,单效率比ResultSet滚动要好,但是absolute()方法并不是所有jdbc驱动都支持。


B.物理分页就是数据库本身提供了分页方式,如mysql的limit,好处是效率高,不好的地方就是不同数据库有不同的搞法。hibernate采用的是物理分页,mybatis默认采用逻辑分页(数据量大的时候会造成内存溢出),不过可以用插件或其他方式能达到物理分页效果。
1.ibatis中查询函数主要有queryForList 和 queryForPaginatedList,queryForPaginatedList 是逻辑分页,据说可以直接分页, 以至于在数据量大了的时候, 性能急剧下降, 根本不能使用.
2.(如项目2013年写的几个项目能达到物理分页效果)回到 queryForList, 如下调用可以取得queryEntities的全部数据:
        List<EntityClass> entities = entities = getSqlMapClientTemplate().queryForList("queryEntities", paramsMap);如果我们需要知道记录总数, 则需要:
        List<Long> rowCount = getSqlMapClientTemplate().queryForList("getRowCount", paramsMap);
        因此代码中需要在第一次执行queryEntities之前, 先执行getRowCount以取得记录数目. 而queryEntities由于采用了SELECT * FROM table WHERE condition LIMIT m OFFSET n 语法, 会自动跳过n个结果记录(0-based), 返回不超过m条记录. 这要求数据库支持, MySQL, Sqlite 可以. 注意, OFFSET n是在返回的结果集中跳过前n个.以上是简单的iBATIS物理分页的实现, 显然每次我们翻页的时候都要执行一次查询, 但是由于我们只取得LIMIT 条记录, 因此对服务器的内存消耗是固定的小尺寸. 这对浏览大记录集尤为有用.
    3.利用数据库本身的一些特性来分页。即:利用了数据库对sql语法的优化,提高分页性能。
针对Oracle数据库,步骤如下:
a.根据所使用的数据库特性来组织sql进行分页。
b.每次跳转页面的sql查询都不相同。通用的sql分页方式,“限制行数结果集的倒序”分页,步骤如下:
(1).取得符合条件的所有结果集中可以唯一标识的Key值(通常是主键),并正向排序。
(2).利用数据库提供的特殊方法进行“最大结果集”的限制(在Oracle中使用rownum, sql server中使用top, mysql中使用limit...), 该“最大结果集”指包含当前所处页的所有记录数,“最大结果集”应该只包含惟一的Key值。
(3).对步骤(2)中的“最大结果集”进行逆序,并取得“显示当前页显示数量的结果集”,该结果集中只包含惟一的Key值。
(4).通过步骤(3)中所取得的Key值取得显示数据,该显示数据就是当前页应该显示的数据。
针对MySQL数据库,在MySQL数据库中offset关键字的意思是"越过",而limit关键字的意思是“限制”,利用这两者结合可轻松分页。
(1)取得符合条件的结果集,包含全字段。
(2)利用offset关键字越过一段结果集(被越过的结果集就是"(当前页 - 1) * 一页显示数")。
(3)利用limit关键字限制取得一段结果集(被限制取得的结果集就是一页显示数)

分页结论:
1.物理分页速度上并不一定快于逻辑分页,逻辑分页速度上也并不一定快于物理分页。
2.物理分页总是优于逻辑分页:没有必要将属于数据库端的压力加诸到应用端来,就算速度上存在优势,然而其它性能上的优点足以弥补这个缺点。
3.在分页工作前,有必要了解使用数据库本身的一些sql语句特点更好的分页。
### PageHelper 的分页类型及其实现方式 PageHelper 是一款 MyBatis 的第三方分页插件,其主要功能是为 MyBatis 提供分页支持。根据提供的引用内容以及专业领域的知识,可以明确 PageHelper 属于 **物理分页** 插件[^1]。 #### 物理分页逻辑分页的区别 - **物理分页**: - 在数据库层面实现分页操作。 - 使用 SQL 的 `LIMIT` 或 `ROWNUM` 等关键字限制返回的结果集大小。 - 只从数据库中查询当前面需要的数据行数,减少内存占用网络传输开销。 - PageHelper 的核心原理是通过拦截 MyBatis 的 SQL 执行,在原有 SQL 的基础上动态添加分页条件[^3]。 - **逻辑分页**: - 在应用层实现分页操作。 - 查询出所有数据后,再通过程序代码截取需要的部分数据。 - 这种方式会将所有数据加载到内存中,可能导致性能问题,尤其是在数据量较大时。 - MyBatis 的 RowBounds 分页属于逻辑分页的一种实现方式[^2]。 #### PageHelper 的物理分页实现方式 PageHelper 的物理分页实现基于 MyBatis 的插件机制,具体过程如下: 1. **拦截 SQL 执行**:PageHelper 通过 MyBatis 的拦截器机制拦截 SQL 的执行。 2. **修改 SQL**:在拦截到的 SQL 上动态添加分页相关的条件,例如 `LIMIT` 或 `ROWNUM`。 3. **缓存 Count 查询**:PageHelper 会生成一个对应的 `COUNT` 查询语句,用于统计总记录数,并将结果保存到 `page` 对象中。 4. **封装分页结果**:最终的查询结果会被封装到一个 `Page` 对象中,该对象不仅包含当前的数据,还包含分页信息(如总记录数、当前码、每显示条数等)[^4]。 以下是一个使用 PageHelper 实现物理分页的示例代码: ```java // Service 层 public ResponseResult<?> getPhysicalPaging(int pageNum, int pageSize, Stu stu) { if (pageNum <= 0) { pageNum = 1; } if (pageSize <= 0) { pageSize = 10; } if (ObjectUtil.isEmpty(stu)) { stu = new Stu(); } // 开始分页 PageHelper.startPage(pageNum, pageSize); // 查询数据 List<Stu> stuList = stuMapper.selectAll(stu); // 封装分页信息 PageInfo<Stu> page = new PageInfo<>(stuList); return ResponseResult.success(page); } // Controller 层 @GetMapping("/physicalPaging") public ResponseResult<?> getPhysicalPaging(int pageNum, int pageSize, Stu stu) { return stuService.getPhysicalPaging(pageNum, pageSize, stu); } ``` #### 总结 PageHelper 是一种物理分页插件,其通过 MyBatis 的插件机制拦截 SQL 并动态修改,从而实现高效的分页查询。相比逻辑分页物理分页能够显著降低内存占用网络传输成本,尤其适合处理大规模数据场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值