news 2026/4/21 0:05:23

【12.MyBatis源码剖析与架构实战】19.MyBatis分⻚插件设计与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【12.MyBatis源码剖析与架构实战】19.MyBatis分⻚插件设计与实战

MyBatis 分页插件设计与实战(完整实操案例)

分页查询是业务系统中最常见的需求之一。虽然可以手动在 SQL 后拼接LIMITROWNUM,但这样会侵入业务代码,且需要为每个查询编写重复的分页逻辑。通过 MyBatis 插件机制,我们可以实现一个透明物理分页插件:开发者只需在调用 Mapper 方法前设置分页参数,插件自动拦截 SQL 并改写为对应数据库的分页语句,同时返回总记录数。


一、需求分析

功能点说明
透明分页无需修改原有 Mapper 接口和 XML,只需在 Service 层设置分页参数
物理分页使用数据库方言(LIMIT/ROWNUM/OFFSET FETCH)实现真分页
自动查询总数执行分页查询时自动生成COUNT(*)SQL 并查询总记录数
方言适配支持 MySQL、PostgreSQL、Oracle、SQL Server 等
返回结果返回包含分页信息(总条数、当前页、每页大小)和数据的统一对象

二、设计思路

基于 MyBatis 插件机制,拦截Executor.query方法(因为所有查询最终都会经过它)。核心步骤如下:

  1. 定义分页参数对象Page:存储当前页码、每页大小、总记录数、排序字段等。
  2. 定义方言接口Dialect:提供getCountSql(生成计数 SQL)和getPageSql(生成分页 SQL)方法。
  3. 实现各数据库方言:MySQL、Oracle、PostgreSQL 等。
  4. 编写分页拦截器PageInterceptor
    • 通过@Intercepts注解拦截Executor.query方法。
    • 判断参数中是否包含Page对象,若无则直接放行。
    • 若有,则:
      • 通过反射获取原始的MappedStatementBoundSql
      • 生成并执行 COUNT SQL,获取总记录数并设置到Page对象中。
      • 生成分页 SQL,替换原有BoundSql中的 SQL,并继续执行查询。
      • 将查询结果封装到Page对象中返回。
  5. 使用ThreadLocal传递分页参数:实现线程安全的分页参数传递,避免侵入方法签名。

三、完整源码实现

3.1 分页参数对象Page.java
packagecom.example.plugin.page;importjava.io.Serializable;importjava.util.List;publicclassPage<T>implementsSerializable{privatestaticfinallongserialVersionUID=1L;// 当前页码privateintpageNum=1;// 每页条数privateintpageSize=10;// 总记录数privatelongtotal=0;// 总页数privateintpages=0;// 查询结果集privateList<T>list;// 是否查询总数(默认 true)privatebooleancount=true;// 排序字段(可选,用于简化)privateStringorderBy;publicPage(){}publicPage(intpageNum,intpageSize){this.pageNum=pageNum;this.pageSize=pageSize;}// getter / setter 省略}
3.2 方言接口Dialect.java
packagecom.example.plugin.dialect;publicinterfaceDialect{/** * 根据原始 SQL 生成计数 SQL * @param originalSql 原始查询 SQL * @return 计数 SQL */StringgetCountSql(StringoriginalSql);/** * 根据原始 SQL 和分页参数生成分页 SQL * @param originalSql 原始查询 SQL * @param offset 起始行(从0开始) * @param limit 每页条数 * @return 分页 SQL */StringgetPageSql(StringoriginalSql,intoffset,intlimit);}
3.3 MySQL 方言实现MySQLDialect.java
packagecom.example.plugin.dialect;publicclassMySQLDialectimplementsDialect{@OverridepublicStringgetCountSql(StringoriginalSql){// 简单的 SELECT COUNT(*) 包装,实际需处理 ORDER BY、DISTINCT 等return"SELECT COUNT(*) FROM ("+originalSql+") AS tmp_count";}@OverridepublicStringgetPageSql(StringoriginalSql,intoffset,intlimit){returnoriginalSql+" LIMIT "+offset+", "+limit;}}
3.4 Oracle 方言实现OracleDialect.java
packagecom.example.plugin.dialect;publicclassOracleDialectimplementsDialect{@OverridepublicStringgetCountSql(StringoriginalSql){return"SELECT COUNT(*) FROM ("+originalSql+")";}@OverridepublicStringgetPageSql(StringoriginalSql,intoffset,intlimit){intstartRow=offset+1;intendRow=offset+limit;StringBuildersql=newStringBuilder();sql.append("SELECT * FROM (SELECT TMP_PAGE.*, ROWNUM PAGE_ROW_NUM FROM (");sql.append(originalSql);sql.append(") TMP_PAGE WHERE ROWNUM <= ").append
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 23:56:30

YashanDb数据库安装小记

1、使用下面的连接下载yashandb安装包curl –O https://linked.yashandb.com/upload1010/yashandb-23.4.1.109-linux-x86_64.tar.gz2、创建安装用户建议创建一个新用户安装YashanDB数据库。切换至root用户&#xff0c;并执行如下命令创建新用户yashan&#xff1a;配置sudo免密。…

作者头像 李华
网站建设 2026/4/20 23:53:12

MATLAB datetime函数实战:从数据导入到跨时区分析

1. 数据导入与解析&#xff1a;datetime的十八般武艺 第一次处理多源时间数据时&#xff0c;我盯着电脑屏幕发呆了半小时——日志文件里是Unix时间戳&#xff0c;Excel表格里是"2023/05/12"这样的文本&#xff0c;API返回的JSON数据又带着时区标志。直到发现MATLAB的…

作者头像 李华
网站建设 2026/4/20 23:53:06

告别移植烦恼!一个头文件搞定STC全系列51单片机延时函数(附源码)

跨代STC单片机通用延时库设计与实战指南 引言&#xff1a;嵌入式开发中的延时困境 在STC单片机开发过程中&#xff0c;延时函数就像空气一样无处不在却又容易被忽视——直到你需要在不同型号芯片间移植代码时才会发现它的重要性。我曾亲眼见证一个团队因为延时函数不兼容导致整…

作者头像 李华