当前位置: 首页 > news >正文

MyBatis是如何分页的及原理

MyBatis 是一种持久层框架,支持通过配置文件和注解将 SQL 映射为 Java 对象。在实际开发中,查询数据时经常需要进行分页处理。 MyBatis 也提供了支持分页的方案,其主要思路是使用 Limit 偏移量和限制个数,来获取指定数量的数据。下面将会介绍 MyBatis 如何进行分页。

MyBatis 提供两种分页方式:基于参数改造和基于插件拦截 。下面我们将分别介绍这两种方式:

1、基于参数改造:

第一种分页方式是基于参数改造的,通过添加参数 limit 和 offset 就可以实现查询从某个位置开始的若干条记录,代码实现如下:

<select id="selectSomeData"parameterType="map" resultType="com.example.SomeData">SELECT * FROM sometableORDER BY somecolumnLIMIT #{limit} OFFSET #{offset}
</select>

这段 SQL 语句会返回从偏移量为 offset 的位置开始的 limit 条结果。例如:LIMIT 30,10 表示从第 31 行开始返回 10 行结果。

在实际应用中,我们可以将 limit 和 offset 抽取成两个参数,并传入到 MyBatis 中。

2、基于插件拦截 :

MyBatis 还提供了另外一种分页方式,基于插件拦截机制。这种方式更加灵活,支持实现更为复杂的分页功能。

我们需要自定义一个拦截器,实现 Interceptor 接口,并重写其中唯一的 intercept 方法,在其中对 SQL 语句进行修改,添加分页信息。具体操作如下:

首先,创建一个类来实现该拦截器:

public class PageInterceptor implements Interceptor {/*** 拦截方法** @param invocation* @return* @throws Throwable*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取原始的SQL语句String sql = (String) invocation.getArgs()[0];// 查询总数并计算出总页数和当前页int total = count(sql);// 如果总数小于等于0,则直接返回空结果集if (total <= 0) {return Collections.emptyList();}// 计算出当前页的起始位置和结束位置int offset = getOffset(pageNo, pageSize);int limit = pageSize;// 构造含分页信息的新SQLString newSql = getNewSql(sql, offset, limit);// 将新SQL替换成原来的SQL,并继续执行原有方法ReflectionUtils.setFieldValue(invocation, "h.sql", newSql);Object result = invocation.proceed();// 包装成Page对象,并返回Page<T> pageResult = new Page<>(pageNo,pageSize,total,(List<T>)result);return pageResult;}/*** 获取新的SQL语句(含分页信息)** @param sql* @param offset* @param limit* @return*/private String getNewSql(String sql, int offset, int limit) {return sql + " LIMIT " + offset + "," + limit;}/*** 获取查询结果总数** @param sql* @return*/private int count(String sql){// code omitted}/*** 计算当前分页的 Offset** @param pageNo* @param pageSize* @return*/private int getOffset(int pageNo, int pageSize) {return (pageNo - 1) * pageSize;}
}

然后,我们需要在 mybatis-config.xml 配置文件中注册该拦截器:

<plugins><plugin interceptor="com.example.mybatis.PageInterceptor"/>
</plugins>

最终,在查询数据时,我们便可以按照以下方式进行分页处理了:

public List<User> selectUserListByPage(int startRow, int pageSize){RowBounds rowBounds = new RowBounds(startRow,pageSize);String statement = "com.example.UserMapper.selectUserList";return sqlSession.selectList(statement,null,rowBounds);
}

实现原理简述

  1. 注册拦截器:PageHelper插件通过MyBatis的插件机制注册为一个拦截器,主要拦截Executor接口的query方法。

  2. 动态修改SQL:拦截器在执行query方法前,检查是否有分页信息,如果有,则根据分页参数(如当前页、每页数量)修改SQL,添加分页限制。

  3. 数据库方言:考虑到不同的数据库有不同的分页语法,PageHelper支持多种数据库方言,会根据配置自动选择合适的分页实现。

  4. 执行并返回结果:修改后的SQL被执行,返回的结果是分页后的数据集。

查询电影数据案例
准备工作

首先,确保已经引入了PageHelper依赖,并在MyBatis的配置文件中配置了PageHelper插件,指定数据库方言,例如针对MySQL:

mybatis-config.xml:

<configuration><!-- ... --><plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"><property name="helperDialect" value="mysql"/></plugin></plugins><!-- ... -->
</configuration>

定义Mapper接口和映射文件 

MovieMapper.java:

public interface MovieMapper {List<Movie> selectMovies();
}

MovieMapper.xml:

<mapper namespace="com.example.mapper.MovieMapper"><select id="selectMovies" resultType="com.example.entity.Movie">SELECT * FROM movie</select>
</mapper>
分页查询示例代码

在服务类中,使用PageHelper进行分页查询:

MovieService.java:

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;@Service
public class MovieService {@Autowiredprivate MovieMapper movieMapper;public Page<Movie> getMoviesByPage(int pageNum, int pageSize) {// 开启分页PageHelper.startPage(pageNum, pageSize);// 执行查询,PageHelper会自动进行分页处理List<Movie> movies = movieMapper.selectMovies();// 返回分页对象,包含了分页信息和数据列表return (Page<Movie>) movies;}
}

在这个例子中,当调用getMoviesByPage方法时,首先通过PageHelper.startPage(pageNum, pageSize)设置分页信息。随后,当调用movieMapper.selectMovies()执行查询时,PageHelper拦截了这次查询请求,根据前面设置的分页参数动态地在SQL查询语句末尾添加了适合MySQL数据库的LIMIT和OFFSET子句,从而只获取当前页面所需的数据。最后,返回的是一个包含了当前页数据以及分页元数据(如总页数、总记录数等)的Page对象。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • AWS CDN新增用户ip 地区 城市 响应头
  • 前端a-tree遇到的问题
  • 普通人还有必要学习 Python 之类的编程语言吗?
  • 值得关注的数据资产入表
  • C#开发:Git的安装和使用
  • Linux多线程编程-哲学家就餐问题详解与实现(C语言)
  • 【c++刷题笔记-动态规划】day41: 121. 买卖股票的最佳时机、122.买卖股票的最佳时机II 、
  • Perl之正则表达式
  • 【数学建模】技术革新——Lingo的使用超详解
  • 基于Go1.19的站点模板爬虫
  • Vue2中的指令修饰符
  • Linux系统下weblogic10.3.6版本打补丁步骤
  • 最新版康泰克完整版- Kontakt v7.10.5 for Win和Mac,支持m芯片和intel,有入库工具
  • flutter 手写 TabBar
  • 鸿蒙开发:Universal Keystore Kit(密钥管理服务)【查询密钥是否存在(ArkTS)】
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • Angular 响应式表单之下拉框
  • Druid 在有赞的实践
  • EOS是什么
  • JavaScript 奇技淫巧
  • Sublime text 3 3103 注册码
  • 翻译:Hystrix - How To Use
  • 服务器从安装到部署全过程(二)
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 聊聊hikari连接池的leakDetectionThreshold
  • 区块链技术特点之去中心化特性
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 一些css基础学习笔记
  • 智能合约开发环境搭建及Hello World合约
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • # Redis 入门到精通(一)数据类型(4)
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (python)数据结构---字典
  • (算法二)滑动窗口
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)ObjectiveC 深浅拷贝学习
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .Net Core 微服务之Consul(二)-集群搭建
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET delegate 委托 、 Event 事件,接口回调
  • .net framework 4.8 开发windows系统服务
  • .NET Micro Framework 4.2 beta 源码探析
  • .net 程序发生了一个不可捕获的异常
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .NET 中什么样的类是可使用 await 异步等待的?
  • .NET构架之我见
  • .NET中分布式服务
  • :“Failed to access IIS metabase”解决方法
  • @TableId注解详细介绍 mybaits 实体类主键注解